From 762e3119401fbee25ec18fee2ff220d9b12d48e8 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Wed, 11 Feb 2026 21:44:05 +0800 Subject: Add sheet system and reorganize workspace - Add new "sheet" crate to workspace - Rename "systems/asset" to "systems/_asset" - Reorder workspace members and dependencies for clarity - Update constants with new paths for auth and rules - Add conditional compilation for system module --- systems/asset/Cargo.toml | 15 -- systems/asset/macros/Cargo.toml | 13 - systems/asset/macros/src/lib.rs | 43 ---- systems/asset/src/asset.rs | 552 ---------------------------------------- systems/asset/src/error.rs | 106 -------- systems/asset/src/lib.rs | 6 - systems/asset/src/rw.rs | 85 ------- systems/asset/test/Cargo.toml | 9 - systems/asset/test/src/lib.rs | 53 ---- 9 files changed, 882 deletions(-) delete mode 100644 systems/asset/Cargo.toml delete mode 100644 systems/asset/macros/Cargo.toml delete mode 100644 systems/asset/macros/src/lib.rs delete mode 100644 systems/asset/src/asset.rs delete mode 100644 systems/asset/src/error.rs delete mode 100644 systems/asset/src/lib.rs delete mode 100644 systems/asset/src/rw.rs delete mode 100644 systems/asset/test/Cargo.toml delete mode 100644 systems/asset/test/src/lib.rs (limited to 'systems/asset') diff --git a/systems/asset/Cargo.toml b/systems/asset/Cargo.toml deleted file mode 100644 index 5e86bbd..0000000 --- a/systems/asset/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "asset_system" -edition = "2024" -version.workspace = true - -[dependencies] -string_proc = { path = "../../utils/string_proc" } - -asset_macros = { path = "macros" } -tokio = { version = "1.48.0", features = ["full"] } -thiserror = "1.0.69" - -winapi = { version = "0.3", features = ["winnt"] } - -constants = { path = "../_constants" } diff --git a/systems/asset/macros/Cargo.toml b/systems/asset/macros/Cargo.toml deleted file mode 100644 index d4142f2..0000000 --- a/systems/asset/macros/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "asset_macros" -version.workspace = true -edition = "2024" - -[lib] -proc-macro = true - -[dependencies] -string_proc = { path = "../../../utils/string_proc" } -syn = { version = "2.0", features = ["full", "extra-traits"] } -quote = "1.0" -proc-macro2 = "1.0" diff --git a/systems/asset/macros/src/lib.rs b/systems/asset/macros/src/lib.rs deleted file mode 100644 index 1535af0..0000000 --- a/systems/asset/macros/src/lib.rs +++ /dev/null @@ -1,43 +0,0 @@ -use proc_macro::TokenStream; -use quote::quote; -use string_proc::snake_case; -use syn::parse_macro_input; - -#[proc_macro_derive(RWDataTest)] -pub fn rw_data_test_derive(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as syn::DeriveInput); - let name = &input.ident; - let test_mod_name = syn::Ident::new( - &format!("{}_rw_test", snake_case!(name.to_string())), - name.span(), - ); - - let expanded = quote! { - #[cfg(test)] - mod #test_mod_name { - use super::*; - use asset_system::rw::RWData; - - #[tokio::test] - async fn test() { - let temp_dir = std::env::current_dir().unwrap().join(".temp/"); - tokio::fs::create_dir_all(&temp_dir) - .await - .expect("Create dir failed!"); - let temp_file = temp_dir.join(stringify!(#name)); - - #name::write(#name::test_data(), &temp_file) - .await - .expect("Write test data failed!"); - - let read_data = #name::read(&temp_file) - .await - .expect("Re-read data failed!"); - - assert!(#name::verify_data(#name::test_data(), read_data)); - } - } - }; - - TokenStream::from(expanded) -} diff --git a/systems/asset/src/asset.rs b/systems/asset/src/asset.rs deleted file mode 100644 index a3b1c6a..0000000 --- a/systems/asset/src/asset.rs +++ /dev/null @@ -1,552 +0,0 @@ -use std::{ - borrow::Cow, - collections::HashSet, - ffi::OsStr, - marker::PhantomData, - path::{Path, PathBuf}, -}; - -use constants::{LOCK_FILE_PREFIX, TEMP_FILE_PREFIX}; -use string_proc::format_path::format_path; - -use crate::{ - error::{DataApplyError, DataReadError, DataWriteError, HandleLockError, PrecheckFailed}, - rw::RWData, -}; - -pub struct ReadOnlyAsset -where - RWDataType: RWData, -{ - _data_type: PhantomData, - path: PathBuf, -} - -/// Nothing special, I'm just too lazy -macro_rules! asset_from { - (|$v:ident| $src:ty => $expr:expr) => { - impl From<$src> for ReadOnlyAsset - where - RWDataType: RWData, - { - fn from($v: $src) -> Self { - ReadOnlyAsset { - _data_type: PhantomData, - path: $expr, - } - } - } - }; -} - -asset_from!(|v| PathBuf => v); -asset_from!(|v| &Path => v.to_path_buf()); -asset_from!(|v| String => PathBuf::from(v)); -asset_from!(|v| &str => PathBuf::from(v)); - -impl AsRef for ReadOnlyAsset -where - RWDataType: RWData, -{ - fn as_ref(&self) -> &Path { - self.path.as_ref() - } -} - -impl From> for PathBuf -where - RWDataType: RWData, -{ - fn from(value: ReadOnlyAsset) -> Self { - value.path - } -} - -impl ReadOnlyAsset -where - RWDataType: RWData, -{ - /// Read asset content from `ReadOnlyAsset` - /// ```ignore - /// let sheet_asset: ReadOnlyAsset = "my_sheet.sheet".into(); - /// let sheet = sheet_asset.read().await.unwrap(); - /// ``` - pub async fn read(&self) -> Result { - RWDataType::read(&self.path).await - } - - /// Create a `Handle` from `ReadOnlyAsset` to exclusively edit this `ReadOnlyAsset` - /// ```ignore - /// let sheet_asset: ReadOnlyAsset = "my_sheet.sheet".into(); - /// let mut sheet_handle = sheet_asset.get_handle().await.unwrap(); - /// ``` - pub async fn get_handle(&self) -> Result, HandleLockError> { - let asset_path = &self.path; - - // If the asset file does not exist, cannot create an editable handle - if !asset_path.exists() { - return Err(HandleLockError::AssetFileNotFound(asset_path.clone())); - } - - // Get the lock path and temp path - let lock_path = self.get_lock_path()?; - let temp_path = self.get_temp_path()?; - - // Attempt to create the lock file; success means the lock does not exist and editing is allowed - match tokio::fs::OpenOptions::new() - .write(true) - .create_new(true) - .open(&lock_path) - .await - { - Ok(_) => { - return Ok(Handle { - _data_type: PhantomData, - writed: false, - asset_path: self.path.clone(), - lock_path, - temp_path, - }); - } - Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => { - return Err(HandleLockError::AssetLocked); - } - Err(e) => { - return Err(HandleLockError::IoError(e)); - } - } - } - - /// Get the lock file name for the current `ReadOnlyAsset` - /// ``` - /// # use asset::rw::FooData; - /// # use asset::asset::ReadOnlyAsset; - /// let foo_asset = ReadOnlyAsset::::from("my/foo.txt"); - /// let lock_path = foo_asset - /// .get_lock_path() - /// .unwrap(); - /// assert_eq!(lock_path.to_string_lossy(), "my/~foo.txt"); - /// ``` - pub fn get_lock_path(&self) -> Result { - // Get the file name - let Some(file_name) = self.path.file_name() else { - return Err(HandleLockError::ReadFileNameFailed); - }; - - let file_name_str = check_path(file_name)?; - - let mut edit_path = self.path.clone(); - edit_path.set_file_name(format!("{}{}", LOCK_FILE_PREFIX, file_name_str)); - - let Ok(edit_path) = format_path(edit_path) else { - return Err(HandleLockError::ParsePathFailed); - }; - - Ok(edit_path) - } - - /// Get the name of the temporary editing file for the current `ReadOnlyAsset` - /// ``` - /// # use asset::rw::FooData; - /// # use asset::asset::ReadOnlyAsset; - /// let foo_asset = ReadOnlyAsset::::from("my/foo.txt"); - /// let temp_path = foo_asset - /// .get_temp_path() - /// .unwrap(); - /// assert_eq!(temp_path.to_string_lossy(), "my/.tmp_foo.txt"); - /// ``` - pub fn get_temp_path(&self) -> Result { - // Get the file name - let Some(file_name) = self.path.file_name() else { - return Err(HandleLockError::ReadFileNameFailed); - }; - - let file_name_str = check_path(file_name)?; - - let mut temp_path = self.path.clone(); - temp_path.set_file_name(format!("{}{}", TEMP_FILE_PREFIX, file_name_str)); - - let Ok(edit_path) = format_path(temp_path) else { - return Err(HandleLockError::ParsePathFailed); - }; - - Ok(edit_path) - } -} - -pub struct Handle -where - RWDataType: RWData, -{ - _data_type: PhantomData, - writed: bool, - asset_path: PathBuf, - lock_path: PathBuf, - temp_path: PathBuf, -} - -impl Drop for Handle -where - RWDataType: RWData, -{ - fn drop(&mut self) { - if self.lock_path.exists() { - let _ = std::fs::remove_file(&self.lock_path); - } - } -} - -impl Handle -where - RWDataType: RWData, -{ - /// Write content into the `Handle`, this will not affect the associated `ReadOnlyAsset` - pub async fn write(&mut self, data: RWDataType) -> Result<(), DataWriteError> { - self.writed = true; - RWDataType::write(data, &self.temp_path).await - } - - /// Read the content of this `Handle`; - /// if the `Handle` has never been written to, read the content of the original `ReadOnlyAsset` - pub async fn read(&self) -> Result { - let path = if self.writed { - &self.temp_path - } else { - &self.asset_path - }; - RWDataType::read(path).await - } - - /// Atomically write the content of the `Handle` to the `ReadOnlyAsset` - pub async fn apply(self) -> Result<(), DataApplyError> { - let from = &self.temp_path; - let to = &self.asset_path; - - // Cannot perform Apply operation when the target file does not exist - // Reason: Cannot apply modifications to a non-existent file, even if editing was declared - if !to.exists() { - return Err(DataApplyError::AssetFileNotFound(to.clone())); - } - - if self.writed { - tokio::fs::rename(&from, &to) - .await - .map_err(|e| DataApplyError::IoError(e))?; - } - Ok(()) - } - - pub fn get_rename_operation(&self) -> (PathBuf, PathBuf) { - (self.temp_path.clone(), self.asset_path.clone()) - } -} - -/// Strict Apply precheck -/// -/// It checks: -/// 1. Whether related files exist -/// 2. If the Handle has been written to, whether the written content can be found -/// 3. Whether the Handle and its ReadOnlyAsset are in the same directory -/// 4. Whether the ReadOnlyAsset is locked (preventing writes/renames) -/// 5. Whether the Handle is locked (preventing writes/renames) -/// -/// > This is only necessary when you need to ensure `multiple Handles must either all succeed or all fail together` -/// -/// If you only need to ensure a single Handle can Apply correctly, -/// use: -/// ```ignore -/// // ... -/// let mut handle = my_asset.get_handle().await?; -/// handle.write(/* Your Data */).await?; -/// handle.apply().await?; -/// ``` -pub async fn apply_precheck(handle: &Handle) -> Result<(), PrecheckFailed> -where - D: RWData, -{ - let Ok(from) = format_path(&handle.temp_path) else { - return Err(PrecheckFailed::FormatPathFailed); - }; - let Ok(to) = format_path(&handle.asset_path) else { - return Err(PrecheckFailed::FormatPathFailed); - }; - - tokio::try_join!( - check_lock_file_exist(handle), - check_asset_file_exist(handle), - check_temp_file_exist_when_writed(handle), - check_asset_path(handle), - check_handle_is_cross_directory(&from, &to), - )?; - Ok(()) -} - -/// Check if all rename operations to be performed are valid -pub async fn apply_precheck_rename_operations( - rename_operations: &[(PathBuf, PathBuf)], -) -> Result<(), PrecheckFailed> { - let mut seen = HashSet::new(); - - for (from, to) in rename_operations { - if !seen.insert(from) { - return Err(PrecheckFailed::HasSamePath); - } - if !seen.insert(to) { - return Err(PrecheckFailed::HasSamePath); - } - } - - // This validation must be executed sequentially - // Because on Unix, we need to attempt writing files to directories - // If executed in parallel, conflicts may arise - - for (from, to) in rename_operations { - #[cfg(windows)] - check_temp_file_moveable_windows(from, to).await?; - - #[cfg(windows)] - check_asset_file_writeable_windows(to).await?; - - #[cfg(unix)] - check_temp_file_moveable_unix(from, to).await?; - - #[cfg(unix)] - check_asset_file_writeable_unix(to).await?; - } - Ok(()) -} - -async fn check_lock_file_exist(handle: &Handle) -> Result<(), PrecheckFailed> -where - D: RWData, -{ - if !handle.lock_path.exists() { - return Err(PrecheckFailed::LockNotFound); - } - Ok(()) -} - -async fn check_asset_file_exist(handle: &Handle) -> Result<(), PrecheckFailed> -where - D: RWData, -{ - if !handle.asset_path.exists() { - return Err(PrecheckFailed::AssetNotFound); - } - Ok(()) -} - -async fn check_temp_file_exist_when_writed(handle: &Handle) -> Result<(), PrecheckFailed> -where - D: RWData, -{ - if handle.writed && !handle.temp_path.exists() { - return Err(PrecheckFailed::WritedButTempNotFound); - } - Ok(()) -} - -async fn check_asset_path(handle: &Handle) -> Result<(), PrecheckFailed> -where - D: RWData, -{ - if let Some(file_name) = handle.asset_path.file_name() { - if check_path(file_name).is_ok() { - return Ok(()); - } - } - Err(PrecheckFailed::AssetPathInvalid) -} - -async fn check_handle_is_cross_directory( - from: &PathBuf, - to: &PathBuf, -) -> Result<(), PrecheckFailed> { - let from_parent = from.parent(); - let to_parent = to.parent(); - - // "Huh? You ask why we report an error when the parent directory doesn't exist?" - // - // Operations are not allowed in the root directory or when there is no parent directory. - // 1. This indicates they are not in the correct location - // (ideally there should be at least one parent directory). - // 2. This makes it impossible to compare the two directories. - if from_parent.is_none() || to_parent.is_none() { - return Err(PrecheckFailed::HandleFileIsNoParent); - } - - if from_parent.unwrap() != to_parent.unwrap() { - return Err(PrecheckFailed::HandleIsCrossDirectory); - } - - Ok(()) -} - -fn check_path(file_name: &OsStr) -> Result, PrecheckFailed> { - let file_name_str = file_name.to_string_lossy(); - - // When operating on a TEMP_FILE or LOCK_FILE, - // names like `~~foo.txt` or `.tmp_.tmp_foo.txt` would be generated - // This is not expected and should result in an error - - // Check if the file name starts with ~ - if file_name_str.starts_with(LOCK_FILE_PREFIX) { - return Err(PrecheckFailed::LockOnLockFile); - } - - // Check if the file name starts with .tmp_ - if file_name_str.starts_with(TEMP_FILE_PREFIX) { - return Err(PrecheckFailed::TempForTempFile); - } - - Ok(file_name_str) -} - -#[cfg(windows)] -async fn check_asset_file_writeable_windows(path: &Path) -> Result<(), PrecheckFailed> { - use std::os::windows::fs::MetadataExt; - - let metadata = tokio::fs::metadata(path) - .await - .map_err(|_| PrecheckFailed::AssetNotWritable)?; - - if metadata.file_attributes() & 0x1 != 0 { - return Err(PrecheckFailed::AssetNotWritable); - } - - if !check_file_can_be_deleted_windows(path) { - return Err(PrecheckFailed::AssetNotWritable); - } - - Ok(()) -} - -#[cfg(windows)] -async fn check_temp_file_moveable_windows(temp: &Path, asset: &Path) -> Result<(), PrecheckFailed> { - if !check_file_can_be_deleted_windows(temp) { - return Err(PrecheckFailed::TempNotMoveable); - } - if asset.exists() && !check_file_can_be_deleted_windows(asset) { - return Err(PrecheckFailed::TempNotMoveable); - } - - Ok(()) -} - -#[cfg(windows)] -fn check_file_can_be_deleted_windows(path: &Path) -> bool { - use std::os::windows::fs::OpenOptionsExt; - - use winapi::um::winnt::{DELETE, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE}; - std::fs::OpenOptions::new() - .access_mode(DELETE) - .share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) - .open(path) - .is_ok() -} - -#[cfg(unix)] -async fn check_asset_file_writeable_unix(path: &Path) -> Result<(), PrecheckFailed> { - let parent = path.parent().ok_or(PrecheckFailed::AssetPathInvalid)?; - - if !check_dir_writable_unix(parent) { - return Err(PrecheckFailed::AssetNotWritable); - } - - Ok(()) -} - -#[cfg(unix)] -async fn check_temp_file_moveable_unix(temp: &Path, asset: &Path) -> Result<(), PrecheckFailed> { - if !temp.exists() { - return Err(PrecheckFailed::TempNotMoveable); - } - - let temp_parent = temp.parent().ok_or(PrecheckFailed::TempNotMoveable)?; - if !check_dir_writable_unix(temp_parent) { - return Err(PrecheckFailed::TempNotMoveable); - } - - let asset_parent = asset.parent().ok_or(PrecheckFailed::TempNotMoveable)?; - if !check_dir_writable_unix(asset_parent) { - return Err(PrecheckFailed::TempNotMoveable); - } - - Ok(()) -} - -#[cfg(unix)] -fn check_dir_writable_unix(dir: &Path) -> bool { - use std::fs::OpenOptions; - - let test_path = dir.join(".write_test"); - - let result = OpenOptions::new() - .create_new(true) - .write(true) - .open(&test_path); - - match result { - Ok(_) => { - let _ = std::fs::remove_file(test_path); - true - } - Err(_) => false, - } -} - -#[macro_export] -macro_rules! apply { - // Single handle - ($handle:expr $(,)?) => {{ - async { - // Single-handle precheck - if let Err(e) = asset::asset::apply_precheck(&$handle).await { - return Err(asset::error::DataApplyError::PrecheckFailed(e)); - } - - // Direct apply - $handle.apply().await - } - }}; - - // Multiple handles - ($first:expr, $($rest:expr),+ $(,)?) => {{ - async { - // Collect rename ops - let rename_ops: Vec<(std::path::PathBuf, std::path::PathBuf)> = vec![ - $first.get_rename_operation(), - $( - $rest.get_rename_operation(), - )+ - ]; - - // Batch precheck - if let Err(e) = asset_system::asset::apply_precheck_rename_operations(&rename_ops).await { - return Err(asset_system::error::DataApplyError::PrecheckFailed(e)); - } - - // Per-handle precheck - if let Err(e) = asset_system::asset::apply_precheck(&$first).await { - return Err(asset_system::error::DataApplyError::PrecheckFailed(e)); - } - $( - if let Err(e) = asset_system::asset::apply_precheck(&$rest).await { - return Err(asset_system::error::DataApplyError::PrecheckFailed(e)); - } - )+ - - // Apply - if let Err(e) = $first.apply().await { - return Err(e); - } - $( - if let Err(e) = $rest.apply().await { - return Err(e); - } - )+ - - Ok(()) - } - }}; -} diff --git a/systems/asset/src/error.rs b/systems/asset/src/error.rs deleted file mode 100644 index 5dc529f..0000000 --- a/systems/asset/src/error.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::path::PathBuf; - -#[derive(Debug, thiserror::Error)] -pub enum DataReadError { - #[error("IO error: {0}")] - IoError(#[from] std::io::Error), - - #[error("Parse error: {0}")] - ParseError(String), -} - -#[derive(Debug, thiserror::Error)] -pub enum DataWriteError { - #[error("IO error: {0}")] - IoError(#[from] std::io::Error), -} - -#[derive(Debug, thiserror::Error)] -pub enum DataApplyError { - #[error("IO error: {0}")] - IoError(#[from] std::io::Error), - - #[error("Asset file not found: {0}")] - AssetFileNotFound(PathBuf), - - #[error("Pre-check failed: {0}")] - PrecheckFailed(#[from] PrecheckFailed), -} - -#[derive(Debug, thiserror::Error)] -pub enum PrecheckFailed { - /// Lock file does not exist - /// Means trying to apply a modification that cannot be applied - #[error("Lock not found")] - LockNotFound, - - /// Asset file does not exist - /// Apply phase will fail due to this condition - #[error("Asset not found")] - AssetNotFound, - - // Note: !writed is allowed, - // but writed without a TEMP_FILE is not allowed - /// Handle produced a write - /// but the temporary file does not exist, indicating an abnormal write operation - #[error("Temp not found")] - WritedButTempNotFound, - - /// Asset path is invalid - /// This is not an issue that should arise from normal Handle creation flow - #[error("Asset path invalid")] - AssetPathInvalid, - - /// Temporary file cannot be moved - #[error("Temp file not moveable")] - TempNotMoveable, - - /// Asset file cannot be moved - #[error("Asset file not writable")] - AssetNotWritable, - - /// Handle is processing a cross-directory operation - /// This is not atomic - #[error("Handle is cross-directory")] - HandleIsCrossDirectory, - - /// Asset file has no parent directory - /// This is not a valid path for file operations - #[error("Asset file has no parent directory")] - HandleFileIsNoParent, - - /// A handle with the same path already exists - /// This operation will cause a conflict - #[error("Handle with same path exists")] - HasSamePath, - - #[error("Lock on lock file")] - LockOnLockFile, - - #[error("Temp file for temp file")] - TempForTempFile, - - #[error("Asset path cannot be formatted")] - FormatPathFailed, -} - -#[derive(Debug, thiserror::Error)] -pub enum HandleLockError { - #[error("Parse path failed")] - ParsePathFailed, - - #[error("Asset file not found")] - AssetFileNotFound(PathBuf), - - #[error("Read file name failed")] - ReadFileNameFailed, - - #[error("Asset locked")] - AssetLocked, - - #[error("IO error: {0}")] - IoError(#[from] std::io::Error), - - #[error("Pre-check failed: {0}")] - PrecheckFailed(#[from] PrecheckFailed), -} diff --git a/systems/asset/src/lib.rs b/systems/asset/src/lib.rs deleted file mode 100644 index ac4317f..0000000 --- a/systems/asset/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod asset; -pub mod error; -pub mod rw; - -#[allow(unused)] -pub use asset_macros::*; diff --git a/systems/asset/src/rw.rs b/systems/asset/src/rw.rs deleted file mode 100644 index 784d44d..0000000 --- a/systems/asset/src/rw.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::path::PathBuf; - -use crate::error::{DataReadError, DataWriteError}; - -pub trait RWData { - type DataType; - - /// Implement read logic - /// Given a path, return the specific data - fn read(path: &PathBuf) -> impl Future> + Send + Sync; - - /// Implement write logic - /// Given data and a path, write to the filesystem - fn write( - data: DataType, - path: &PathBuf, - ) -> impl Future> + Send + Sync; - - /// Provide test data - fn test_data() -> DataType; - - /// Given two sets of data, determine if they are equal - /// - /// Add RWDataTest derive to your struct to automatically generate tests - /// ```ignore - /// #[derive(RWDataTest)] - /// struct FooData; - /// ``` - fn verify_data(data_a: DataType, data_b: DataType) -> bool; -} - -#[macro_export] -macro_rules! ensure_eq { - ($a:expr, $b:expr) => { - if $a != $b { - return false; - } - }; -} - -// Test Data -pub struct FooData { - pub age: i32, - pub name: String, -} - -impl RWData for FooData { - type DataType = FooData; - - async fn read(path: &PathBuf) -> Result { - let content = tokio::fs::read_to_string(path) - .await - .map_err(|e| DataReadError::IoError(e))?; - let parts: Vec<&str> = content.split('=').collect(); - if parts.len() != 2 { - return Err(DataReadError::ParseError("Invalid format".to_string())); - } - let name = parts[0].to_string(); - let age: i32 = parts[1] - .parse() - .map_err(|_| DataReadError::ParseError("Invalid age".to_string()))?; - Ok(FooData { age, name }) - } - - async fn write(data: FooData, path: &PathBuf) -> Result<(), DataWriteError> { - let content = format!("{}={}", data.name, data.age); - tokio::fs::write(path, content) - .await - .map_err(|e| DataWriteError::IoError(e))?; - Ok(()) - } - - fn test_data() -> FooData { - FooData { - age: 24, - name: "OneOneFourFiveOneFour".to_string(), - } - } - - fn verify_data(data_a: FooData, data_b: FooData) -> bool { - crate::ensure_eq!(data_a.age, data_b.age); - crate::ensure_eq!(data_a.name, data_b.name); - return true; - } -} diff --git a/systems/asset/test/Cargo.toml b/systems/asset/test/Cargo.toml deleted file mode 100644 index 3ac0779..0000000 --- a/systems/asset/test/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "asset_test" -version.workspace = true -edition = "2024" - -[dependencies] -asset_system = { path = "../" } - -tokio = { version = "1.48.0", features = ["full"] } diff --git a/systems/asset/test/src/lib.rs b/systems/asset/test/src/lib.rs deleted file mode 100644 index 4aad777..0000000 --- a/systems/asset/test/src/lib.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::path::PathBuf; - -use asset_system::{ - RWDataTest, ensure_eq, - error::{DataReadError, DataWriteError}, - rw::RWData, -}; - -#[derive(RWDataTest)] -pub struct FooData { - pub age: i32, - pub name: String, -} - -impl RWData for FooData { - type DataType = FooData; - - async fn read(path: &PathBuf) -> Result { - let content = tokio::fs::read_to_string(path) - .await - .map_err(|e| DataReadError::IoError(e))?; - let parts: Vec<&str> = content.split('=').collect(); - if parts.len() != 2 { - return Err(DataReadError::ParseError("Invalid format".to_string())); - } - let name = parts[0].to_string(); - let age: i32 = parts[1] - .parse() - .map_err(|_| DataReadError::ParseError("Invalid age".to_string()))?; - Ok(FooData { age, name }) - } - - async fn write(data: FooData, path: &PathBuf) -> Result<(), DataWriteError> { - let content = format!("{}={}", data.name, data.age); - tokio::fs::write(path, content) - .await - .map_err(|e| DataWriteError::IoError(e))?; - Ok(()) - } - - fn test_data() -> FooData { - FooData { - age: 24, - name: "OneOneFourFiveOneFour".to_string(), - } - } - - fn verify_data(data_a: FooData, data_b: FooData) -> bool { - ensure_eq!(data_a.age, data_b.age); - ensure_eq!(data_a.name, data_b.name); - return true; - } -} -- cgit