From 8b38e22525adcafecd8b50daf041a1dc2d672d0b Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Wed, 11 Mar 2026 15:34:47 +0800 Subject: Replace cfg_file with new config system --- utils/cfg_file/Cargo.toml | 23 --- utils/cfg_file/cfg_file_derive/Cargo.toml | 11 -- utils/cfg_file/cfg_file_derive/src/lib.rs | 130 --------------- utils/cfg_file/cfg_file_test/Cargo.toml | 9 - utils/cfg_file/cfg_file_test/src/lib.rs | 95 ----------- utils/cfg_file/src/config.rs | 263 ------------------------------ utils/cfg_file/src/lib.rs | 7 - 7 files changed, 538 deletions(-) delete mode 100644 utils/cfg_file/Cargo.toml delete mode 100644 utils/cfg_file/cfg_file_derive/Cargo.toml delete mode 100644 utils/cfg_file/cfg_file_derive/src/lib.rs delete mode 100644 utils/cfg_file/cfg_file_test/Cargo.toml delete mode 100644 utils/cfg_file/cfg_file_test/src/lib.rs delete mode 100644 utils/cfg_file/src/config.rs delete mode 100644 utils/cfg_file/src/lib.rs (limited to 'utils') diff --git a/utils/cfg_file/Cargo.toml b/utils/cfg_file/Cargo.toml deleted file mode 100644 index 0685329..0000000 --- a/utils/cfg_file/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "cfg_file" -edition = "2024" -version.workspace = true - -[features] -default = ["derive"] -derive = [] - -[dependencies] -cfg_file_derive = { path = "cfg_file_derive" } - -# Async -tokio = { version = "1.48.0", features = ["full"] } -async-trait = "0.1.89" - -# Serialization -serde = { version = "1.0.228", features = ["derive"] } -serde_yaml = "0.9.34" -serde_json = "1.0.145" -ron = "0.11.0" -toml = "0.9.8" -bincode2 = "2.0.1" diff --git a/utils/cfg_file/cfg_file_derive/Cargo.toml b/utils/cfg_file/cfg_file_derive/Cargo.toml deleted file mode 100644 index ce5e77f..0000000 --- a/utils/cfg_file/cfg_file_derive/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "cfg_file_derive" -edition = "2024" -version.workspace = true - -[lib] -proc-macro = true - -[dependencies] -syn = { version = "2.0", features = ["full", "extra-traits"] } -quote = "1.0" diff --git a/utils/cfg_file/cfg_file_derive/src/lib.rs b/utils/cfg_file/cfg_file_derive/src/lib.rs deleted file mode 100644 index e916311..0000000 --- a/utils/cfg_file/cfg_file_derive/src/lib.rs +++ /dev/null @@ -1,130 +0,0 @@ -extern crate proc_macro; - -use proc_macro::TokenStream; -use quote::quote; -use syn::parse::ParseStream; -use syn::{Attribute, DeriveInput, Expr, parse_macro_input}; -/// # Macro - ConfigFile -/// -/// ## Usage -/// -/// Use `#[derive(ConfigFile)]` to derive the ConfigFile trait for a struct -/// -/// Specify the default storage path via `#[cfg_file(path = "...")]` -/// -/// ## About the `cfg_file` attribute macro -/// -/// Use `#[cfg_file(path = "string")]` to specify the configuration file path -/// -/// Or use `#[cfg_file(path = constant_expression)]` to specify the configuration file path -/// -/// ## Path Rules -/// -/// Paths starting with `"./"`: relative to the current working directory -/// -/// Other paths: treated as absolute paths -/// -/// When no path is specified: use the struct name + ".json" as the default filename (e.g., `my_struct.json`) -/// -/// ## Example -/// ```ignore -/// #[derive(ConfigFile)] -/// #[cfg_file(path = "./config.json")] -/// struct AppConfig; -/// ``` -#[proc_macro_derive(ConfigFile, attributes(cfg_file))] -pub fn derive_config_file(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - let name = &input.ident; - - // Process 'cfg_file' - let path_expr = match find_cfg_file_path(&input.attrs) { - Some(PathExpr::StringLiteral(path)) => { - if let Some(path_str) = path.strip_prefix("./") { - quote! { - std::env::current_dir()?.join(#path_str) - } - } else { - // Using Absolute Path - quote! { - std::path::PathBuf::from(#path) - } - } - } - Some(PathExpr::PathExpression(path_expr)) => { - // For path expressions (constants), generate code that references the constant - quote! { - std::path::PathBuf::from(#path_expr) - } - } - None => { - let default_file = to_snake_case(&name.to_string()) + ".json"; - quote! { - std::env::current_dir()?.join(#default_file) - } - } - }; - - let expanded = quote! { - impl cfg_file::config::ConfigFile for #name { - type DataType = #name; - - fn default_path() -> Result { - Ok(#path_expr) - } - } - }; - - TokenStream::from(expanded) -} - -enum PathExpr { - StringLiteral(String), - PathExpression(syn::Expr), -} - -fn find_cfg_file_path(attrs: &[Attribute]) -> Option { - for attr in attrs { - if attr.path().is_ident("cfg_file") { - let parser = |meta: ParseStream| { - let path_meta: syn::MetaNameValue = meta.parse()?; - if path_meta.path.is_ident("path") { - match &path_meta.value { - // String literal case: path = "./vault.toml" - Expr::Lit(expr_lit) if matches!(expr_lit.lit, syn::Lit::Str(_)) => { - if let syn::Lit::Str(lit_str) = &expr_lit.lit { - return Ok(PathExpr::StringLiteral(lit_str.value())); - } - } - // Path expression case: path = SERVER_FILE_VAULT or crate::constants::SERVER_FILE_VAULT - expr @ (Expr::Path(_) | Expr::Macro(_)) => { - return Ok(PathExpr::PathExpression(expr.clone())); - } - _ => {} - } - } - Err(meta.error("expected `path = \"...\"` or `path = CONSTANT`")) - }; - - if let Ok(path_expr) = attr.parse_args_with(parser) { - return Some(path_expr); - } - } - } - None -} - -fn to_snake_case(s: &str) -> String { - let mut snake = String::new(); - for (i, c) in s.chars().enumerate() { - if c.is_uppercase() { - if i != 0 { - snake.push('_'); - } - snake.push(c.to_ascii_lowercase()); - } else { - snake.push(c); - } - } - snake -} diff --git a/utils/cfg_file/cfg_file_test/Cargo.toml b/utils/cfg_file/cfg_file_test/Cargo.toml deleted file mode 100644 index 5db1010..0000000 --- a/utils/cfg_file/cfg_file_test/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "cfg_file_test" -version = "0.1.0" -edition = "2024" - -[dependencies] -cfg_file = { path = "../../cfg_file", features = ["default"] } -tokio = { version = "1.48.0", features = ["full"] } -serde = { version = "1.0.228", features = ["derive"] } diff --git a/utils/cfg_file/cfg_file_test/src/lib.rs b/utils/cfg_file/cfg_file_test/src/lib.rs deleted file mode 100644 index f70d00d..0000000 --- a/utils/cfg_file/cfg_file_test/src/lib.rs +++ /dev/null @@ -1,95 +0,0 @@ -#[cfg(test)] -mod test_cfg_file { - use cfg_file::ConfigFile; - use cfg_file::config::ConfigFile; - use serde::{Deserialize, Serialize}; - use std::collections::HashMap; - - #[derive(ConfigFile, Deserialize, Serialize, Default)] - #[cfg_file(path = "./.temp/example_cfg.toml")] - struct ExampleConfig { - name: String, - age: i32, - hobby: Vec, - secret: HashMap, - } - - #[derive(ConfigFile, Deserialize, Serialize, Default)] - #[cfg_file(path = "./.temp/example_bincode.bcfg")] - struct ExampleBincodeConfig { - name: String, - age: i32, - hobby: Vec, - secret: HashMap, - } - - #[tokio::test] - async fn test_config_file_serialization() { - let mut example = ExampleConfig { - name: "Weicao".to_string(), - age: 22, - hobby: ["Programming", "Painting"] - .iter() - .map(|m| m.to_string()) - .collect(), - secret: HashMap::new(), - }; - let secret_no_comments = - "Actually, I'm really too lazy to write comments, documentation, and unit tests."; - example - .secret - .entry("No comments".to_string()) - .insert_entry(secret_no_comments.to_string()); - - let secret_peek = "Of course, it's peeking at you who's reading the source code."; - example - .secret - .entry("Peek".to_string()) - .insert_entry(secret_peek.to_string()); - - ExampleConfig::write(&example).await.unwrap(); // Write to default path. - - // Read from default path. - let read_cfg = ExampleConfig::read().await.unwrap(); - assert_eq!(read_cfg.name, "Weicao"); - assert_eq!(read_cfg.age, 22); - assert_eq!(read_cfg.hobby, vec!["Programming", "Painting"]); - assert_eq!(read_cfg.secret["No comments"], secret_no_comments); - assert_eq!(read_cfg.secret["Peek"], secret_peek); - } - - #[tokio::test] - async fn test_bincode_config_file_serialization() { - let mut example = ExampleBincodeConfig { - name: "Weicao".to_string(), - age: 22, - hobby: ["Programming", "Painting"] - .iter() - .map(|m| m.to_string()) - .collect(), - secret: HashMap::new(), - }; - let secret_no_comments = - "Actually, I'm really too lazy to write comments, documentation, and unit tests."; - example - .secret - .entry("No comments".to_string()) - .insert_entry(secret_no_comments.to_string()); - - let secret_peek = "Of course, it's peeking at you who's reading the source code."; - example - .secret - .entry("Peek".to_string()) - .insert_entry(secret_peek.to_string()); - - ExampleBincodeConfig::write(&example).await.unwrap(); // Write to default path. - - // Read from default path. - let read_cfg = ExampleBincodeConfig::read().await.unwrap(); - assert_eq!(read_cfg.name, "Weicao"); - assert_eq!(read_cfg.age, 22); - assert_eq!(read_cfg.hobby, vec!["Programming", "Painting"]); - assert_eq!(read_cfg.secret["No comments"], secret_no_comments); - assert_eq!(read_cfg.secret["Peek"], secret_peek); - } -} diff --git a/utils/cfg_file/src/config.rs b/utils/cfg_file/src/config.rs deleted file mode 100644 index d3f5477..0000000 --- a/utils/cfg_file/src/config.rs +++ /dev/null @@ -1,263 +0,0 @@ -use async_trait::async_trait; -use bincode2; -use ron; -use serde::{Deserialize, Serialize}; -use std::{ - borrow::Cow, - env::current_dir, - io::Error, - path::{Path, PathBuf}, -}; -use tokio::{fs, io::AsyncReadExt}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum ConfigFormat { - Yaml, - Toml, - Ron, - Json, - Bincode, -} - -impl ConfigFormat { - fn from_filename(filename: &str) -> Option { - if filename.ends_with(".yaml") || filename.ends_with(".yml") { - Some(Self::Yaml) - } else if filename.ends_with(".toml") || filename.ends_with(".tom") { - Some(Self::Toml) - } else if filename.ends_with(".ron") { - Some(Self::Ron) - } else if filename.ends_with(".json") { - Some(Self::Json) - } else if filename.ends_with(".bcfg") { - Some(Self::Bincode) - } else { - None - } - } -} - -/// # Trait - ConfigFile -/// -/// Used to implement more convenient persistent storage functionality for structs -/// -/// This trait requires the struct to implement Default and serde's Serialize and Deserialize traits -/// -/// ## Implementation -/// -/// ```ignore -/// // Your struct -/// #[derive(Default, Serialize, Deserialize)] -/// struct YourData; -/// -/// impl ConfigFile for YourData { -/// type DataType = YourData; -/// -/// // Specify default path -/// fn default_path() -> Result { -/// Ok(current_dir()?.join("data.json")) -/// } -/// } -/// ``` -/// -/// > **Using derive macro** -/// > -/// > We provide the derive macro `#[derive(ConfigFile)]` -/// > -/// > You can implement this trait more quickly, please check the module cfg_file::cfg_file_derive -/// -#[async_trait] -pub trait ConfigFile: Serialize + for<'a> Deserialize<'a> + Default { - type DataType: Serialize + for<'a> Deserialize<'a> + Default + Send + Sync; - - fn default_path() -> Result; - - /// # Read from default path - /// - /// Read data from the path specified by default_path() - /// - /// ```ignore - /// fn main() -> Result<(), std::io::Error> { - /// let data = YourData::read().await?; - /// } - /// ``` - async fn read() -> Result - where - Self: Sized + Send + Sync, - { - let path = Self::default_path()?; - Self::read_from(path).await - } - - /// # Read from the given path - /// - /// Read data from the path specified by the path parameter - /// - /// ```ignore - /// fn main() -> Result<(), std::io::Error> { - /// let data_path = current_dir()?.join("data.json"); - /// let data = YourData::read_from(data_path).await?; - /// } - /// ``` - async fn read_from(path: impl AsRef + Send) -> Result - where - Self: Sized + Send + Sync, - { - let path = path.as_ref(); - let cwd = current_dir()?; - let file_path = cwd.join(path); - - // Check if file exists - if fs::metadata(&file_path).await.is_err() { - return Err(std::io::Error::new( - std::io::ErrorKind::NotFound, - "Config file not found", - )); - } - - // Determine file format first - let format = file_path - .file_name() - .and_then(|name| name.to_str()) - .and_then(ConfigFormat::from_filename) - .unwrap_or(ConfigFormat::Bincode); // Default to Bincode - - // Deserialize based on format - let result = match format { - ConfigFormat::Yaml => { - let mut file = fs::File::open(&file_path).await?; - let mut contents = String::new(); - file.read_to_string(&mut contents).await?; - serde_yaml::from_str(&contents) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))? - } - ConfigFormat::Toml => { - let mut file = fs::File::open(&file_path).await?; - let mut contents = String::new(); - file.read_to_string(&mut contents).await?; - toml::from_str(&contents) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))? - } - ConfigFormat::Ron => { - let mut file = fs::File::open(&file_path).await?; - let mut contents = String::new(); - file.read_to_string(&mut contents).await?; - ron::from_str(&contents) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))? - } - ConfigFormat::Json => { - let mut file = fs::File::open(&file_path).await?; - let mut contents = String::new(); - file.read_to_string(&mut contents).await?; - serde_json::from_str(&contents) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))? - } - ConfigFormat::Bincode => { - // For Bincode, we need to read the file as bytes directly - let bytes = fs::read(&file_path).await?; - bincode2::deserialize(&bytes) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))? - } - }; - - Ok(result) - } - - /// # Write to default path - /// - /// Write data to the path specified by default_path() - /// - /// ```ignore - /// fn main() -> Result<(), std::io::Error> { - /// let data = YourData::default(); - /// YourData::write(&data).await?; - /// } - /// ``` - async fn write(val: &Self::DataType) -> Result<(), std::io::Error> - where - Self: Sized + Send + Sync, - { - let path = Self::default_path()?; - Self::write_to(val, path).await - } - /// # Write to the given path - /// - /// Write data to the path specified by the path parameter - /// - /// ```ignore - /// fn main() -> Result<(), std::io::Error> { - /// let data = YourData::default(); - /// let data_path = current_dir()?.join("data.json"); - /// YourData::write_to(&data, data_path).await?; - /// } - /// ``` - async fn write_to( - val: &Self::DataType, - path: impl AsRef + Send, - ) -> Result<(), std::io::Error> - where - Self: Sized + Send + Sync, - { - let path = path.as_ref(); - - if let Some(parent) = path.parent() - && !parent.exists() - { - tokio::fs::create_dir_all(parent).await?; - } - - let cwd = current_dir()?; - let file_path = cwd.join(path); - - // Determine file format - let format = file_path - .file_name() - .and_then(|name| name.to_str()) - .and_then(ConfigFormat::from_filename) - .unwrap_or(ConfigFormat::Bincode); // Default to Bincode - - match format { - ConfigFormat::Yaml => { - let contents = serde_yaml::to_string(val) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; - fs::write(&file_path, contents).await? - } - ConfigFormat::Toml => { - let contents = toml::to_string(val) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; - fs::write(&file_path, contents).await? - } - ConfigFormat::Ron => { - let mut pretty_config = ron::ser::PrettyConfig::new(); - pretty_config.new_line = Cow::from("\n"); - pretty_config.indentor = Cow::from(" "); - - let contents = ron::ser::to_string_pretty(val, pretty_config) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; - fs::write(&file_path, contents).await? - } - ConfigFormat::Json => { - let contents = serde_json::to_string(val) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; - fs::write(&file_path, contents).await? - } - ConfigFormat::Bincode => { - let bytes = bincode2::serialize(val) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; - fs::write(&file_path, bytes).await? - } - } - Ok(()) - } - - /// Check if the file returned by `default_path` exists - fn exist() -> bool - where - Self: Sized + Send + Sync, - { - let Ok(path) = Self::default_path() else { - return false; - }; - path.exists() - } -} diff --git a/utils/cfg_file/src/lib.rs b/utils/cfg_file/src/lib.rs deleted file mode 100644 index 72246e7..0000000 --- a/utils/cfg_file/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[cfg(feature = "derive")] -extern crate cfg_file_derive; - -#[cfg(feature = "derive")] -pub use cfg_file_derive::*; - -pub mod config; -- cgit