summaryrefslogtreecommitdiff
path: root/crates/utils
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2025-09-12 00:34:17 +0800
committer魏曹先生 <1992414357@qq.com>2025-09-12 00:34:17 +0800
commitbb5b15b5abba1caf0fa8d8861c118cd73edf76d0 (patch)
treef971fdc187441037949345f41301d85e2ec65602 /crates/utils
parent75b747a2569ad94381f9dd0b26780c3759d9f637 (diff)
Add Crate `cfg_file`
.../utils/cfg_file .../cfg_file/cfg_file_derive .../cfg_file/cfg_file_example
Diffstat (limited to 'crates/utils')
-rw-r--r--crates/utils/cfg_file/Cargo.toml17
-rw-r--r--crates/utils/cfg_file/cfg_file_derive/Cargo.toml11
-rw-r--r--crates/utils/cfg_file/cfg_file_derive/src/lib.rs87
-rw-r--r--crates/utils/cfg_file/cfg_file_example/Cargo.toml10
-rw-r--r--crates/utils/cfg_file/cfg_file_example/src/main.rs45
-rw-r--r--crates/utils/cfg_file/src/lib.rs1
6 files changed, 171 insertions, 0 deletions
diff --git a/crates/utils/cfg_file/Cargo.toml b/crates/utils/cfg_file/Cargo.toml
new file mode 100644
index 0000000..9d9a6b8
--- /dev/null
+++ b/crates/utils/cfg_file/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "cfg_file"
+edition = "2024"
+version.workspace = true
+
+[dependencies]
+
+# Async
+tokio = { version = "1.46.1", features = ["full"] }
+async-trait = "0.1.88"
+
+# Serialization
+serde = { version = "1.0.219", features = ["derive"] }
+serde_yaml = "0.9.34"
+serde_json = "1.0.140"
+ron = "0.11.0"
+toml = "0.9.0"
diff --git a/crates/utils/cfg_file/cfg_file_derive/Cargo.toml b/crates/utils/cfg_file/cfg_file_derive/Cargo.toml
new file mode 100644
index 0000000..ce5e77f
--- /dev/null
+++ b/crates/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/crates/utils/cfg_file/cfg_file_derive/src/lib.rs b/crates/utils/cfg_file/cfg_file_derive/src/lib.rs
new file mode 100644
index 0000000..3a6d0fd
--- /dev/null
+++ b/crates/utils/cfg_file/cfg_file_derive/src/lib.rs
@@ -0,0 +1,87 @@
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::parse::ParseStream;
+use syn::{
+ parse_macro_input,
+ DeriveInput,
+ Attribute
+};
+
+#[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(path) => {
+ if path.starts_with("./") {
+ let path_str = &path[2..];
+ quote! {
+ std::env::current_dir()?.join(#path_str)
+ }
+ } else {
+ // Using Absolute Path
+ quote! {
+ std::path::PathBuf::from(#path)
+ }
+ }
+ }
+ 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)
+}
+
+fn find_cfg_file_path(attrs: &[Attribute]) -> Option<String> {
+ 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") {
+ if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }) = path_meta.value {
+ return Ok(lit.value());
+ }
+ }
+ Err(meta.error("expected `path = \"...\"`"))
+ };
+
+ if let Ok(path) = attr.parse_args_with(parser) {
+ return Some(path);
+ }
+ }
+ }
+ 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
+} \ No newline at end of file
diff --git a/crates/utils/cfg_file/cfg_file_example/Cargo.toml b/crates/utils/cfg_file/cfg_file_example/Cargo.toml
new file mode 100644
index 0000000..932bc31
--- /dev/null
+++ b/crates/utils/cfg_file/cfg_file_example/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "cfg_file_example"
+edition = "2024"
+version.workspace = true
+
+[dependencies]
+cfg_file = { path = "../../cfg_file" }
+cfg_file_derive = { path = "../cfg_file_derive" }
+tokio = { version = "1.46.1", features = ["full"] }
+serde = { version = "1.0.219", features = ["derive"] } \ No newline at end of file
diff --git a/crates/utils/cfg_file/cfg_file_example/src/main.rs b/crates/utils/cfg_file/cfg_file_example/src/main.rs
new file mode 100644
index 0000000..aacef4e
--- /dev/null
+++ b/crates/utils/cfg_file/cfg_file_example/src/main.rs
@@ -0,0 +1,45 @@
+use std::collections::HashMap;
+use cfg_file_derive::ConfigFile;
+use serde::{Deserialize, Serialize};
+use cfg_file::config::ConfigFile;
+
+#[derive(ConfigFile, Deserialize, Serialize, Default)]
+#[cfg_file(path = "./.temp/example/cfg_file/example_cfg.toml")]
+struct ExampleConfig {
+ name: String,
+ age: i32,
+ hobby: Vec<String>,
+ secret: HashMap<String, String>,
+}
+
+#[tokio::main]
+async fn main() {
+ let mut example = ExampleConfig {
+ name: "Weicao".to_string(),
+ age: 22,
+ hobby: vec![ "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; // Write to default path.
+
+ // Read from default path.
+ let read_cfg = ExampleConfig::read().await;
+ 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/crates/utils/cfg_file/src/lib.rs b/crates/utils/cfg_file/src/lib.rs
new file mode 100644
index 0000000..a105933
--- /dev/null
+++ b/crates/utils/cfg_file/src/lib.rs
@@ -0,0 +1 @@
+pub mod config; \ No newline at end of file