From ad60d1f5e46e0fbe2a5463a511c4fccc9b5198b5 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Mon, 15 Dec 2025 10:05:37 +0800 Subject: Add edit mapping action for sheet operations --- crates/utils/string_proc/src/format_path.rs | 9 ++ crates/vcs_actions/src/actions/sheet_actions.rs | 162 ++++++++++++++++++--- crates/vcs_actions/src/registry/client_registry.rs | 5 +- crates/vcs_actions/src/registry/server_registry.rs | 5 +- crates/vcs_data/src/data/local/align.rs | 3 + crates/vcs_data/src/data/local/file_status.rs | 22 +++ crates/vcs_data/src/data/sheet.rs | 3 + 7 files changed, 188 insertions(+), 21 deletions(-) diff --git a/crates/utils/string_proc/src/format_path.rs b/crates/utils/string_proc/src/format_path.rs index 152b791..35689b8 100644 --- a/crates/utils/string_proc/src/format_path.rs +++ b/crates/utils/string_proc/src/format_path.rs @@ -38,6 +38,11 @@ pub fn format_path_str(path: impl Into) -> Result Result { -// } +pub type OperationArgument = (EditMappingOperations, Option); + +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)] +pub enum EditMappingOperations { + Move, + Erase, +} + +#[derive(Serialize, Deserialize, Default)] +pub enum EditMappingActionResult { + Success, + + // Fail + AuthorizeFailed(String), + MappingNotFound(FromRelativePathBuf), + InvalidMove(InvalidMoveReason), + + #[default] + Unknown, +} + +#[derive(Serialize, Deserialize)] +pub enum InvalidMoveReason { + MoveOperationButNoTarget(FromRelativePathBuf), + ContainsDuplicateMapping(ToRelativePathBuf), +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct EditMappingActionArguments { + pub operations: HashMap, +} + +/// This Action only modifies Sheet Mapping and +/// does not interfere with the actual location of local files or Local Mapping +#[action_gen] +pub async fn edit_mapping_action( + ctx: ActionContext, + args: EditMappingActionArguments, +) -> Result { + let instance = check_connection_instance(&ctx)?; + + // Auth Member + let member_id = match auth_member(&ctx, instance).await { + Ok(id) => id, + Err(e) => { + return Ok(EditMappingActionResult::AuthorizeFailed(e.to_string())); + } + }; + + // Check sheet + let sheet_name = get_current_sheet_name(&ctx, instance, &member_id).await?; + + if ctx.is_proc_on_remote() { + let vault = try_get_vault(&ctx)?; + let mut sheet = vault.sheet(&sheet_name).await?; + + // Precheck + for (from_path, (operation, to_path)) in args.operations.iter() { + // Check mapping exists + if !sheet.mapping().contains_key(from_path) { + write_and_return!( + instance, + EditMappingActionResult::MappingNotFound(from_path.clone()) + ); + } + + // Move check + if operation == &EditMappingOperations::Move { + // Check if target exists + if let Some(to_path) = to_path { + // Check if target is duplicate + if sheet.mapping().contains_key(to_path) { + write_and_return!( + instance, + EditMappingActionResult::InvalidMove( + InvalidMoveReason::ContainsDuplicateMapping(to_path.clone()) + ) + ); + } + } else { + write_and_return!( + instance, + EditMappingActionResult::InvalidMove( + InvalidMoveReason::MoveOperationButNoTarget(from_path.clone()) + ) + ); + } + } + } + + // Process + for (from_path, (operation, to_path)) in args.operations { + match operation { + // During the Precheck phase, it has been ensured that: + // 1. The mapping to be edited for the From path indeed exists + // 2. The location of the To path is indeed empty + // 3. In Move mode, To path can be safely unwrapped + // Therefore, the following unwrap() calls are safe to execute + EditMappingOperations::Move => { + let mapping = sheet.mapping_mut().remove(&from_path).unwrap(); + let to_path = to_path.unwrap(); + sheet + .add_mapping(to_path, mapping.id, mapping.version) + .await?; + } + EditMappingOperations::Erase => { + sheet.mapping_mut().remove(&from_path).unwrap(); + } + } + } + + // Write + sheet.persist().await?; + + write_and_return!(instance, EditMappingActionResult::Success); + } + + if ctx.is_proc_on_local() { + let result = instance + .lock() + .await + .read::() + .await?; + if matches!(result, EditMappingActionResult::Success) { + sign_vault_modified(true).await; + } + return Ok(result); + } + + Ok(EditMappingActionResult::Success) +} diff --git a/crates/vcs_actions/src/registry/client_registry.rs b/crates/vcs_actions/src/registry/client_registry.rs index 6e2814f..6667a74 100644 --- a/crates/vcs_actions/src/registry/client_registry.rs +++ b/crates/vcs_actions/src/registry/client_registry.rs @@ -13,7 +13,9 @@ use crate::{ local_actions::{ register_set_upstream_vault_action, register_update_to_latest_info_action, }, - sheet_actions::{register_drop_sheet_action, register_make_sheet_action}, + sheet_actions::{ + register_drop_sheet_action, register_edit_mapping_action, register_make_sheet_action, + }, track_action::register_track_file_action, user_actions::register_change_virtual_file_edit_right_action, }, @@ -30,6 +32,7 @@ fn register_actions(pool: &mut ActionPool) { // Sheet Actions register_make_sheet_action(pool); register_drop_sheet_action(pool); + register_edit_mapping_action(pool); // Track Action register_track_file_action(pool); diff --git a/crates/vcs_actions/src/registry/server_registry.rs b/crates/vcs_actions/src/registry/server_registry.rs index 9c12d24..404625c 100644 --- a/crates/vcs_actions/src/registry/server_registry.rs +++ b/crates/vcs_actions/src/registry/server_registry.rs @@ -2,7 +2,9 @@ use action_system::action_pool::ActionPool; use crate::actions::{ local_actions::{register_set_upstream_vault_action, register_update_to_latest_info_action}, - sheet_actions::{register_drop_sheet_action, register_make_sheet_action}, + sheet_actions::{ + register_drop_sheet_action, register_edit_mapping_action, register_make_sheet_action, + }, track_action::register_track_file_action, user_actions::register_change_virtual_file_edit_right_action, }; @@ -17,6 +19,7 @@ pub fn server_action_pool() -> ActionPool { // Sheet Actions register_make_sheet_action(&mut pool); register_drop_sheet_action(&mut pool); + register_edit_mapping_action(&mut pool); // Track Action register_track_file_action(&mut pool); diff --git a/crates/vcs_data/src/data/local/align.rs b/crates/vcs_data/src/data/local/align.rs index 1b8c3c9..715c785 100644 --- a/crates/vcs_data/src/data/local/align.rs +++ b/crates/vcs_data/src/data/local/align.rs @@ -16,6 +16,7 @@ pub struct AlignTasks { pub created: Vec<(AlignTasksName, AlignPathBuf)>, pub lost: Vec<(AlignTasksName, AlignPathBuf)>, pub moved: Vec<(AlignTasksName, (AlignLostPathBuf, AlignCreatedPathBuf))>, + pub erased: Vec<(AlignTasksName, AlignPathBuf)>, } impl AlignTasks { @@ -24,6 +25,7 @@ impl AlignTasks { created: path_hash_set_sort_helper(result.created.clone(), "created"), lost: path_hash_set_sort_helper(result.lost.clone(), "lost"), moved: path_hash_map_sort_helper(result.moved.clone(), "moved"), + erased: path_hash_set_sort_helper(result.erased.clone(), "erased"), } } @@ -32,6 +34,7 @@ impl AlignTasks { created: path_hash_set_sort_helper(result.created, "created"), lost: path_hash_set_sort_helper(result.lost, "lost"), moved: path_hash_map_sort_helper(result.moved, "moved"), + erased: path_hash_set_sort_helper(result.erased, "erased"), } } } diff --git a/crates/vcs_data/src/data/local/file_status.rs b/crates/vcs_data/src/data/local/file_status.rs index 7841237..599e4a3 100644 --- a/crates/vcs_data/src/data/local/file_status.rs +++ b/crates/vcs_data/src/data/local/file_status.rs @@ -33,6 +33,9 @@ pub struct AnalyzeResult<'a> { /// Lost local files pub lost: HashSet, + /// Erased local files + pub erased: HashSet, + /// Modified local files (excluding moved files) /// For files that were both moved and modified, changes can only be detected after LocalSheet mapping is aligned with actual files pub modified: HashSet, @@ -154,6 +157,23 @@ impl<'a> AnalyzeResult<'a> { .cloned() .collect(); + // Files that exist locally but not in remote + let mut erased_files: HashSet = HashSet::new(); + + if let Some(cached_data) = &analyze_ctx.cached_sheet_data { + if let Some(local_sheet) = &analyze_ctx.local_sheet { + let cached_sheet_mapping = cached_data.mapping(); + let local_sheet_mapping = &local_sheet.data.mapping; + + // Find paths that exist in local sheet but not in cached sheet + for local_path in local_sheet_mapping.keys() { + if !cached_sheet_mapping.contains_key(local_path) { + erased_files.insert(local_path.clone()); + } + } + } + } + // Calculate hashes for new files let new_files_for_hash: Vec = new_files .iter() @@ -231,6 +251,7 @@ impl<'a> AnalyzeResult<'a> { vfid.map(|vfid| (vfid, (from.clone(), to.clone()))) }) .collect(); + result.erased = erased_files; Ok(()) } @@ -299,6 +320,7 @@ impl<'a> AnalyzeResult<'a> { created: HashSet::new(), lost: HashSet::new(), modified: HashSet::new(), + erased: HashSet::new(), } } } diff --git a/crates/vcs_data/src/data/sheet.rs b/crates/vcs_data/src/data/sheet.rs index 891888a..900331d 100644 --- a/crates/vcs_data/src/data/sheet.rs +++ b/crates/vcs_data/src/data/sheet.rs @@ -69,6 +69,9 @@ pub struct SheetData { pub(crate) id_mapping: Option>, } +#[derive(Default, Serialize, Deserialize, ConfigFile, Clone)] +pub struct SheetInputs {} + #[derive(Debug, Default, Serialize, Deserialize, ConfigFile, Clone, Eq, PartialEq)] pub struct SheetMappingMetadata { pub id: VirtualFileId, -- cgit