summaryrefslogtreecommitdiff
path: root/legacy_utils/cfg_file/cfg_file_derive
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-03-11 15:34:47 +0800
committer魏曹先生 <1992414357@qq.com>2026-03-11 15:34:47 +0800
commit8b38e22525adcafecd8b50daf041a1dc2d672d0b (patch)
treeb5c012dbbdd6975f405105b778d0ba9354a21233 /legacy_utils/cfg_file/cfg_file_derive
parente49128388e3b2f73523d82bf7f16fa1eae019c37 (diff)
Replace cfg_file with new config system
Diffstat (limited to 'legacy_utils/cfg_file/cfg_file_derive')
-rw-r--r--legacy_utils/cfg_file/cfg_file_derive/Cargo.toml11
-rw-r--r--legacy_utils/cfg_file/cfg_file_derive/src/lib.rs130
2 files changed, 141 insertions, 0 deletions
diff --git a/legacy_utils/cfg_file/cfg_file_derive/Cargo.toml b/legacy_utils/cfg_file/cfg_file_derive/Cargo.toml
new file mode 100644
index 0000000..ce5e77f
--- /dev/null
+++ b/legacy_utils/cfg_file/cfg_file_derive/Cargo.toml
@@ -0,0 +1,11 @@
+[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/legacy_utils/cfg_file/cfg_file_derive/src/lib.rs b/legacy_utils/cfg_file/cfg_file_derive/src/lib.rs
new file mode 100644
index 0000000..e916311
--- /dev/null
+++ b/legacy_utils/cfg_file/cfg_file_derive/src/lib.rs
@@ -0,0 +1,130 @@
+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<std::path::PathBuf, std::io::Error> {
+ Ok(#path_expr)
+ }
+ }
+ };
+
+ TokenStream::from(expanded)
+}
+
+enum PathExpr {
+ StringLiteral(String),
+ PathExpression(syn::Expr),
+}
+
+fn find_cfg_file_path(attrs: &[Attribute]) -> Option<PathExpr> {
+ 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
+}