summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2025-09-26 17:40:39 +0800
committerGitHub <noreply@github.com>2025-09-26 17:40:39 +0800
commitcf85a0a75b6b6b201b4ee0906bb756b19f957af8 (patch)
tree78138b8564d132edba20226a7522532746bfb79e
parentf5e2a00d6701729eb33da5962069c4432db426c8 (diff)
parent4951e2e98bab7a2996893939ee77f0279145b556 (diff)
Merge pull request #10 from JustEnoughVCS/jvcs_dev
Jvcs dev
-rw-r--r--Cargo.lock31
-rw-r--r--crates/utils/string_proc/src/format_processer.rs (renamed from crates/utils/string_proc/src/string_processer.rs)16
-rw-r--r--crates/utils/string_proc/src/lib.rs9
-rw-r--r--crates/utils/string_proc/src/macros.rs32
-rw-r--r--crates/utils/string_proc/src/simple_processer.rs15
-rw-r--r--crates/utils/tcp_connection/Cargo.toml1
-rw-r--r--crates/utils/tcp_connection/src/error.rs15
-rw-r--r--crates/utils/tcp_connection/src/instance.rs146
-rw-r--r--crates/utils/tcp_connection/src/lib.rs6
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/Cargo.toml1
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/lib.rs6
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/test_challenge.rs13
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/test_connection.rs7
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/test_file_transfer.rs13
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/test_msgpack.rs112
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/test_tcp_target_build.rs7
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/test_utils.rs4
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/test_utils/handle.rs (renamed from crates/utils/tcp_connection/src/handle.rs)3
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/test_utils/target.rs (renamed from crates/utils/tcp_connection/src/target.rs)7
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/test_utils/target_configure.rs (renamed from crates/utils/tcp_connection/src/target_configure.rs)0
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/test_utils/target_connection.rs (renamed from crates/utils/tcp_connection/src/target_connection.rs)14
-rw-r--r--crates/vcs/src/data/sheet.rs125
-rw-r--r--crates/vcs/src/data/vault.rs4
-rw-r--r--crates/vcs/src/data/vault/config.rs1
-rw-r--r--crates/vcs/src/data/vault/sheets.rs19
-rw-r--r--crates/vcs/src/data/vault/virtual_file.rs1
-rw-r--r--crates/vcs/src/lib.rs2
-rw-r--r--crates/vcs/src/service.rs2
-rw-r--r--crates/vcs/src/service/server_entry.rs0
-rw-r--r--crates/vcs/src/service/standard_handle.rs1
-rw-r--r--crates/vcs/vcs_test/Cargo.toml1
-rw-r--r--crates/vcs/vcs_test/src/test_sheet_creation_management_and_persistence.rs53
-rw-r--r--crates/vcs/vcs_test/src/test_virtual_file_creation_and_update.rs2
33 files changed, 551 insertions, 118 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9e44fb5..dcce341 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -658,6 +658,12 @@ dependencies = [
]
[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
name = "pbkdf2"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -846,6 +852,28 @@ dependencies = [
]
[[package]]
+name = "rmp"
+version = "0.8.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
+dependencies = [
+ "byteorder",
+ "num-traits",
+ "paste",
+]
+
+[[package]]
+name = "rmp-serde"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db"
+dependencies = [
+ "byteorder",
+ "rmp",
+ "serde",
+]
+
+[[package]]
name = "ron"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1125,6 +1153,7 @@ dependencies = [
"pem",
"rand 0.10.0-rc.0",
"ring",
+ "rmp-serde",
"rsa",
"serde",
"serde_json",
@@ -1137,6 +1166,7 @@ dependencies = [
name = "tcp_connection_test"
version = "0.1.0"
dependencies = [
+ "serde",
"tcp_connection",
"tokio",
]
@@ -1306,6 +1336,7 @@ version = "0.1.0"
dependencies = [
"cfg_file",
"tcp_connection",
+ "tcp_connection_test",
"tokio",
"vcs",
]
diff --git a/crates/utils/string_proc/src/string_processer.rs b/crates/utils/string_proc/src/format_processer.rs
index 8b51c12..8d0a770 100644
--- a/crates/utils/string_proc/src/string_processer.rs
+++ b/crates/utils/string_proc/src/format_processer.rs
@@ -1,8 +1,8 @@
-pub struct StringProcesser {
+pub struct FormatProcesser {
content: Vec<String>,
}
-impl From<String> for StringProcesser {
+impl From<String> for FormatProcesser {
fn from(value: String) -> Self {
Self {
content: Self::process_string(value),
@@ -10,7 +10,7 @@ impl From<String> for StringProcesser {
}
}
-impl From<&str> for StringProcesser {
+impl From<&str> for FormatProcesser {
fn from(value: &str) -> Self {
Self {
content: Self::process_string(value.to_string()),
@@ -18,7 +18,7 @@ impl From<&str> for StringProcesser {
}
}
-impl StringProcesser {
+impl FormatProcesser {
/// Process the string into an intermediate format
fn process_string(input: String) -> Vec<String> {
let mut result = String::new();
@@ -46,9 +46,11 @@ impl StringProcesser {
while let Some(c) = chars.next() {
processed.push(c);
if let Some(&next) = chars.peek()
- && c.is_lowercase() && next.is_uppercase() {
- processed.push(' ');
- }
+ && c.is_lowercase()
+ && next.is_uppercase()
+ {
+ processed.push(' ');
+ }
}
processed
diff --git a/crates/utils/string_proc/src/lib.rs b/crates/utils/string_proc/src/lib.rs
index 1f24028..e5559b9 100644
--- a/crates/utils/string_proc/src/lib.rs
+++ b/crates/utils/string_proc/src/lib.rs
@@ -1,9 +1,10 @@
+pub mod format_processer;
pub mod macros;
-pub mod string_processer;
+pub mod simple_processer;
#[cfg(test)]
mod tests {
- use crate::string_processer::StringProcesser;
+ use crate::format_processer::FormatProcesser;
#[test]
fn test_processer() {
@@ -22,7 +23,7 @@ mod tests {
];
for (input, expected) in test_cases {
- let processor = StringProcesser::from(input);
+ let processor = FormatProcesser::from(input);
assert_eq!(
processor.to_camel_case(),
expected,
@@ -34,7 +35,7 @@ mod tests {
#[test]
fn test_conversions() {
- let processor = StringProcesser::from("brewCoffee");
+ let processor = FormatProcesser::from("brewCoffee");
assert_eq!(processor.to_upper_case(), "BREW COFFEE");
assert_eq!(processor.to_lower_case(), "brew coffee");
diff --git a/crates/utils/string_proc/src/macros.rs b/crates/utils/string_proc/src/macros.rs
index 9d85338..135268e 100644
--- a/crates/utils/string_proc/src/macros.rs
+++ b/crates/utils/string_proc/src/macros.rs
@@ -1,63 +1,63 @@
#[macro_export]
macro_rules! camel_case {
($input:expr) => {{
- use string_proc::string_processer::StringProcesser;
- StringProcesser::from($input).to_camel_case()
+ use string_proc::format_processer::FormatProcesser;
+ FormatProcesser::from($input).to_camel_case()
}};
}
#[macro_export]
macro_rules! upper_case {
($input:expr) => {{
- use string_proc::string_processer::StringProcesser;
- StringProcesser::from($input).to_upper_case()
+ use string_proc::format_processer::FormatProcesser;
+ FormatProcesser::from($input).to_upper_case()
}};
}
#[macro_export]
macro_rules! lower_case {
($input:expr) => {{
- use string_proc::string_processer::StringProcesser;
- StringProcesser::from($input).to_lower_case()
+ use string_proc::format_processer::FormatProcesser;
+ FormatProcesser::from($input).to_lower_case()
}};
}
#[macro_export]
macro_rules! title_case {
($input:expr) => {{
- use string_proc::string_processer::StringProcesser;
- StringProcesser::from($input).to_title_case()
+ use string_proc::format_processer::FormatProcesser;
+ FormatProcesser::from($input).to_title_case()
}};
}
#[macro_export]
macro_rules! dot_case {
($input:expr) => {{
- use string_proc::string_processer::StringProcesser;
- StringProcesser::from($input).to_dot_case()
+ use string_proc::format_processer::FormatProcesser;
+ FormatProcesser::from($input).to_dot_case()
}};
}
#[macro_export]
macro_rules! snake_case {
($input:expr) => {{
- use string_proc::string_processer::StringProcesser;
- StringProcesser::from($input).to_snake_case()
+ use string_proc::format_processer::FormatProcesser;
+ FormatProcesser::from($input).to_snake_case()
}};
}
#[macro_export]
macro_rules! kebab_case {
($input:expr) => {{
- use string_proc::string_processer::StringProcesser;
- StringProcesser::from($input).to_kebab_case()
+ use string_proc::format_processer::FormatProcesser;
+ FormatProcesser::from($input).to_kebab_case()
}};
}
#[macro_export]
macro_rules! pascal_case {
($input:expr) => {{
- use string_proc::string_processer::StringProcesser;
- StringProcesser::from($input).to_pascal_case()
+ use string_proc::format_processer::FormatProcesser;
+ FormatProcesser::from($input).to_pascal_case()
}};
}
diff --git a/crates/utils/string_proc/src/simple_processer.rs b/crates/utils/string_proc/src/simple_processer.rs
new file mode 100644
index 0000000..2de5dfc
--- /dev/null
+++ b/crates/utils/string_proc/src/simple_processer.rs
@@ -0,0 +1,15 @@
+/// Sanitizes a file path by replacing special characters with underscores.
+///
+/// This function takes a file path as input and returns a sanitized version
+/// where characters that are not allowed in file paths (such as path separators
+/// and other reserved characters) are replaced with underscores.
+pub fn sanitize_file_path<P: AsRef<str>>(path: P) -> String {
+ let path_str = path.as_ref();
+ path_str
+ .chars()
+ .map(|c| match c {
+ '/' | '\\' | ':' | '*' | '?' | '"' | '<' | '>' | '|' => '_',
+ _ => c,
+ })
+ .collect()
+}
diff --git a/crates/utils/tcp_connection/Cargo.toml b/crates/utils/tcp_connection/Cargo.toml
index e70baf0..22466c8 100644
--- a/crates/utils/tcp_connection/Cargo.toml
+++ b/crates/utils/tcp_connection/Cargo.toml
@@ -9,6 +9,7 @@ tokio = { version = "1.46.1", features = ["full"] }
# Serialization
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
+rmp-serde = "1.3.0"
# Error handling
thiserror = "1.0.69"
diff --git a/crates/utils/tcp_connection/src/error.rs b/crates/utils/tcp_connection/src/error.rs
index 171e23d..ffcce6f 100644
--- a/crates/utils/tcp_connection/src/error.rs
+++ b/crates/utils/tcp_connection/src/error.rs
@@ -32,6 +32,9 @@ pub enum TcpTargetError {
#[error("Unsupported operation: {0}")]
Unsupported(String),
+
+ #[error("Pool already exists: {0}")]
+ PoolAlreadyExists(String),
}
impl From<io::Error> for TcpTargetError {
@@ -87,3 +90,15 @@ impl From<pem::PemError> for TcpTargetError {
TcpTargetError::Crypto(error.to_string())
}
}
+
+impl From<rmp_serde::encode::Error> for TcpTargetError {
+ fn from(error: rmp_serde::encode::Error) -> Self {
+ TcpTargetError::Serialization(error.to_string())
+ }
+}
+
+impl From<rmp_serde::decode::Error> for TcpTargetError {
+ fn from(error: rmp_serde::decode::Error) -> Self {
+ TcpTargetError::Serialization(error.to_string())
+ }
+}
diff --git a/crates/utils/tcp_connection/src/instance.rs b/crates/utils/tcp_connection/src/instance.rs
index 217b10a..fd620e2 100644
--- a/crates/utils/tcp_connection/src/instance.rs
+++ b/crates/utils/tcp_connection/src/instance.rs
@@ -48,7 +48,7 @@ impl Default for ConnectionConfig {
}
pub struct ConnectionInstance {
- stream: TcpStream,
+ pub(crate) stream: TcpStream,
config: ConnectionConfig,
}
@@ -90,6 +90,35 @@ impl ConnectionInstance {
Ok(())
}
+ /// Serialize data to MessagePack and write to the target machine
+ pub async fn write_msgpack<Data>(&mut self, data: Data) -> Result<(), TcpTargetError>
+ where
+ Data: Serialize,
+ {
+ let msgpack_data = rmp_serde::to_vec(&data)?;
+ let len = msgpack_data.len() as u32;
+
+ self.stream.write_all(&len.to_be_bytes()).await?;
+ self.stream.write_all(&msgpack_data).await?;
+ Ok(())
+ }
+
+ /// Read data from target machine and deserialize from MessagePack
+ pub async fn read_msgpack<Data>(&mut self) -> Result<Data, TcpTargetError>
+ where
+ Data: serde::de::DeserializeOwned,
+ {
+ let mut len_buf = [0u8; 4];
+ self.stream.read_exact(&mut len_buf).await?;
+ let len = u32::from_be_bytes(len_buf) as usize;
+
+ let mut buffer = vec![0; len];
+ self.stream.read_exact(&mut buffer).await?;
+
+ let data = rmp_serde::from_slice(&buffer)?;
+ Ok(data)
+ }
+
/// Read data from target machine and deserialize
pub async fn read<Data>(&mut self) -> Result<Data, TcpTargetError>
where
@@ -213,6 +242,73 @@ impl ConnectionInstance {
Ok(String::from_utf8_lossy(&buffer).to_string())
}
+ /// Write large MessagePack data to the target machine (chunked)
+ pub async fn write_large_msgpack<Data>(
+ &mut self,
+ data: Data,
+ chunk_size: impl Into<u32>,
+ ) -> Result<(), TcpTargetError>
+ where
+ Data: Serialize,
+ {
+ let msgpack_data = rmp_serde::to_vec(&data)?;
+ let chunk_size = chunk_size.into() as usize;
+ let len = msgpack_data.len() as u32;
+
+ // Write total length first
+ self.stream.write_all(&len.to_be_bytes()).await?;
+
+ // Write data in chunks
+ let mut offset = 0;
+ while offset < msgpack_data.len() {
+ let end = std::cmp::min(offset + chunk_size, msgpack_data.len());
+ let chunk = &msgpack_data[offset..end];
+ match self.stream.write(chunk).await {
+ Ok(n) => offset += n,
+ Err(err) => return Err(TcpTargetError::Io(err.to_string())),
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Read large MessagePack data from the target machine (chunked)
+ pub async fn read_large_msgpack<Data>(
+ &mut self,
+ chunk_size: impl Into<u32>,
+ ) -> Result<Data, TcpTargetError>
+ where
+ Data: serde::de::DeserializeOwned,
+ {
+ let chunk_size = chunk_size.into() as usize;
+
+ // Read total length first
+ let mut len_buf = [0u8; 4];
+ self.stream.read_exact(&mut len_buf).await?;
+ let total_len = u32::from_be_bytes(len_buf) as usize;
+
+ // Read data in chunks
+ let mut buffer = Vec::with_capacity(total_len);
+ let mut remaining = total_len;
+ let mut chunk_buf = vec![0; chunk_size];
+
+ while remaining > 0 {
+ let read_size = std::cmp::min(chunk_size, remaining);
+ let chunk = &mut chunk_buf[..read_size];
+
+ match self.stream.read_exact(chunk).await {
+ Ok(_) => {
+ buffer.extend_from_slice(chunk);
+ remaining -= read_size;
+ }
+ Err(err) => return Err(TcpTargetError::Io(err.to_string())),
+ }
+ }
+
+ let data = rmp_serde::from_slice(&buffer)?;
+ Ok(data)
+ }
+
/// Write file to target machine.
pub async fn write_file(&mut self, file_path: impl AsRef<Path>) -> Result<(), TcpTargetError> {
let path = file_path.as_ref();
@@ -319,9 +415,10 @@ impl ConnectionInstance {
// Make sure parent directory exists
if let Some(parent) = path.parent()
- && !parent.exists() {
- tokio::fs::create_dir_all(parent).await?;
- }
+ && !parent.exists()
+ {
+ tokio::fs::create_dir_all(parent).await?;
+ }
// Read file header (version + size + crc)
let mut version_buf = [0u8; 8];
@@ -398,15 +495,16 @@ impl ConnectionInstance {
// Validate CRC if enabled
if self.config.enable_crc_validation
- && let Some(crc_calculator) = crc_calculator {
- let actual_crc = crc_calculator.finalize();
- if actual_crc != expected_crc && expected_crc != 0 {
- return Err(TcpTargetError::File(format!(
- "CRC validation failed: expected {:08x}, got {:08x}",
- expected_crc, actual_crc
- )));
- }
+ && let Some(crc_calculator) = crc_calculator
+ {
+ let actual_crc = crc_calculator.finalize();
+ if actual_crc != expected_crc && expected_crc != 0 {
+ return Err(TcpTargetError::File(format!(
+ "CRC validation failed: expected {:08x}, got {:08x}",
+ expected_crc, actual_crc
+ )));
}
+ }
// Final flush and sync
writer.flush().await?;
@@ -576,22 +674,26 @@ fn parse_ed25519_public_key(pem: &str) -> [u8; 32] {
let mut key_bytes = [0u8; 32];
if let Ok(pem_data) = pem::parse(pem)
- && pem_data.tag() == "PUBLIC KEY" && pem_data.contents().len() >= 32 {
- let contents = pem_data.contents();
- key_bytes.copy_from_slice(&contents[contents.len() - 32..]);
- }
+ && pem_data.tag() == "PUBLIC KEY"
+ && pem_data.contents().len() >= 32
+ {
+ let contents = pem_data.contents();
+ key_bytes.copy_from_slice(&contents[contents.len() - 32..]);
+ }
key_bytes
}
/// Parse Ed25519 private key from PEM format
fn parse_ed25519_private_key(pem: &str) -> Result<SigningKey, TcpTargetError> {
if let Ok(pem_data) = pem::parse(pem)
- && pem_data.tag() == "PRIVATE KEY" && pem_data.contents().len() >= 32 {
- let contents = pem_data.contents();
- let mut seed = [0u8; 32];
- seed.copy_from_slice(&contents[contents.len() - 32..]);
- return Ok(SigningKey::from_bytes(&seed));
- }
+ && pem_data.tag() == "PRIVATE KEY"
+ && pem_data.contents().len() >= 32
+ {
+ let contents = pem_data.contents();
+ let mut seed = [0u8; 32];
+ seed.copy_from_slice(&contents[contents.len() - 32..]);
+ return Ok(SigningKey::from_bytes(&seed));
+ }
Err(TcpTargetError::Crypto(
"Invalid Ed25519 private key format".to_string(),
))
diff --git a/crates/utils/tcp_connection/src/lib.rs b/crates/utils/tcp_connection/src/lib.rs
index 928457b..a5b5c20 100644
--- a/crates/utils/tcp_connection/src/lib.rs
+++ b/crates/utils/tcp_connection/src/lib.rs
@@ -1,10 +1,4 @@
-pub mod target;
-pub mod target_configure;
-pub mod target_connection;
-
#[allow(dead_code)]
pub mod instance;
-pub mod handle;
-
pub mod error;
diff --git a/crates/utils/tcp_connection/tcp_connection_test/Cargo.toml b/crates/utils/tcp_connection/tcp_connection_test/Cargo.toml
index e4cba71..397f13a 100644
--- a/crates/utils/tcp_connection/tcp_connection_test/Cargo.toml
+++ b/crates/utils/tcp_connection/tcp_connection_test/Cargo.toml
@@ -6,3 +6,4 @@ version.workspace = true
[dependencies]
tcp_connection = { path = "../../tcp_connection" }
tokio = { version = "1.46.1", features = ["full"] }
+serde = { version = "1.0.219", features = ["derive"] }
diff --git a/crates/utils/tcp_connection/tcp_connection_test/src/lib.rs b/crates/utils/tcp_connection/tcp_connection_test/src/lib.rs
index f0eb66e..c9372d4 100644
--- a/crates/utils/tcp_connection/tcp_connection_test/src/lib.rs
+++ b/crates/utils/tcp_connection/tcp_connection_test/src/lib.rs
@@ -9,3 +9,9 @@ pub mod test_challenge;
#[cfg(test)]
pub mod test_file_transfer;
+
+#[cfg(test)]
+pub mod test_msgpack;
+
+pub mod test_utils;
+pub use test_utils::*;
diff --git a/crates/utils/tcp_connection/tcp_connection_test/src/test_challenge.rs b/crates/utils/tcp_connection/tcp_connection_test/src/test_challenge.rs
index 95b0e3c..2fc1a87 100644
--- a/crates/utils/tcp_connection/tcp_connection_test/src/test_challenge.rs
+++ b/crates/utils/tcp_connection/tcp_connection_test/src/test_challenge.rs
@@ -1,16 +1,17 @@
use std::{env::current_dir, time::Duration};
-use tcp_connection::{
- handle::{ClientHandle, ServerHandle},
- instance::ConnectionInstance,
- target::TcpServerTarget,
- target_configure::ServerTargetConfig,
-};
+use tcp_connection::instance::ConnectionInstance;
use tokio::{
join,
time::{sleep, timeout},
};
+use crate::test_utils::{
+ handle::{ClientHandle, ServerHandle},
+ target::TcpServerTarget,
+ target_configure::ServerTargetConfig,
+};
+
pub(crate) struct ExampleChallengeClientHandle;
impl ClientHandle<ExampleChallengeServerHandle> for ExampleChallengeClientHandle {
diff --git a/crates/utils/tcp_connection/tcp_connection_test/src/test_connection.rs b/crates/utils/tcp_connection/tcp_connection_test/src/test_connection.rs
index 79aac65..8c3ab01 100644
--- a/crates/utils/tcp_connection/tcp_connection_test/src/test_connection.rs
+++ b/crates/utils/tcp_connection/tcp_connection_test/src/test_connection.rs
@@ -1,12 +1,13 @@
use std::time::Duration;
-use tcp_connection::{
+use tcp_connection::instance::ConnectionInstance;
+use tokio::{join, time::sleep};
+
+use crate::test_utils::{
handle::{ClientHandle, ServerHandle},
- instance::ConnectionInstance,
target::TcpServerTarget,
target_configure::ServerTargetConfig,
};
-use tokio::{join, time::sleep};
pub(crate) struct ExampleClientHandle;
diff --git a/crates/utils/tcp_connection/tcp_connection_test/src/test_file_transfer.rs b/crates/utils/tcp_connection/tcp_connection_test/src/test_file_transfer.rs
index 9425d30..4237ea7 100644
--- a/crates/utils/tcp_connection/tcp_connection_test/src/test_file_transfer.rs
+++ b/crates/utils/tcp_connection/tcp_connection_test/src/test_file_transfer.rs
@@ -1,16 +1,17 @@
use std::{env::current_dir, time::Duration};
-use tcp_connection::{
- handle::{ClientHandle, ServerHandle},
- instance::ConnectionInstance,
- target::TcpServerTarget,
- target_configure::ServerTargetConfig,
-};
+use tcp_connection::instance::ConnectionInstance;
use tokio::{
join,
time::{sleep, timeout},
};
+use crate::test_utils::{
+ handle::{ClientHandle, ServerHandle},
+ target::TcpServerTarget,
+ target_configure::ServerTargetConfig,
+};
+
pub(crate) struct ExampleFileTransferClientHandle;
impl ClientHandle<ExampleFileTransferServerHandle> for ExampleFileTransferClientHandle {
diff --git a/crates/utils/tcp_connection/tcp_connection_test/src/test_msgpack.rs b/crates/utils/tcp_connection/tcp_connection_test/src/test_msgpack.rs
new file mode 100644
index 0000000..7a7dc1f
--- /dev/null
+++ b/crates/utils/tcp_connection/tcp_connection_test/src/test_msgpack.rs
@@ -0,0 +1,112 @@
+use serde::{Deserialize, Serialize};
+use std::time::Duration;
+use tcp_connection::instance::ConnectionInstance;
+use tokio::{join, time::sleep};
+
+use crate::test_utils::{
+ handle::{ClientHandle, ServerHandle},
+ target::TcpServerTarget,
+ target_configure::ServerTargetConfig,
+};
+
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+struct TestData {
+ id: u32,
+ name: String,
+}
+
+impl Default for TestData {
+ fn default() -> Self {
+ Self {
+ id: 0,
+ name: String::new(),
+ }
+ }
+}
+
+pub(crate) struct MsgPackClientHandle;
+
+impl ClientHandle<MsgPackServerHandle> for MsgPackClientHandle {
+ async fn process(mut instance: ConnectionInstance) {
+ // Test basic MessagePack serialization
+ let test_data = TestData {
+ id: 42,
+ name: "Test MessagePack".to_string(),
+ };
+
+ // Write MessagePack data
+ if let Err(e) = instance.write_msgpack(&test_data).await {
+ panic!("Write MessagePack failed: {}", e);
+ }
+
+ // Read response
+ let response: TestData = match instance.read_msgpack().await {
+ Ok(data) => data,
+ Err(e) => panic!("Read MessagePack response failed: {}", e),
+ };
+
+ // Verify response
+ assert_eq!(response.id, test_data.id * 2);
+ assert_eq!(response.name, format!("Processed: {}", test_data.name));
+ }
+}
+
+pub(crate) struct MsgPackServerHandle;
+
+impl ServerHandle<MsgPackClientHandle> for MsgPackServerHandle {
+ async fn process(mut instance: ConnectionInstance) {
+ // Read MessagePack data
+ let received_data: TestData = match instance.read_msgpack().await {
+ Ok(data) => data,
+ Err(_) => return,
+ };
+
+ // Process data
+ let response = TestData {
+ id: received_data.id * 2,
+ name: format!("Processed: {}", received_data.name),
+ };
+
+ // Write response as MessagePack
+ if let Err(e) = instance.write_msgpack(&response).await {
+ panic!("Write MessagePack response failed: {}", e);
+ }
+ }
+}
+
+#[tokio::test]
+async fn test_msgpack_basic() {
+ let host = "localhost:5013";
+
+ // Server setup
+ let Ok(server_target) =
+ TcpServerTarget::<MsgPackClientHandle, MsgPackServerHandle>::from_domain(host).await
+ else {
+ panic!("Test target built failed from a domain named `{}`", host);
+ };
+
+ // Client setup
+ let Ok(client_target) =
+ TcpServerTarget::<MsgPackClientHandle, MsgPackServerHandle>::from_domain(host).await
+ else {
+ panic!("Test target built failed from a domain named `{}`", host);
+ };
+
+ let future_server = async move {
+ // Only process once
+ let configured_server = server_target.server_cfg(ServerTargetConfig::default().once());
+
+ // Listen here
+ let _ = configured_server.listen().await;
+ };
+
+ let future_client = async move {
+ // Wait for server start
+ let _ = sleep(Duration::from_secs_f32(1.5)).await;
+
+ // Connect here
+ let _ = client_target.connect().await;
+ };
+
+ let _ = async { join!(future_client, future_server) }.await;
+}
diff --git a/crates/utils/tcp_connection/tcp_connection_test/src/test_tcp_target_build.rs b/crates/utils/tcp_connection/tcp_connection_test/src/test_tcp_target_build.rs
index bcaada3..aa1ec74 100644
--- a/crates/utils/tcp_connection/tcp_connection_test/src/test_tcp_target_build.rs
+++ b/crates/utils/tcp_connection/tcp_connection_test/src/test_tcp_target_build.rs
@@ -1,6 +1,7 @@
-use tcp_connection::target::TcpServerTarget;
-
-use crate::test_connection::{ExampleClientHandle, ExampleServerHandle};
+use crate::{
+ test_connection::{ExampleClientHandle, ExampleServerHandle},
+ test_utils::target::TcpServerTarget,
+};
#[test]
fn test_tcp_test_target_build() {
diff --git a/crates/utils/tcp_connection/tcp_connection_test/src/test_utils.rs b/crates/utils/tcp_connection/tcp_connection_test/src/test_utils.rs
new file mode 100644
index 0000000..badf27d
--- /dev/null
+++ b/crates/utils/tcp_connection/tcp_connection_test/src/test_utils.rs
@@ -0,0 +1,4 @@
+pub mod handle;
+pub mod target;
+pub mod target_configure;
+pub mod target_connection;
diff --git a/crates/utils/tcp_connection/src/handle.rs b/crates/utils/tcp_connection/tcp_connection_test/src/test_utils/handle.rs
index ee77b43..4f9bdbb 100644
--- a/crates/utils/tcp_connection/src/handle.rs
+++ b/crates/utils/tcp_connection/tcp_connection_test/src/test_utils/handle.rs
@@ -1,6 +1,7 @@
-use crate::instance::ConnectionInstance;
use std::future::Future;
+use tcp_connection::instance::ConnectionInstance;
+
pub trait ClientHandle<RequestServer> {
fn process(instance: ConnectionInstance) -> impl Future<Output = ()> + Send;
}
diff --git a/crates/utils/tcp_connection/src/target.rs b/crates/utils/tcp_connection/tcp_connection_test/src/test_utils/target.rs
index 88b931a..8972b2a 100644
--- a/crates/utils/tcp_connection/src/target.rs
+++ b/crates/utils/tcp_connection/tcp_connection_test/src/test_utils/target.rs
@@ -1,5 +1,3 @@
-use crate::handle::{ClientHandle, ServerHandle};
-use crate::target_configure::{ClientTargetConfig, ServerTargetConfig};
use serde::{Deserialize, Serialize};
use std::{
fmt::{Display, Formatter},
@@ -9,6 +7,11 @@ use std::{
};
use tokio::net::lookup_host;
+use crate::test_utils::{
+ handle::{ClientHandle, ServerHandle},
+ target_configure::{ClientTargetConfig, ServerTargetConfig},
+};
+
const DEFAULT_PORT: u16 = 8080;
#[derive(Debug, Serialize, Deserialize)]
diff --git a/crates/utils/tcp_connection/src/target_configure.rs b/crates/utils/tcp_connection/tcp_connection_test/src/test_utils/target_configure.rs
index d739ac9..d739ac9 100644
--- a/crates/utils/tcp_connection/src/target_configure.rs
+++ b/crates/utils/tcp_connection/tcp_connection_test/src/test_utils/target_configure.rs
diff --git a/crates/utils/tcp_connection/src/target_connection.rs b/crates/utils/tcp_connection/tcp_connection_test/src/test_utils/target_connection.rs
index 87fd1ab..d5bf2c3 100644
--- a/crates/utils/tcp_connection/src/target_connection.rs
+++ b/crates/utils/tcp_connection/tcp_connection_test/src/test_utils/target_connection.rs
@@ -1,12 +1,11 @@
+use tcp_connection::{error::TcpTargetError, instance::ConnectionInstance};
use tokio::{
net::{TcpListener, TcpSocket},
spawn,
};
-use crate::{
- error::TcpTargetError,
+use crate::test_utils::{
handle::{ClientHandle, ServerHandle},
- instance::ConnectionInstance,
target::TcpServerTarget,
target_configure::ServerTargetConfig,
};
@@ -17,7 +16,10 @@ where
Server: ServerHandle<Client>,
{
/// Attempts to establish a connection to the TCP server.
- /// This function initiates a connection to the server address specified in the target configuration.
+ ///
+ /// This function initiates a connection to the server address
+ /// specified in the target configuration.
+ ///
/// This is a Block operation.
pub async fn connect(&self) -> Result<(), TcpTargetError> {
let addr = self.get_addr();
@@ -37,7 +39,9 @@ where
}
/// Attempts to establish a connection to the TCP server.
- /// This function initiates a connection to the server address specified in the target configuration.
+ ///
+ /// This function initiates a connection to the server address
+ /// specified in the target configuration.
pub async fn listen(&self) -> Result<(), TcpTargetError> {
let addr = self.get_addr();
let listener = match TcpListener::bind(addr).await {
diff --git a/crates/vcs/src/data/sheet.rs b/crates/vcs/src/data/sheet.rs
index edf307a..95599ff 100644
--- a/crates/vcs/src/data/sheet.rs
+++ b/crates/vcs/src/data/sheet.rs
@@ -2,6 +2,7 @@ use std::{collections::HashMap, path::PathBuf};
use cfg_file::{ConfigFile, config::ConfigFile};
use serde::{Deserialize, Serialize};
+use string_proc::simple_processer::sanitize_file_path;
use crate::{
constants::SERVER_FILE_SHEET,
@@ -16,14 +17,24 @@ pub type SheetPathBuf = PathBuf;
pub type InputName = String;
pub type InputRelativePathBuf = PathBuf;
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, Eq)]
pub struct InputPackage {
/// Name of the input package
pub name: InputName,
+
+ /// The sheet from which this input package was created
+ pub from: SheetName,
+
/// Files in this input package with their relative paths and virtual file IDs
pub files: Vec<(InputRelativePathBuf, VirtualFileId)>,
}
+impl PartialEq for InputPackage {
+ fn eq(&self, other: &Self) -> bool {
+ self.name == other.name
+ }
+}
+
const SHEET_NAME: &str = "{sheet-name}";
pub struct Sheet<'a> {
@@ -60,21 +71,30 @@ impl<'a> Sheet<'a> {
&self.data.inputs
}
+ /// Get the names of the inputs of this sheet
+ pub fn input_names(&self) -> Vec<String> {
+ self.data
+ .inputs
+ .iter()
+ .map(|input| input.name.clone())
+ .collect()
+ }
+
/// Get the mapping of this sheet
pub fn mapping(&self) -> &HashMap<SheetPathBuf, VirtualFileId> {
&self.data.mapping
}
/// Add an input package to the sheet
- pub fn add_input(
- &mut self,
- input_name: InputName,
- files: Vec<(InputRelativePathBuf, VirtualFileId)>,
- ) {
- self.data.inputs.push(InputPackage {
- name: input_name,
- files,
- });
+ pub fn add_input(&mut self, input_package: InputPackage) -> Result<(), std::io::Error> {
+ if self.data.inputs.iter().any(|input| input == &input_package) {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::AlreadyExists,
+ format!("Input package '{}' already exists", input_package.name),
+ ));
+ }
+ self.data.inputs.push(input_package);
+ Ok(())
}
/// Remove an input package from the sheet
@@ -116,4 +136,89 @@ impl<'a> Sheet<'a> {
.vault_path()
.join(SERVER_FILE_SHEET.replace(SHEET_NAME, name.as_ref()))
}
+
+ /// Export files from the current sheet as an InputPackage for importing into other sheets
+ ///
+ /// This is the recommended way to create InputPackages. It takes a list of sheet paths
+ /// and generates an InputPackage with optimized relative paths by removing the longest
+ /// common prefix from all provided paths, then placing the files under a directory
+ /// named with the output_name.
+ ///
+ /// # Example
+ /// Given paths:
+ /// - `MyProject/Art/Character/Model/final.fbx`
+ /// - `MyProject/Art/Character/Texture/final.png`
+ /// - `MyProject/Art/Character/README.md`
+ ///
+ /// With output_name = "MyExport", the resulting package will contain:
+ /// - `MyExport/Model/final.fbx`
+ /// - `MyExport/Texture/final.png`
+ /// - `MyExport/README.md`
+ ///
+ /// # Arguments
+ /// * `output_name` - Name of the output package (will be used as the root directory)
+ /// * `paths` - List of sheet paths to include in the package
+ ///
+ /// # Returns
+ /// Returns an InputPackage containing the exported files with optimized paths,
+ /// or an error if paths are empty or files are not found in the sheet mapping
+ pub fn output_mappings(
+ &self,
+ output_name: InputName,
+ paths: &[SheetPathBuf],
+ ) -> Result<InputPackage, std::io::Error> {
+ let output_name = sanitize_file_path(output_name);
+
+ // Return error for empty paths since there's no need to generate an empty package
+ if paths.is_empty() {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::InvalidInput,
+ "Cannot generate output package with empty paths",
+ ));
+ }
+
+ // Find the longest common prefix among all paths
+ let common_prefix = paths.iter().skip(1).fold(paths[0].clone(), |prefix, path| {
+ Self::common_path_prefix(prefix, path)
+ });
+
+ // Create output files with optimized relative paths
+ let files = paths
+ .iter()
+ .map(|path| {
+ let relative_path = path.strip_prefix(&common_prefix).unwrap_or(path);
+ let output_path = PathBuf::from(&output_name).join(relative_path);
+
+ self.data
+ .mapping
+ .get(path)
+ .map(|vfid| (output_path, vfid.clone()))
+ .ok_or_else(|| {
+ std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ format!("File not found: {:?}", path),
+ )
+ })
+ })
+ .collect::<Result<Vec<_>, _>>()?;
+
+ Ok(InputPackage {
+ name: output_name,
+ from: self.name.clone(),
+ files,
+ })
+ }
+
+ /// Helper function to find common path prefix between two paths
+ fn common_path_prefix(path1: impl Into<PathBuf>, path2: impl Into<PathBuf>) -> PathBuf {
+ let path1 = path1.into();
+ let path2 = path2.into();
+
+ path1
+ .components()
+ .zip(path2.components())
+ .take_while(|(a, b)| a == b)
+ .map(|(comp, _)| comp)
+ .collect()
+ }
}
diff --git a/crates/vcs/src/data/vault.rs b/crates/vcs/src/data/vault.rs
index 5b34c6f..5d17a81 100644
--- a/crates/vcs/src/data/vault.rs
+++ b/crates/vcs/src/data/vault.rs
@@ -72,9 +72,7 @@ impl Vault {
create_dir_all(vault_path.join(SERVER_PATH_VF_ROOT))?;
let Some(vault) = Vault::init(config, &vault_path) else {
- return Err(std::io::Error::other(
- "Failed to initialize vault",
- ));
+ return Err(std::io::Error::other("Failed to initialize vault"));
};
// 6. Create host member
diff --git a/crates/vcs/src/data/vault/config.rs b/crates/vcs/src/data/vault/config.rs
index e879325..40ba09f 100644
--- a/crates/vcs/src/data/vault/config.rs
+++ b/crates/vcs/src/data/vault/config.rs
@@ -23,6 +23,7 @@ impl Default for VaultConfig {
}
}
+/// Vault Management
impl VaultConfig {
// Change name of the vault.
pub fn change_name(&mut self, name: impl Into<String>) {
diff --git a/crates/vcs/src/data/vault/sheets.rs b/crates/vcs/src/data/vault/sheets.rs
index bcd5779..0bba4f5 100644
--- a/crates/vcs/src/data/vault/sheets.rs
+++ b/crates/vcs/src/data/vault/sheets.rs
@@ -13,6 +13,7 @@ use crate::{
},
};
+/// Vault Sheets Management
impl Vault {
/// Load all sheets in the vault
///
@@ -153,6 +154,9 @@ impl Vault {
/// and will not be used in the future.
///
/// For a safer deletion method, consider using `delete_sheet_safety`.
+ ///
+ /// Note: This function is intended for server-side use only and should not be
+ /// arbitrarily called by other members to prevent unauthorized data deletion.
pub async fn delete_sheet(&self, sheet_name: &SheetName) -> Result<(), std::io::Error> {
let sheet_name = snake_case!(sheet_name.clone());
@@ -173,11 +177,18 @@ impl Vault {
/// Safely delete the sheet
///
- /// The sheet will be moved to the trash directory, ensuring it does not appear in the results of `sheets` and `sheet_names` methods.
- /// However, if the sheet's holder attempts to access the sheet through the `sheet` method, the system will automatically restore it from the trash directory.
- /// This means: the sheet will only permanently remain in the trash directory, waiting for manual cleanup by an administrator, when it is truly no longer in use.
+ /// The sheet will be moved to the trash directory, ensuring it does not appear in the
+ /// results of `sheets` and `sheet_names` methods.
+ /// However, if the sheet's holder attempts to access the sheet through the `sheet` method,
+ /// the system will automatically restore it from the trash directory.
+ /// This means: the sheet will only permanently remain in the trash directory,
+ /// waiting for manual cleanup by an administrator, when it is truly no longer in use.
+ ///
+ /// This is a safer deletion method because it provides the possibility of recovery,
+ /// avoiding irreversible data loss caused by accidental deletion.
///
- /// This is a safer deletion method because it provides the possibility of recovery, avoiding irreversible data loss caused by accidental deletion.
+ /// Note: This function is intended for server-side use only and should not be
+ /// arbitrarily called by other members to prevent unauthorized data deletion.
pub async fn delete_sheet_safely(&self, sheet_name: &SheetName) -> Result<(), std::io::Error> {
let sheet_name = snake_case!(sheet_name.clone());
diff --git a/crates/vcs/src/data/vault/virtual_file.rs b/crates/vcs/src/data/vault/virtual_file.rs
index 23e964a..83b1c82 100644
--- a/crates/vcs/src/data/vault/virtual_file.rs
+++ b/crates/vcs/src/data/vault/virtual_file.rs
@@ -70,6 +70,7 @@ impl VirtualFileVersionDescription {
}
}
+/// Virtual File Operations
impl Vault {
/// Generate a temporary path for receiving
pub fn virtual_file_temp_path(&self) -> PathBuf {
diff --git a/crates/vcs/src/lib.rs b/crates/vcs/src/lib.rs
index 1b41391..9a84b4d 100644
--- a/crates/vcs/src/lib.rs
+++ b/crates/vcs/src/lib.rs
@@ -3,3 +3,5 @@ pub mod current;
#[allow(dead_code)]
pub mod data;
+
+pub mod service;
diff --git a/crates/vcs/src/service.rs b/crates/vcs/src/service.rs
new file mode 100644
index 0000000..53365b8
--- /dev/null
+++ b/crates/vcs/src/service.rs
@@ -0,0 +1,2 @@
+pub mod server_entry;
+pub mod standard_handle;
diff --git a/crates/vcs/src/service/server_entry.rs b/crates/vcs/src/service/server_entry.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/crates/vcs/src/service/server_entry.rs
diff --git a/crates/vcs/src/service/standard_handle.rs b/crates/vcs/src/service/standard_handle.rs
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/crates/vcs/src/service/standard_handle.rs
@@ -0,0 +1 @@
+
diff --git a/crates/vcs/vcs_test/Cargo.toml b/crates/vcs/vcs_test/Cargo.toml
index 0ed51d8..1cc43ac 100644
--- a/crates/vcs/vcs_test/Cargo.toml
+++ b/crates/vcs/vcs_test/Cargo.toml
@@ -5,6 +5,7 @@ version.workspace = true
[dependencies]
tcp_connection = { path = "../../utils/tcp_connection" }
+tcp_connection_test = { path = "../../utils/tcp_connection/tcp_connection_test" }
cfg_file = { path = "../../utils/cfg_file", features = ["default"] }
vcs = { path = "../../vcs" }
diff --git a/crates/vcs/vcs_test/src/test_sheet_creation_management_and_persistence.rs b/crates/vcs/vcs_test/src/test_sheet_creation_management_and_persistence.rs
index 1c7182b..8abcc4d 100644
--- a/crates/vcs/vcs_test/src/test_sheet_creation_management_and_persistence.rs
+++ b/crates/vcs/vcs_test/src/test_sheet_creation_management_and_persistence.rs
@@ -48,20 +48,23 @@ async fn test_sheet_creation_management_and_persistence() -> Result<(), std::io:
// Test 2: Add input packages to the sheet
let input_name = "source_files".to_string();
- let files = vec![
- (
- InputRelativePathBuf::from("src/main.rs"),
- VirtualFileId::new(),
- ),
- (
- InputRelativePathBuf::from("src/lib.rs"),
- VirtualFileId::new(),
- ),
- ];
- // Need to get a mutable reference to the sheet
+ // First add mapping entries that will be used to generate the input package
let mut sheet = vault.sheet(&sheet_name).await?;
- sheet.add_input(input_name.clone(), files.clone());
+
+ // Add mapping entries for the files
+ let main_rs_path = vcs::data::sheet::SheetPathBuf::from("src/main.rs");
+ let lib_rs_path = vcs::data::sheet::SheetPathBuf::from("src/lib.rs");
+ let main_rs_id = VirtualFileId::new();
+ let lib_rs_id = VirtualFileId::new();
+
+ sheet.add_mapping(main_rs_path.clone(), main_rs_id.clone());
+ sheet.add_mapping(lib_rs_path.clone(), lib_rs_id.clone());
+
+ // Use output_mappings to generate the InputPackage
+ let paths = vec![main_rs_path, lib_rs_path];
+ let input_package = sheet.output_mappings(input_name.clone(), &paths)?;
+ sheet.add_input(input_package)?;
// Verify input was added
assert_eq!(sheet.inputs().len(), 1);
@@ -70,11 +73,11 @@ async fn test_sheet_creation_management_and_persistence() -> Result<(), std::io:
assert_eq!(added_input.files.len(), 2);
assert_eq!(
added_input.files[0].0,
- InputRelativePathBuf::from("src/main.rs")
+ InputRelativePathBuf::from("source_files/main.rs")
);
assert_eq!(
added_input.files[1].0,
- InputRelativePathBuf::from("src/lib.rs")
+ InputRelativePathBuf::from("source_files/lib.rs")
);
// Test 3: Add mapping entries
@@ -83,7 +86,7 @@ async fn test_sheet_creation_management_and_persistence() -> Result<(), std::io:
sheet.add_mapping(mapping_path.clone(), virtual_file_id.clone());
// Verify mapping was added
- assert_eq!(sheet.mapping().len(), 1);
+ assert_eq!(sheet.mapping().len(), 3);
assert_eq!(sheet.mapping().get(&mapping_path), Some(&virtual_file_id));
// Test 4: Persist sheet to disk
@@ -93,7 +96,7 @@ async fn test_sheet_creation_management_and_persistence() -> Result<(), std::io:
let reloaded_sheet = vault.sheet(&sheet_name).await?;
assert_eq!(reloaded_sheet.holder(), &member_id);
assert_eq!(reloaded_sheet.inputs().len(), 1);
- assert_eq!(reloaded_sheet.mapping().len(), 1);
+ assert_eq!(reloaded_sheet.mapping().len(), 3);
// Test 5: Remove input package
let mut sheet_for_removal = vault.sheet(&sheet_name).await?;
@@ -107,7 +110,7 @@ async fn test_sheet_creation_management_and_persistence() -> Result<(), std::io:
// Test 6: Remove mapping entry
let removed_virtual_file_id = sheet_for_removal.remove_mapping(&mapping_path);
assert_eq!(removed_virtual_file_id, Some(virtual_file_id));
- assert_eq!(sheet_for_removal.mapping().len(), 0);
+ assert_eq!(sheet_for_removal.mapping().len(), 2);
// Test 7: List all sheets in vault
let sheet_names = vault.sheet_names()?;
@@ -244,7 +247,7 @@ async fn test_sheet_data_serialization() -> Result<(), std::io::Error> {
// Add some inputs
let input_name = "source_files".to_string();
- let files = vec![
+ let _files = vec![
(
InputRelativePathBuf::from("src/main.rs"),
VirtualFileId::new(),
@@ -254,7 +257,19 @@ async fn test_sheet_data_serialization() -> Result<(), std::io::Error> {
VirtualFileId::new(),
),
];
- sheet.add_input(input_name, files);
+ // First add mapping entries
+ let main_rs_path = vcs::data::sheet::SheetPathBuf::from("src/main.rs");
+ let lib_rs_path = vcs::data::sheet::SheetPathBuf::from("src/lib.rs");
+ let main_rs_id = VirtualFileId::new();
+ let lib_rs_id = VirtualFileId::new();
+
+ sheet.add_mapping(main_rs_path.clone(), main_rs_id.clone());
+ sheet.add_mapping(lib_rs_path.clone(), lib_rs_id.clone());
+
+ // Use output_mappings to generate the InputPackage
+ let paths = vec![main_rs_path, lib_rs_path];
+ let input_package = sheet.output_mappings(input_name.clone(), &paths)?;
+ sheet.add_input(input_package)?;
// Add some mappings
sheet.add_mapping(
diff --git a/crates/vcs/vcs_test/src/test_virtual_file_creation_and_update.rs b/crates/vcs/vcs_test/src/test_virtual_file_creation_and_update.rs
index 598e7be..d86c13a 100644
--- a/crates/vcs/vcs_test/src/test_virtual_file_creation_and_update.rs
+++ b/crates/vcs/vcs_test/src/test_virtual_file_creation_and_update.rs
@@ -1,7 +1,7 @@
use std::time::Duration;
use cfg_file::config::ConfigFile;
-use tcp_connection::{
+use tcp_connection_test::{
handle::{ClientHandle, ServerHandle},
target::TcpServerTarget,
target_configure::ServerTargetConfig,