summaryrefslogtreecommitdiff
path: root/crates/vcs/src
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2025-09-29 16:01:42 +0800
committerGitHub <noreply@github.com>2025-09-29 16:01:42 +0800
commit50b801a11e1c3bb3012ac189197b7e84663ba902 (patch)
treeaa9f86e49e01189a4350816c782ee38ccacd8dfd /crates/vcs/src
parent114ed3c65d9d5765a9e9553f1d2f14a3772c6867 (diff)
parentd6283f0964afcf093e4c53df3c05ac9af8e28596 (diff)
Merge pull request #14 from JustEnoughVCS/jvcs_dev
Jvcs dev
Diffstat (limited to 'crates/vcs/src')
-rw-r--r--crates/vcs/src/data/sheet.rs163
-rw-r--r--crates/vcs/src/data/vault/virtual_file.rs4
2 files changed, 146 insertions, 21 deletions
diff --git a/crates/vcs/src/data/sheet.rs b/crates/vcs/src/data/sheet.rs
index 95599ff..a6220c9 100644
--- a/crates/vcs/src/data/sheet.rs
+++ b/crates/vcs/src/data/sheet.rs
@@ -97,8 +97,8 @@ impl<'a> Sheet<'a> {
Ok(())
}
- /// Remove an input package from the sheet
- pub fn remove_input(&mut self, input_name: &InputName) -> Option<InputPackage> {
+ /// Deny and remove an input package from the sheet
+ pub fn deny_input(&mut self, input_name: &InputName) -> Option<InputPackage> {
self.data
.inputs
.iter()
@@ -106,14 +106,127 @@ impl<'a> Sheet<'a> {
.map(|pos| self.data.inputs.remove(pos))
}
- /// Add a mapping entry to the sheet
- pub fn add_mapping(&mut self, sheet_path: SheetPathBuf, virtual_file_id: VirtualFileId) {
- self.data.mapping.insert(sheet_path, virtual_file_id);
+ /// Accept an input package and insert to the sheet
+ pub fn accept_import(
+ &mut self,
+ input_name: &InputName,
+ insert_to: &SheetPathBuf,
+ ) -> Result<(), std::io::Error> {
+ // Remove inputs
+ let input = self
+ .inputs()
+ .iter()
+ .position(|input| input.name == *input_name)
+ .map(|pos| self.data.inputs.remove(pos));
+
+ // Ensure input is not empty
+ let Some(input) = input else {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ "Empty inputs.",
+ ));
+ };
+
+ // Insert to sheet
+ for (relative_path, virtual_file_id) in input.files {
+ let _ = self.add_mapping(insert_to.join(relative_path), virtual_file_id);
+ }
+
+ Ok(())
+ }
+
+ /// Add (or Edit) a mapping entry to the sheet
+ ///
+ /// This operation performs safety checks to ensure the member has the right to add the mapping:
+ /// 1. If the virtual file ID doesn't exist in the vault, the mapping is added directly
+ /// 2. If the virtual file exists, check if the member has edit rights to the virtual file
+ /// 3. If member has edit rights, the mapping is not allowed to be modified and returns an error
+ /// 4. If member doesn't have edit rights, the mapping is allowed (member is giving up the file)
+ ///
+ /// Note: Full validation adds overhead - avoid frequent calls
+ pub async fn add_mapping(
+ &mut self,
+ sheet_path: SheetPathBuf,
+ virtual_file_id: VirtualFileId,
+ ) -> Result<(), std::io::Error> {
+ // Check if the virtual file exists in the vault
+ if self.vault_reference.virtual_file(&virtual_file_id).is_err() {
+ // Virtual file doesn't exist, add the mapping directly
+ self.data.mapping.insert(sheet_path, virtual_file_id);
+ return Ok(());
+ }
+
+ // Check if the holder has edit rights to the virtual file
+ match self
+ .vault_reference
+ .has_virtual_file_edit_right(self.holder(), &virtual_file_id)
+ .await
+ {
+ Ok(false) => {
+ // Holder doesn't have rights, add the mapping (member is giving up the file)
+ self.data.mapping.insert(sheet_path, virtual_file_id);
+ Ok(())
+ }
+ Ok(true) => {
+ // Holder has edit rights, don't allow modifying the mapping
+ Err(std::io::Error::new(
+ std::io::ErrorKind::PermissionDenied,
+ "Member has edit rights to the virtual file, cannot modify mapping",
+ ))
+ }
+ Err(_) => {
+ // Error checking rights, don't allow modifying the mapping
+ Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "Failed to check virtual file edit rights",
+ ))
+ }
+ }
}
/// Remove a mapping entry from the sheet
- pub fn remove_mapping(&mut self, sheet_path: &SheetPathBuf) -> Option<VirtualFileId> {
- self.data.mapping.remove(sheet_path)
+ ///
+ /// This operation performs safety checks to ensure the member has the right to remove the mapping:
+ /// 1. Member must NOT have edit rights to the virtual file to release it (ensuring clear ownership)
+ /// 2. If the virtual file doesn't exist, the mapping is removed but no ID is returned
+ /// 3. If member has no edit rights and the file exists, returns the removed virtual file ID
+ ///
+ /// Note: Full validation adds overhead - avoid frequent calls
+ pub async fn remove_mapping(&mut self, sheet_path: &SheetPathBuf) -> Option<VirtualFileId> {
+ let virtual_file_id = match self.data.mapping.get(sheet_path) {
+ Some(id) => id,
+ None => {
+ // The mapping entry doesn't exist, nothing to remove
+ return None;
+ }
+ };
+
+ // Check if the virtual file exists in the vault
+ if self.vault_reference.virtual_file(virtual_file_id).is_err() {
+ // Virtual file doesn't exist, remove the mapping and return None
+ self.data.mapping.remove(sheet_path);
+ return None;
+ }
+
+ // Check if the holder has edit rights to the virtual file
+ match self
+ .vault_reference
+ .has_virtual_file_edit_right(self.holder(), virtual_file_id)
+ .await
+ {
+ Ok(false) => {
+ // Holder doesn't have rights, remove and return the virtual file ID
+ self.data.mapping.remove(sheet_path)
+ }
+ Ok(true) => {
+ // Holder has edit rights, don't remove the mapping
+ None
+ }
+ Err(_) => {
+ // Error checking rights, don't remove the mapping
+ None
+ }
+ }
}
/// Persist the sheet to disk
@@ -178,9 +291,7 @@ impl<'a> Sheet<'a> {
}
// 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)
- });
+ let common_prefix = Self::find_longest_common_prefix(paths);
// Create output files with optimized relative paths
let files = paths
@@ -209,16 +320,28 @@ impl<'a> Sheet<'a> {
})
}
- /// 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();
+ /// Helper function to find the longest common prefix among all paths
+ fn find_longest_common_prefix(paths: &[SheetPathBuf]) -> PathBuf {
+ if paths.is_empty() {
+ return PathBuf::new();
+ }
+
+ let first_path = &paths[0];
+ let mut common_components = Vec::new();
+
+ for (component_idx, first_component) in first_path.components().enumerate() {
+ for path in paths.iter().skip(1) {
+ if let Some(component) = path.components().nth(component_idx) {
+ if component != first_component {
+ return common_components.into_iter().collect();
+ }
+ } else {
+ return common_components.into_iter().collect();
+ }
+ }
+ common_components.push(first_component);
+ }
- path1
- .components()
- .zip(path2.components())
- .take_while(|(a, b)| a == b)
- .map(|(comp, _)| comp)
- .collect()
+ common_components.into_iter().collect()
}
}
diff --git a/crates/vcs/src/data/vault/virtual_file.rs b/crates/vcs/src/data/vault/virtual_file.rs
index 83b1c82..fe83594 100644
--- a/crates/vcs/src/data/vault/virtual_file.rs
+++ b/crates/vcs/src/data/vault/virtual_file.rs
@@ -225,7 +225,7 @@ impl Vault {
// Create metadata
let mut meta = VirtualFileMeta {
current_version: FIRST_VERSION.to_string(),
- hold_member: String::default(),
+ hold_member: member_id.clone(), // The holder of the newly created virtual file is the creator by default
version_description,
histories: Vec::default(),
};
@@ -244,6 +244,8 @@ impl Vault {
}
fs::rename(receive_path, move_path).await?;
+ //
+
Ok(new_id)
}
Err(e) => {