From f871f39ebfd22b8d7c86de1cc172db2d73a3bf9d Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Thu, 6 Nov 2025 22:11:18 +0800 Subject: feat: Add local data management modules (WIP) - cached_sheet.rs: Cached sheet data management - local_sheet.rs: Local sheet mapping structure - member_held.rs: Member file holding status tracking - NOTE: These modules are still under development --- crates/vcs_data/src/data/local/cached_sheet.rs | 50 ++++++++++++++++++++ crates/vcs_data/src/data/local/local_sheet.rs | 63 ++++++++++++++++++++++++++ crates/vcs_data/src/data/local/member_held.rs | 25 ++++++++++ 3 files changed, 138 insertions(+) create mode 100644 crates/vcs_data/src/data/local/cached_sheet.rs create mode 100644 crates/vcs_data/src/data/local/local_sheet.rs create mode 100644 crates/vcs_data/src/data/local/member_held.rs (limited to 'crates') diff --git a/crates/vcs_data/src/data/local/cached_sheet.rs b/crates/vcs_data/src/data/local/cached_sheet.rs new file mode 100644 index 0000000..7ca1f2f --- /dev/null +++ b/crates/vcs_data/src/data/local/cached_sheet.rs @@ -0,0 +1,50 @@ +use std::{io::Error, path::PathBuf}; + +use cfg_file::config::ConfigFile; +use string_proc::snake_case; + +use crate::{ + constants::CLIENT_FILE_CACHED_SHEET, + current::current_local_path, + data::{ + member::MemberId, + sheet::{SheetData, SheetName}, + }, +}; + +const SHEET_NAME: &str = "{sheet_name}"; +const ACCOUNT_NAME: &str = "{account}"; + +pub struct CachedSheet; + +impl CachedSheet { + /// Read the cached sheet data. + pub async fn cached_sheet_data( + account_name: MemberId, + sheet_name: SheetName, + ) -> Result { + let account_name = snake_case!(account_name); + let sheet_name = snake_case!(sheet_name); + + let Some(path) = Self::cached_sheet_path(account_name, sheet_name) else { + return Err(Error::new( + std::io::ErrorKind::NotFound, + "Local workspace not found!", + )); + }; + let data = SheetData::read_from(path).await?; + Ok(data) + } + + /// Get the path to the cached sheet file. + pub fn cached_sheet_path(account_name: MemberId, sheet_name: SheetName) -> Option { + let current_workspace = current_local_path()?; + Some( + current_workspace.join( + CLIENT_FILE_CACHED_SHEET + .replace(SHEET_NAME, &sheet_name.to_string()) + .replace(ACCOUNT_NAME, &account_name.to_string()), + ), + ) + } +} diff --git a/crates/vcs_data/src/data/local/local_sheet.rs b/crates/vcs_data/src/data/local/local_sheet.rs new file mode 100644 index 0000000..63b73ac --- /dev/null +++ b/crates/vcs_data/src/data/local/local_sheet.rs @@ -0,0 +1,63 @@ +use std::{collections::HashMap, path::PathBuf}; + +use ::serde::{Deserialize, Serialize}; +use cfg_file::ConfigFile; +use chrono::NaiveDate; + +use crate::{ + constants::CLIENT_FILE_LOCAL_SHEET_NOSET, + data::vault::virtual_file::{VirtualFileId, VirtualFileVersionDescription}, +}; + +pub type LocalFilePathBuf = PathBuf; + +#[derive(Debug, Default, Serialize, Deserialize, ConfigFile)] +#[cfg_file(path = CLIENT_FILE_LOCAL_SHEET_NOSET)] // Do not use LocalSheet::write or LocalSheet::read +pub struct LocalSheet { + /// Local file path to virtual file ID mapping. + #[serde(rename = "mapping")] + mapping: HashMap, // Path to VFID +} + +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct MappingMetaData { + /// Hash value generated immediately after the file is downloaded to the local workspace + #[serde(rename = "hash")] + hash_when_updated: String, + + /// Time when the file was downloaded to the local workspace + #[serde(rename = "date", with = "naive_date_serde")] + date_when_updated: NaiveDate, + + /// Size of the file when downloaded to the local workspace + #[serde(rename = "size")] + size_when_updated: u64, + + /// Version description when the file was downloaded to the local workspace + #[serde(rename = "version")] + version_desc_when_updated: VirtualFileVersionDescription, + + /// Virtual file ID corresponding to the local path + #[serde(rename = "id")] + mapping_vfid: VirtualFileId, +} + +mod naive_date_serde { + use chrono::NaiveDate; + use serde::{self, Deserialize, Deserializer, Serializer}; + + pub fn serialize(date: &NaiveDate, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&date.format("%Y-%m-%d").to_string()) + } + + 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) + } +} diff --git a/crates/vcs_data/src/data/local/member_held.rs b/crates/vcs_data/src/data/local/member_held.rs new file mode 100644 index 0000000..e9e384d --- /dev/null +++ b/crates/vcs_data/src/data/local/member_held.rs @@ -0,0 +1,25 @@ +use std::collections::HashMap; + +use cfg_file::ConfigFile; +use serde::{Deserialize, Serialize}; + +use crate::{ + constants::CLIENT_FILE_MEMBER_HELD_NOSET, + data::{member::MemberId, vault::virtual_file::VirtualFileId}, +}; + +#[derive(Debug, Default, Clone, Serialize, Deserialize, ConfigFile)] +#[cfg_file(path = CLIENT_FILE_MEMBER_HELD_NOSET)] +pub struct MemberHeld { + /// File holding status + held_status: HashMap, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub enum HeldStatus { + HeldWith(MemberId), // Held, status changes are sync to the client + NotHeld, // Not held, status changes are sync to the client + + #[default] + WantedToKnow, // Holding status is unknown, notify server must inform client +} -- cgit