From c811c5818d21a67280ef9dd35ad40f6f5411daa5 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Sat, 21 Mar 2026 16:37:51 +0800 Subject: Good Bye! Legacy JVCS --- legacy_data/src/data/vault/mapping_share.rs | 420 ---------------------------- 1 file changed, 420 deletions(-) delete mode 100644 legacy_data/src/data/vault/mapping_share.rs (limited to 'legacy_data/src/data/vault/mapping_share.rs') diff --git a/legacy_data/src/data/vault/mapping_share.rs b/legacy_data/src/data/vault/mapping_share.rs deleted file mode 100644 index 2635b48..0000000 --- a/legacy_data/src/data/vault/mapping_share.rs +++ /dev/null @@ -1,420 +0,0 @@ -use std::{collections::HashMap, io::Error, path::PathBuf}; - -use cfg_file::{ConfigFile, config::ConfigFile}; -use just_fmt::{fmt_path::fmt_path_str, snake_case}; -use rand::{Rng, rng}; -use serde::{Deserialize, Serialize}; -use tokio::fs; - -use crate::{ - constants::{ - KEY_SHARE_ID, KEY_SHEET_NAME, SERVER_FILE_SHEET_SHARE, SERVER_PATH_SHARES, - SERVER_SUFFIX_SHEET_SHARE_FILE_NO_DOT, - }, - data::{ - member::MemberId, - sheet::{Sheet, SheetMappingMetadata, SheetName, SheetPathBuf}, - vault::Vault, - }, -}; - -pub type SheetShareId = String; - -#[derive(Default, Serialize, Deserialize, ConfigFile, Clone, Debug)] -pub struct Share { - /// Sharer: the member who created this share item - #[serde(rename = "sharer")] - pub sharer: MemberId, - - /// Description of the share item - #[serde(rename = "desc")] - pub description: String, - - /// Metadata path - #[serde(skip)] - pub path: Option, - - /// From: which sheet the member exported the file from - #[serde(rename = "from")] - pub from_sheet: SheetName, - - /// Mappings: the sheet mappings contained in the share item - #[serde(rename = "map")] - pub mappings: HashMap, -} - -#[derive(Default, Serialize, Deserialize, ConfigFile, Clone, PartialEq, Eq)] -pub enum ShareMergeMode { - /// If a path or file already exists during merge, prioritize the incoming share - /// Path conflict: replace the mapping content at the local path with the incoming content - /// File conflict: delete the original file mapping and create a new one - Overwrite, - - /// If a path or file already exists during merge, skip overwriting this entry - Skip, - - /// Pre-check for conflicts, prohibit merging if any conflicts are found - #[default] - Safe, - - /// Reject all shares - RejectAll, -} - -#[derive(Default, Serialize, Deserialize, ConfigFile, Clone)] -pub struct ShareMergeConflict { - /// Duplicate mappings exist - pub duplicate_mapping: Vec, - - /// Duplicate files exist - pub duplicate_file: Vec, -} - -impl ShareMergeConflict { - /// Check if there are no conflicts - pub fn ok(&self) -> bool { - self.duplicate_mapping.is_empty() && self.duplicate_file.is_empty() - } -} - -impl Vault { - /// Get the path of a share item in a sheet - pub fn share_file_path(&self, sheet_name: &SheetName, share_id: &SheetShareId) -> PathBuf { - let sheet_name = snake_case!(sheet_name.clone()); - let share_id = share_id.clone(); - - // Format the path to remove "./" prefix and normalize it - let path_str = SERVER_FILE_SHEET_SHARE - .replace(KEY_SHEET_NAME, &sheet_name) - .replace(KEY_SHARE_ID, &share_id); - - // Use format_path to normalize the path - match fmt_path_str(&path_str) { - Ok(normalized_path) => self.vault_path().join(normalized_path), - Err(_) => { - // Fallback to original behavior if formatting fails - self.vault_path().join(path_str) - } - } - } - - /// Get the actual paths of all share items in a sheet - pub async fn share_file_paths(&self, sheet_name: &SheetName) -> Vec { - let sheet_name = snake_case!(sheet_name.clone()); - let shares_dir = self - .vault_path() - .join(SERVER_PATH_SHARES.replace(KEY_SHEET_NAME, &sheet_name)); - - let mut result = Vec::new(); - if let Ok(mut entries) = fs::read_dir(shares_dir).await { - while let Ok(Some(entry)) = entries.next_entry().await { - let path = entry.path(); - if path.is_file() - && path.extension().and_then(|s| s.to_str()) - == Some(SERVER_SUFFIX_SHEET_SHARE_FILE_NO_DOT) - { - result.push(path); - } - } - } - result - } -} - -impl<'a> Sheet<'a> { - /// Get the shares of a sheet - pub async fn get_shares(&self) -> Result, std::io::Error> { - let paths = self.vault_reference.share_file_paths(&self.name).await; - let mut shares = Vec::new(); - - for path in paths { - match Share::read_from(&path).await { - Ok(mut share) => { - share.path = Some(path); - shares.push(share); - } - Err(e) => return Err(e), - } - } - - Ok(shares) - } - - /// Get a share of a sheet - pub async fn get_share(&self, share_id: &SheetShareId) -> Result { - let path = self.vault_reference.share_file_path(&self.name, share_id); - let mut share = Share::read_from(&path).await?; - share.path = Some(path); - Ok(share) - } - - /// Import a share of a sheet by its ID - pub async fn merge_share_by_id( - self, - share_id: &SheetShareId, - share_merge_mode: ShareMergeMode, - ) -> Result<(), std::io::Error> { - let share = self.get_share(share_id).await?; - self.merge_share(share, share_merge_mode).await - } - - /// Import a share of a sheet - pub async fn merge_share( - mut self, - share: Share, - share_merge_mode: ShareMergeMode, - ) -> Result<(), std::io::Error> { - // Backup original data and edit based on this backup - let mut copy_share = share.clone(); - let mut copy_sheet = self.clone_data(); - - // Pre-check - let conflicts = self.precheck(©_share); - let mut reject_mode = false; - - match share_merge_mode { - // Safe mode: conflicts are not allowed - ShareMergeMode::Safe => { - // Conflicts found - if !conflicts.ok() { - // Do nothing, return Error - return Err(Error::new( - std::io::ErrorKind::AlreadyExists, - "Mappings or files already exist!", - )); - } - } - // Overwrite mode: when conflicts occur, prioritize the share item - ShareMergeMode::Overwrite => { - // Handle duplicate mappings - for path in conflicts.duplicate_mapping { - // Get the share data - let Some(share_value) = copy_share.mappings.remove(&path) else { - return Err(Error::new( - std::io::ErrorKind::NotFound, - format!("Share value `{}` not found!", &path.display()), - )); - }; - // Overwrite - copy_sheet.mapping_mut().insert(path, share_value); - } - - // Handle duplicate IDs - for path in conflicts.duplicate_file { - // Get the share data - let Some(share_value) = copy_share.mappings.remove(&path) else { - return Err(Error::new( - std::io::ErrorKind::NotFound, - format!("Share value `{}` not found!", &path.display()), - )); - }; - - // Extract the file ID - let conflict_vfid = &share_value.id; - - // Through the sheet's ID mapping - let Some(id_mapping) = copy_sheet.id_mapping_mut() else { - return Err(Error::new( - std::io::ErrorKind::NotFound, - "Id mapping not found!", - )); - }; - - // Get the original path from the ID mapping - let Some(raw_path) = id_mapping.remove(conflict_vfid) else { - return Err(Error::new( - std::io::ErrorKind::NotFound, - format!("The path of virtual file `{}' not found!", conflict_vfid), - )); - }; - - // Remove the original path mapping - if copy_sheet.mapping_mut().remove(&raw_path).is_none() { - return Err(Error::new( - std::io::ErrorKind::NotFound, - format!("Remove mapping `{}` failed!", &raw_path.display()), - )); - } - // Insert the new item - copy_sheet.mapping_mut().insert(path, share_value); - } - } - // Skip mode: when conflicts occur, prioritize the local sheet - ShareMergeMode::Skip => { - // Directly remove conflicting items - for path in conflicts.duplicate_mapping { - copy_share.mappings.remove(&path); - } - for path in conflicts.duplicate_file { - copy_share.mappings.remove(&path); - } - } - // Reject all mode: reject all shares - ShareMergeMode::RejectAll => { - reject_mode = true; // Only mark as rejected - } - } - - if !reject_mode { - // Subsequent merging - copy_sheet - .mapping_mut() - .extend(copy_share.mappings.into_iter()); - - // Merge completed - self.data = copy_sheet; // Write the result - - // Merge completed, consume the sheet - self.persist().await.map_err(|err| { - Error::new( - std::io::ErrorKind::NotFound, - format!("Write sheet failed: {}", err), - ) - })?; - } - - // Persistence succeeded, continue to consume the share item - share.remove().await.map_err(|err| { - Error::new( - std::io::ErrorKind::NotFound, - format!("Remove share failed: {}", err.1), - ) - }) - } - - // Pre-check whether the share can be imported into the current sheet without conflicts - fn precheck(&self, share: &Share) -> ShareMergeConflict { - let mut conflicts = ShareMergeConflict::default(); - - for (mapping, metadata) in &share.mappings { - // Check for duplicate mappings - if self.mapping().contains_key(mapping.as_path()) { - conflicts.duplicate_mapping.push(mapping.clone()); - continue; - } - - // Check for duplicate IDs - if let Some(id_mapping) = self.id_mapping() - && id_mapping.contains_key(&metadata.id) { - conflicts.duplicate_file.push(mapping.clone()); - continue; - } - } - - conflicts - } - - /// Share mappings with another sheet - pub async fn share_mappings( - &self, - other_sheet: &SheetName, - mappings: Vec, - sharer: &MemberId, - description: String, - ) -> Result { - let other_sheet = snake_case!(other_sheet.clone()); - let sharer = snake_case!(sharer.clone()); - - // Check if the sheet exists - let sheet_names = self.vault_reference.sheet_names()?; - if !sheet_names.contains(&other_sheet) { - return Err(Error::new( - std::io::ErrorKind::NotFound, - format!("Sheet `{}` not found!", &other_sheet), - )); - } - - // Check if the target file exists, regenerate ID if path already exists, up to 20 attempts - let target_path = { - let mut id; - let mut share_path; - let mut attempts = 0; - - loop { - id = Share::gen_share_id(&sharer); - share_path = self.vault_reference.share_file_path(&other_sheet, &id); - - if !share_path.exists() { - break share_path; - } - - attempts += 1; - if attempts >= 20 { - return Err(Error::new( - std::io::ErrorKind::AlreadyExists, - "Failed to generate unique share ID after 20 attempts!", - )); - } - } - }; - - // Validate that the share is valid - let mut share_mappings = HashMap::new(); - for mapping_path in &mappings { - if let Some(metadata) = self.mapping().get(mapping_path) { - share_mappings.insert(mapping_path.clone(), metadata.clone()); - } else { - return Err(Error::new( - std::io::ErrorKind::NotFound, - format!("Mapping `{}` not found in sheet!", mapping_path.display()), - )); - } - } - - // Build share data - let share_data = Share { - sharer, - description, - path: None, // This is only needed during merging (reading), no need to serialize now - from_sheet: self.name.clone(), - mappings: share_mappings, - }; - - // Write data - Share::write_to(&share_data, target_path).await?; - - Ok(share_data) - } -} - -impl Share { - /// Generate a share ID for a given sharer - pub fn gen_share_id(sharer: &MemberId) -> String { - let sharer_snake = snake_case!(sharer.clone()); - let random_part: String = rng() - .sample_iter(&rand::distr::Alphanumeric) - .take(8) - .map(char::from) - .collect(); - format!("{}@{}", sharer_snake, random_part) - } - - /// Delete a share (reject or remove the share item) - /// If deletion succeeds, returns `Ok(())`; - /// If deletion fails, returns `Err((self, std::io::Error))`, containing the original share object and the error information. - pub async fn remove(self) -> Result<(), (Self, std::io::Error)> { - let Some(path) = &self.path else { - return Err(( - self, - Error::new(std::io::ErrorKind::NotFound, "No share path recorded!"), - )); - }; - - if !path.exists() { - return Err(( - self, - Error::new(std::io::ErrorKind::NotFound, "No share file exists!"), - )); - } - - match fs::remove_file(path).await { - Err(err) => Err(( - self, - Error::other( - format!("Failed to delete share file: {}", err), - ), - )), - Ok(_) => Ok(()), - } - } -} -- cgit