From 7b97b52af021500d8085c875d20215e8dc0f53cc Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Mon, 17 Nov 2025 11:49:49 +0800 Subject: feat: Add file status tracking and SHA1 hash system - Implement SHA1 hash calculation module with async support - Add file status analysis for tracking moves, creates, and modifications - Enhance local file management with relative path handling - Update virtual file actions with improved tracking capabilities --- crates/vcs_data/src/data/local/local_sheet.rs | 207 ++++++++++++++++++++++---- 1 file changed, 179 insertions(+), 28 deletions(-) (limited to 'crates/vcs_data/src/data/local/local_sheet.rs') diff --git a/crates/vcs_data/src/data/local/local_sheet.rs b/crates/vcs_data/src/data/local/local_sheet.rs index bfe8d01..980fa49 100644 --- a/crates/vcs_data/src/data/local/local_sheet.rs +++ b/crates/vcs_data/src/data/local/local_sheet.rs @@ -1,8 +1,7 @@ -use std::{collections::HashMap, io::Error, path::PathBuf}; +use std::{collections::HashMap, io::Error, path::PathBuf, time::SystemTime}; use ::serde::{Deserialize, Serialize}; use cfg_file::{ConfigFile, config::ConfigFile}; -use chrono::NaiveDate; use string_proc::format_path::format_path; use crate::{ @@ -10,11 +9,13 @@ use crate::{ data::{ local::LocalWorkspace, member::MemberId, - vault::virtual_file::{VirtualFileId, VirtualFileVersionDescription}, + sheet::SheetName, + vault::virtual_file::{VirtualFileId, VirtualFileVersion, VirtualFileVersionDescription}, }, }; pub type LocalFilePathBuf = PathBuf; +pub type LocalSheetPathBuf = PathBuf; /// # Local Sheet /// Local sheet information, used to record metadata of actual local files, @@ -27,54 +28,168 @@ pub struct LocalSheet<'a> { pub(crate) data: LocalSheetData, } -#[derive(Debug, Default, Serialize, Deserialize, ConfigFile)] +#[derive(Debug, Default, Serialize, Deserialize, ConfigFile, Clone)] #[cfg_file(path = CLIENT_FILE_LOCAL_SHEET_NOSET)] // Do not use LocalSheet::write or LocalSheet::read pub struct LocalSheetData { - /// Local file path to virtual file ID mapping. + /// Local file path to metadata mapping. #[serde(rename = "mapping")] - pub(crate) mapping: HashMap, // Path to VFID + pub(crate) mapping: HashMap, + + pub(crate) vfs: HashMap, } -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct MappingMetaData { +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct LocalMappingMetadata { /// Hash value generated immediately after the file is downloaded to the local workspace #[serde(rename = "hash")] pub(crate) hash_when_updated: String, /// Time when the file was downloaded to the local workspace - #[serde(rename = "date", with = "naive_date_serde")] - pub(crate) date_when_updated: NaiveDate, + pub(crate) time_when_updated: SystemTime, /// Size of the file when downloaded to the local workspace #[serde(rename = "size")] pub(crate) size_when_updated: u64, /// Version description when the file was downloaded to the local workspace - #[serde(rename = "version")] + #[serde(rename = "version_desc")] pub(crate) version_desc_when_updated: VirtualFileVersionDescription, + /// Version when the file was downloaded to the local workspace + #[serde(rename = "version")] + pub(crate) version_when_updated: VirtualFileVersion, + /// Virtual file ID corresponding to the local path #[serde(rename = "id")] pub(crate) mapping_vfid: VirtualFileId, + + /// Latest modifiy check time + pub(crate) last_modifiy_check_time: SystemTime, + + /// Latest modifiy check result + pub(crate) last_modifiy_check_result: bool, +} + +impl LocalSheetData { + /// Wrap LocalSheetData into LocalSheet with workspace, member, and sheet name + pub fn wrap_to_local_sheet<'a>( + self, + workspace: &'a LocalWorkspace, + member: MemberId, + sheet_name: SheetName, + ) -> LocalSheet<'a> { + LocalSheet { + local_workspace: workspace, + member, + sheet_name, + data: self, + } + } +} + +impl LocalMappingMetadata { + /// Create a new MappingMetaData instance + pub fn new( + hash_when_updated: String, + time_when_updated: SystemTime, + size_when_updated: u64, + version_desc_when_updated: VirtualFileVersionDescription, + version_when_updated: VirtualFileVersion, + mapping_vfid: VirtualFileId, + last_modifiy_check_time: SystemTime, + last_modifiy_check_result: bool, + ) -> Self { + Self { + hash_when_updated, + time_when_updated, + size_when_updated, + version_desc_when_updated, + version_when_updated, + mapping_vfid, + last_modifiy_check_time, + last_modifiy_check_result, + } + } + + /// Getter for hash_when_updated + pub fn hash_when_updated(&self) -> &String { + &self.hash_when_updated + } + + /// Getter for date_when_updated + pub fn time_when_updated(&self) -> &SystemTime { + &self.time_when_updated + } + + /// Getter for size_when_updated + pub fn size_when_updated(&self) -> u64 { + self.size_when_updated + } + + /// Getter for version_desc_when_updated + pub fn version_desc_when_updated(&self) -> &VirtualFileVersionDescription { + &self.version_desc_when_updated + } + + /// Getter for version_when_updated + pub fn version_when_updated(&self) -> &VirtualFileVersion { + &self.version_when_updated + } + + /// Getter for mapping_vfid + pub fn mapping_vfid(&self) -> &VirtualFileId { + &self.mapping_vfid + } + + /// Getter for last_modifiy_check_time + pub fn last_modifiy_check_time(&self) -> &SystemTime { + &self.last_modifiy_check_time + } + + /// Getter for last_modifiy_check_result + pub fn last_modifiy_check_result(&self) -> bool { + self.last_modifiy_check_result + } +} + +impl Default for LocalMappingMetadata { + fn default() -> Self { + Self { + hash_when_updated: Default::default(), + time_when_updated: SystemTime::now(), + size_when_updated: Default::default(), + version_desc_when_updated: Default::default(), + version_when_updated: Default::default(), + mapping_vfid: Default::default(), + last_modifiy_check_time: SystemTime::now(), + last_modifiy_check_result: false, + } + } } -mod naive_date_serde { - use chrono::NaiveDate; +mod instant_serde { use serde::{self, Deserialize, Deserializer, Serializer}; + use tokio::time::Instant; - pub fn serialize(date: &NaiveDate, serializer: S) -> Result + pub fn serialize(instant: &Instant, serializer: S) -> Result where S: Serializer, { - serializer.serialize_str(&date.format("%Y-%m-%d").to_string()) + serializer.serialize_u64(instant.elapsed().as_secs()) } - pub fn deserialize<'de, D>(deserializer: D) -> Result + pub fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { - let s = String::deserialize(deserializer)?; - NaiveDate::parse_from_str(&s, "%Y-%m-%d").map_err(serde::de::Error::custom) + let secs = u64::deserialize(deserializer)?; + Ok(Instant::now() - std::time::Duration::from_secs(secs)) + } +} + +impl<'a> From<&'a LocalSheet<'a>> for &'a LocalSheetData { + fn from(sheet: &'a LocalSheet<'a>) -> Self { + &sheet.data } } @@ -83,10 +198,12 @@ impl<'a> LocalSheet<'a> { pub fn add_mapping( &mut self, path: LocalFilePathBuf, - mapping: MappingMetaData, + mapping: LocalMappingMetadata, ) -> Result<(), std::io::Error> { let path = format_path(path)?; - if self.data.mapping.contains_key(&path) { + if self.data.mapping.contains_key(&path) + || self.data.vfs.contains_key(&mapping.mapping_vfid) + { return Err(Error::new( std::io::ErrorKind::AlreadyExists, "Mapping already exists", @@ -100,8 +217,8 @@ impl<'a> LocalSheet<'a> { /// Move mapping to other path pub fn move_mapping( &mut self, - from: LocalFilePathBuf, - to: LocalFilePathBuf, + from: &LocalFilePathBuf, + to: &LocalFilePathBuf, ) -> Result<(), std::io::Error> { let from = format_path(from)?; let to = format_path(to)?; @@ -124,11 +241,26 @@ impl<'a> LocalSheet<'a> { Ok(()) } + /// Get immutable mapping data + pub fn mapping_data( + &self, + path: &LocalFilePathBuf, + ) -> Result<&LocalMappingMetadata, std::io::Error> { + let path = format_path(path)?; + let Some(data) = self.data.mapping.get(&path) else { + return Err(Error::new( + std::io::ErrorKind::NotFound, + "Path is not found.", + )); + }; + Ok(data) + } + /// Get muttable mapping data pub fn mapping_data_mut( &mut self, - path: LocalFilePathBuf, - ) -> Result<&mut MappingMetaData, std::io::Error> { + path: &LocalFilePathBuf, + ) -> Result<&mut LocalMappingMetadata, std::io::Error> { let path = format_path(path)?; let Some(data) = self.data.mapping.get_mut(&path) else { return Err(Error::new( @@ -139,12 +271,31 @@ impl<'a> LocalSheet<'a> { Ok(data) } - /// Persist the sheet to disk - pub async fn persist(&mut self) -> Result<(), std::io::Error> { - let _path = self + /// Write the sheet to disk + pub async fn write(&mut self) -> Result<(), std::io::Error> { + let path = self .local_workspace .local_sheet_path(&self.member, &self.sheet_name); - LocalSheetData::write_to(&self.data, &self.sheet_name).await?; + self.write_to_path(path).await + } + + /// Write the sheet to custom path + pub async fn write_to_path(&mut self, path: impl Into) -> Result<(), std::io::Error> { + let path = path.into(); + + self.data.vfs = HashMap::new(); + for (path, mapping) in self.data.mapping.iter() { + self.data + .vfs + .insert(mapping.mapping_vfid.clone(), path.clone()); + } + + LocalSheetData::write_to(&self.data, path).await?; Ok(()) } + + /// Get path by VirtualFileId + pub fn path_by_id(&self, vfid: &VirtualFileId) -> Option<&PathBuf> { + self.data.vfs.get(vfid) + } } -- cgit