summaryrefslogtreecommitdiff
path: root/legacy_data/src/data/local/local_sheet.rs
diff options
context:
space:
mode:
Diffstat (limited to 'legacy_data/src/data/local/local_sheet.rs')
-rw-r--r--legacy_data/src/data/local/local_sheet.rs439
1 files changed, 439 insertions, 0 deletions
diff --git a/legacy_data/src/data/local/local_sheet.rs b/legacy_data/src/data/local/local_sheet.rs
new file mode 100644
index 0000000..b9c29f5
--- /dev/null
+++ b/legacy_data/src/data/local/local_sheet.rs
@@ -0,0 +1,439 @@
+use std::{collections::HashMap, io::Error, path::PathBuf, time::SystemTime};
+
+use ::serde::{Deserialize, Serialize};
+use cfg_file::{ConfigFile, config::ConfigFile};
+use string_proc::format_path::format_path;
+
+use crate::{
+ constants::CLIENT_FILE_LOCAL_SHEET_NOSET,
+ data::{
+ local::LocalWorkspace,
+ member::MemberId,
+ 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,
+/// to compare with upstream information for more optimized file submission,
+/// and to determine whether files need to be updated or submitted.
+pub struct LocalSheet<'a> {
+ pub(crate) local_workspace: &'a LocalWorkspace,
+ pub(crate) member: MemberId,
+ pub(crate) sheet_name: String,
+ pub(crate) data: LocalSheetData,
+}
+
+impl<'a> LocalSheet<'a> {
+ /// Create a new LocalSheet instance
+ pub fn new(
+ local_workspace: &'a LocalWorkspace,
+ member: MemberId,
+ sheet_name: String,
+ data: LocalSheetData,
+ ) -> Self {
+ Self {
+ local_workspace,
+ member,
+ sheet_name,
+ data,
+ }
+ }
+}
+
+#[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 metadata mapping.
+ #[serde(rename = "map")]
+ pub(crate) mapping: HashMap<LocalFilePathBuf, LocalMappingMetadata>,
+
+ #[serde(rename = "vfs")]
+ pub(crate) vfs: HashMap<VirtualFileId, LocalFilePathBuf>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct LocalMappingMetadata {
+ /// Hash value generated immediately after the file is downloaded to the local workspace
+ #[serde(rename = "base_hash")]
+ pub(crate) hash_when_updated: String,
+
+ /// Time when the file was downloaded to the local workspace
+ #[serde(rename = "time")]
+ 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 = "desc")]
+ pub(crate) version_desc_when_updated: VirtualFileVersionDescription,
+
+ /// Version when the file was downloaded to the local workspace
+ #[serde(rename = "ver")]
+ 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
+ #[serde(rename = "check_time")]
+ pub(crate) last_modify_check_time: SystemTime,
+
+ /// Latest modifiy check result
+ #[serde(rename = "modified")]
+ pub(crate) last_modify_check_result: bool,
+
+ /// Latest modifiy check hash result
+ #[serde(rename = "current_hash")]
+ pub(crate) last_modify_check_hash: Option<String>,
+}
+
+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
+ #[allow(clippy::too_many_arguments)]
+ 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_modify_check_time: last_modifiy_check_time,
+ last_modify_check_result: last_modifiy_check_result,
+ last_modify_check_hash: None,
+ }
+ }
+
+ /// Getter for hash_when_updated
+ pub fn hash_when_updated(&self) -> &String {
+ &self.hash_when_updated
+ }
+
+ /// Setter for hash_when_updated
+ pub fn set_hash_when_updated(&mut self, hash: String) {
+ self.hash_when_updated = hash;
+ }
+
+ /// Getter for date_when_updated
+ pub fn time_when_updated(&self) -> &SystemTime {
+ &self.time_when_updated
+ }
+
+ /// Setter for time_when_updated
+ pub fn set_time_when_updated(&mut self, time: SystemTime) {
+ self.time_when_updated = time;
+ }
+
+ /// Getter for size_when_updated
+ pub fn size_when_updated(&self) -> u64 {
+ self.size_when_updated
+ }
+
+ /// Setter for size_when_updated
+ pub fn set_size_when_updated(&mut self, size: u64) {
+ self.size_when_updated = size;
+ }
+
+ /// Getter for version_desc_when_updated
+ pub fn version_desc_when_updated(&self) -> &VirtualFileVersionDescription {
+ &self.version_desc_when_updated
+ }
+
+ /// Setter for version_desc_when_updated
+ pub fn set_version_desc_when_updated(&mut self, version_desc: VirtualFileVersionDescription) {
+ self.version_desc_when_updated = version_desc;
+ }
+
+ /// Getter for version_when_updated
+ pub fn version_when_updated(&self) -> &VirtualFileVersion {
+ &self.version_when_updated
+ }
+
+ /// Setter for version_when_updated
+ pub fn set_version_when_updated(&mut self, version: VirtualFileVersion) {
+ self.version_when_updated = version;
+ }
+
+ /// Getter for mapping_vfid
+ pub fn mapping_vfid(&self) -> &VirtualFileId {
+ &self.mapping_vfid
+ }
+
+ /// Setter for mapping_vfid
+ pub fn set_mapping_vfid(&mut self, vfid: VirtualFileId) {
+ self.mapping_vfid = vfid;
+ }
+
+ /// Getter for last_modifiy_check_time
+ pub fn last_modifiy_check_time(&self) -> &SystemTime {
+ &self.last_modify_check_time
+ }
+
+ /// Setter for last_modifiy_check_time
+ pub fn set_last_modifiy_check_time(&mut self, time: SystemTime) {
+ self.last_modify_check_time = time;
+ }
+
+ /// Getter for last_modifiy_check_result
+ pub fn last_modifiy_check_result(&self) -> bool {
+ self.last_modify_check_result
+ }
+
+ /// Setter for last_modifiy_check_result
+ pub fn set_last_modifiy_check_result(&mut self, result: bool) {
+ self.last_modify_check_result = result;
+ }
+
+ /// Getter for last_modifiy_check_hash
+ pub fn last_modifiy_check_hash(&self) -> &Option<String> {
+ &self.last_modify_check_hash
+ }
+
+ /// Setter for last_modifiy_check_hash
+ pub fn set_last_modifiy_check_hash(&mut self, hash: Option<String>) {
+ self.last_modify_check_hash = hash;
+ }
+}
+
+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_modify_check_time: SystemTime::now(),
+ last_modify_check_result: false,
+ last_modify_check_hash: None,
+ }
+ }
+}
+
+mod instant_serde {
+ use serde::{self, Deserialize, Deserializer, Serializer};
+ use tokio::time::Instant;
+
+ pub fn serialize<S>(instant: &Instant, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_u64(instant.elapsed().as_secs())
+ }
+
+ pub fn deserialize<'de, D>(deserializer: D) -> Result<Instant, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ 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
+ }
+}
+
+impl LocalSheetData {
+ /// Add mapping to local sheet data
+ pub fn add_mapping(
+ &mut self,
+ path: &LocalFilePathBuf,
+ mapping: LocalMappingMetadata,
+ ) -> Result<(), std::io::Error> {
+ let path = format_path(path)?;
+ if self.mapping.contains_key(&path) || self.vfs.contains_key(&mapping.mapping_vfid) {
+ return Err(Error::new(
+ std::io::ErrorKind::AlreadyExists,
+ "Mapping already exists",
+ ));
+ }
+
+ self.mapping.insert(path.clone(), mapping.clone());
+ self.vfs.insert(mapping.mapping_vfid.clone(), path);
+ Ok(())
+ }
+
+ /// Move mapping to other path
+ pub fn move_mapping(
+ &mut self,
+ from: &LocalFilePathBuf,
+ to: &LocalFilePathBuf,
+ ) -> Result<(), std::io::Error> {
+ let from = format_path(from)?;
+ let to = format_path(to)?;
+ if self.mapping.contains_key(&to) {
+ return Err(Error::new(
+ std::io::ErrorKind::AlreadyExists,
+ "To path already exists.",
+ ));
+ }
+
+ let Some(old_value) = self.mapping.remove(&from) else {
+ return Err(Error::new(
+ std::io::ErrorKind::NotFound,
+ "From path is not found.",
+ ));
+ };
+
+ // Update vfs mapping
+ self.vfs.insert(old_value.mapping_vfid.clone(), to.clone());
+ self.mapping.insert(to, old_value);
+
+ Ok(())
+ }
+
+ /// Remove mapping from local sheet
+ pub fn remove_mapping(
+ &mut self,
+ path: &LocalFilePathBuf,
+ ) -> Result<LocalMappingMetadata, std::io::Error> {
+ let path = format_path(path)?;
+ match self.mapping.remove(&path) {
+ Some(mapping) => {
+ self.vfs.remove(&mapping.mapping_vfid);
+ Ok(mapping)
+ }
+ None => Err(Error::new(
+ std::io::ErrorKind::NotFound,
+ "Path is not found.",
+ )),
+ }
+ }
+
+ /// 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.mapping.get(&path) else {
+ return Err(Error::new(
+ std::io::ErrorKind::NotFound,
+ "Path is not found.",
+ ));
+ };
+ Ok(data)
+ }
+
+ /// Get mutable mapping data
+ pub fn mapping_data_mut(
+ &mut self,
+ path: &LocalFilePathBuf,
+ ) -> Result<&mut LocalMappingMetadata, std::io::Error> {
+ let path = format_path(path)?;
+ let Some(data) = self.mapping.get_mut(&path) else {
+ return Err(Error::new(
+ std::io::ErrorKind::NotFound,
+ "Path is not found.",
+ ));
+ };
+ Ok(data)
+ }
+
+ /// Get path by VirtualFileId
+ pub fn path_by_id(&self, vfid: &VirtualFileId) -> Option<&PathBuf> {
+ self.vfs.get(vfid)
+ }
+}
+
+impl<'a> LocalSheet<'a> {
+ /// Add mapping to local sheet data
+ pub fn add_mapping(
+ &mut self,
+ path: &LocalFilePathBuf,
+ mapping: LocalMappingMetadata,
+ ) -> Result<(), std::io::Error> {
+ self.data.add_mapping(path, mapping)
+ }
+
+ /// Move mapping to other path
+ pub fn move_mapping(
+ &mut self,
+ from: &LocalFilePathBuf,
+ to: &LocalFilePathBuf,
+ ) -> Result<(), std::io::Error> {
+ self.data.move_mapping(from, to)
+ }
+
+ /// Remove mapping from local sheet
+ pub fn remove_mapping(
+ &mut self,
+ path: &LocalFilePathBuf,
+ ) -> Result<LocalMappingMetadata, std::io::Error> {
+ self.data.remove_mapping(path)
+ }
+
+ /// Get immutable mapping data
+ pub fn mapping_data(
+ &self,
+ path: &LocalFilePathBuf,
+ ) -> Result<&LocalMappingMetadata, std::io::Error> {
+ self.data.mapping_data(path)
+ }
+
+ /// Get mutable mapping data
+ pub fn mapping_data_mut(
+ &mut self,
+ path: &LocalFilePathBuf,
+ ) -> Result<&mut LocalMappingMetadata, std::io::Error> {
+ self.data.mapping_data_mut(path)
+ }
+
+ /// 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);
+ self.write_to_path(path).await
+ }
+
+ /// Write the sheet to custom path
+ pub async fn write_to_path(&mut self, path: impl Into<PathBuf>) -> Result<(), std::io::Error> {
+ let path = path.into();
+ LocalSheetData::write_to(&self.data, path).await?;
+ Ok(())
+ }
+
+ /// Get path by VirtualFileId
+ pub fn path_by_id(&self, vfid: &VirtualFileId) -> Option<&PathBuf> {
+ self.data.path_by_id(vfid)
+ }
+}