From 093dfcb1c179b8c5b46a8c3dedf458439416e295 Mon Sep 17 00:00:00 2001 From: itsparser Date: Fri, 26 Sep 2025 00:09:35 +0530 Subject: [PATCH 1/2] version bump for the 1.0.6 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- gonfig_derive/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38056a5..100fa51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "gonfig" -version = "0.1.5" +version = "0.1.6" dependencies = [ "clap", "gonfig_derive", @@ -251,7 +251,7 @@ dependencies = [ [[package]] name = "gonfig_derive" -version = "0.1.5" +version = "0.1.6" dependencies = [ "darling", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 241980f..48f8fde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gonfig" -version = "0.1.5" +version = "0.1.6" edition = "2021" authors = ["Vasanthkumar Kalaiselvan"] description = "A unified configuration management library for Rust that seamlessly integrates environment variables, config files, and CLI arguments" diff --git a/gonfig_derive/Cargo.toml b/gonfig_derive/Cargo.toml index ea23990..01d2528 100644 --- a/gonfig_derive/Cargo.toml +++ b/gonfig_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gonfig_derive" -version = "0.1.5" +version = "0.1.6" edition = "2021" authors = ["Vasanthkumar Kalaiselvan"] description = "Derive macros for the gonfig configuration management library" From 9bbe68c5719cd87ab9307315eb465b1094d23c06 Mon Sep 17 00:00:00 2001 From: itsparser Date: Sat, 18 Oct 2025 22:52:28 +0530 Subject: [PATCH 2/2] docs: Add comprehensive documentation for Gonfig derive macro Add detailed rustdoc comments to the Gonfig derive macro including: - Overview of generated methods - Container attributes (env_prefix, allow_cli, allow_config) - Field attributes (env_name, cli_name, default, skip) - Configuration priority order - Complete usage examples Also fix clippy warnings for uninlined format arguments across: - src/builder.rs, src/cli.rs, src/config.rs - examples/ - tests/ --- examples/complex.rs | 6 +- examples/comprehensive_skip.rs | 4 +- examples/madara.rs | 8 +- examples/madara_usecase.rs | 16 +-- examples/simple.rs | 2 +- examples/skip_attributes.rs | 4 +- examples/your_usecase.rs | 6 +- gonfig_derive/src/lib.rs | 173 +++++++++++++++++++++++++++++++++ src/builder.rs | 2 +- src/cli.rs | 2 +- src/config.rs | 12 +-- tests/debug_env.rs | 2 +- 12 files changed, 205 insertions(+), 32 deletions(-) diff --git a/examples/complex.rs b/examples/complex.rs index a82be6f..d680f92 100644 --- a/examples/complex.rs +++ b/examples/complex.rs @@ -85,12 +85,12 @@ fn main() -> gonfig::Result<()> { ); } } - Err(e) => eprintln!("Configuration error: {}", e), + Err(e) => eprintln!("Configuration error: {e}"), } println!("\nRaw merged configuration:"); match serde_json::to_string_pretty(&value) { - Ok(json_str) => println!("{}", json_str), - Err(e) => eprintln!("Failed to serialize to JSON: {}", e), + Ok(json_str) => println!("{json_str}"), + Err(e) => eprintln!("Failed to serialize to JSON: {e}"), } Ok(()) diff --git a/examples/comprehensive_skip.rs b/examples/comprehensive_skip.rs index a9ce88d..850e8c1 100644 --- a/examples/comprehensive_skip.rs +++ b/examples/comprehensive_skip.rs @@ -97,7 +97,7 @@ fn main() -> gonfig::Result<()> { println!(" Runtime Connection: {:?}", config.runtime_connection); println!(" Internal Cache: {:?}", config.internal_cache); } - Err(e) => println!("❌ Error loading AppConfig: {}", e), + Err(e) => println!("❌ Error loading AppConfig: {e}"), } println!("\n2. Loading DatabaseConfig:"); @@ -115,7 +115,7 @@ fn main() -> gonfig::Result<()> { println!("\n After setting password from secure vault:"); println!(" Password: [SET FROM VAULT]"); } - Err(e) => println!("❌ Error loading DatabaseConfig: {}", e), + Err(e) => println!("❌ Error loading DatabaseConfig: {e}"), } println!("\n3. Skip vs Include Comparison:"); diff --git a/examples/madara.rs b/examples/madara.rs index 5bdfe5e..4ec6c2a 100644 --- a/examples/madara.rs +++ b/examples/madara.rs @@ -44,7 +44,7 @@ fn main() -> gonfig::Result<()> { std::env::set_var("MDR_SERVER_WORKERS", "4"); let config = Madara::from_gonfig()?; - println!("Loaded config from environment: {:#?}", config); + println!("Loaded config from environment: {config:#?}"); let builder = ConfigBuilder::new() .with_merge_strategy(MergeStrategy::Deep) @@ -63,14 +63,14 @@ fn main() -> gonfig::Result<()> { match builder.build::() { Ok(config) => { - println!("\nValidated config: {:#?}", config); + println!("\nValidated config: {config:#?}"); println!("\nMongo URI: {}", config.mongo.uri); println!("Server: {}:{}", config.server.host, config.server.port); if let Some(workers) = config.server.worker_threads { - println!("Workers: {}", workers); + println!("Workers: {workers}"); } } - Err(e) => eprintln!("Config error: {}", e), + Err(e) => eprintln!("Config error: {e}"), } Ok(()) diff --git a/examples/madara_usecase.rs b/examples/madara_usecase.rs index a6b08d8..6553df2 100644 --- a/examples/madara_usecase.rs +++ b/examples/madara_usecase.rs @@ -52,7 +52,7 @@ fn main() -> gonfig::Result<()> { println!("✅ Loaded config from environment:"); print_madara_config(&config); } - Err(e) => println!("❌ Error: {}", e), + Err(e) => println!("❌ Error: {e}"), } println!("\n2. Loading with custom builder (advanced approach):"); @@ -104,14 +104,14 @@ fn main() -> gonfig::Result<()> { ); if let Some(workers) = config.server.worker_threads { - println!("Worker Threads: {}", workers); + println!("Worker Threads: {workers}"); } if let Some(timeout) = config.mongo.connection_timeout { - println!("Connection Timeout: {}s", timeout); + println!("Connection Timeout: {timeout}s"); } } - Err(e) => println!("❌ Validation failed: {}", e), + Err(e) => println!("❌ Validation failed: {e}"), } println!("\n4. Testing different environment variable patterns:"); @@ -150,20 +150,20 @@ fn print_madara_config(config: &Madara) { println!(" URI: {}", config.mongo.uri); println!(" Database: {}", config.mongo.database); if let Some(timeout) = config.mongo.connection_timeout { - println!(" Timeout: {}s", timeout); + println!(" Timeout: {timeout}s"); } if let Some(pool_size) = config.mongo.max_pool_size { - println!(" Pool Size: {}", pool_size); + println!(" Pool Size: {pool_size}"); } println!(" 🌐 Server:"); println!(" Host: {}", config.server.host); println!(" Port: {}", config.server.port); if let Some(workers) = config.server.worker_threads { - println!(" Workers: {}", workers); + println!(" Workers: {workers}"); } if let Some(cors) = config.server.enable_cors { - println!(" CORS: {}", cors); + println!(" CORS: {cors}"); } } diff --git a/examples/simple.rs b/examples/simple.rs index c3722a8..bf32268 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -27,7 +27,7 @@ fn main() -> gonfig::Result<()> { println!("Port: {}", config.port); println!("Debug: {}", config.debug); } - Err(e) => eprintln!("Error: {}", e), + Err(e) => eprintln!("Error: {e}"), } Ok(()) diff --git a/examples/skip_attributes.rs b/examples/skip_attributes.rs index d1ceadb..3dabde2 100644 --- a/examples/skip_attributes.rs +++ b/examples/skip_attributes.rs @@ -90,7 +90,7 @@ fn main() -> gonfig::Result<()> { println!("\n2. After manual initialization of skipped fields:"); print_app_config_with_skipped(&config); } - Err(e) => println!("❌ Error: {}", e), + Err(e) => println!("❌ Error: {e}"), } println!("\n3. Loading DatabaseConfig with selective skipping:"); @@ -107,7 +107,7 @@ fn main() -> gonfig::Result<()> { println!(" Password: [MANUALLY SET]"); println!(" Pool: [MANUALLY INITIALIZED]"); } - Err(e) => println!("❌ Database config error: {}", e), + Err(e) => println!("❌ Database config error: {e}"), } println!("\n4. Skip attribute use cases:"); diff --git a/examples/your_usecase.rs b/examples/your_usecase.rs index ec68daa..053eb25 100644 --- a/examples/your_usecase.rs +++ b/examples/your_usecase.rs @@ -41,7 +41,7 @@ fn main() -> gonfig::Result<()> { println!("✅ Successfully loaded configuration:"); print_config(&config); } - Err(e) => println!("❌ Error loading config: {}", e), + Err(e) => println!("❌ Error loading config: {e}"), } println!("\n2. Testing individual component loading:"); @@ -56,7 +56,7 @@ fn main() -> gonfig::Result<()> { println!(" Username: {}", mongo.username); println!(" Password: [REDACTED]"); } - Err(e) => println!(" Error: {}", e), + Err(e) => println!(" Error: {e}"), } // Test Application component @@ -67,7 +67,7 @@ fn main() -> gonfig::Result<()> { println!(" Password: [REDACTED]"); println!(" Client: {:?} (skipped in gonfig)", app.client); } - Err(e) => println!(" Error: {}", e), + Err(e) => println!(" Error: {e}"), } println!("\n3. Environment variable mapping demonstration:"); diff --git a/gonfig_derive/src/lib.rs b/gonfig_derive/src/lib.rs index 20adefa..4226d2b 100644 --- a/gonfig_derive/src/lib.rs +++ b/gonfig_derive/src/lib.rs @@ -50,6 +50,179 @@ struct GonfigField { default: Option, } +/// Derive macro for the `Gonfig` trait, enabling declarative configuration management. +/// +/// This macro generates configuration loading methods for your struct, supporting multiple +/// configuration sources: environment variables, CLI arguments, and configuration files. +/// +/// # Generated Methods +/// +/// The macro generates three public methods on your struct: +/// +/// - `from_gonfig() -> Result` - Loads configuration from all enabled sources +/// - `from_gonfig_with_builder(builder: ConfigBuilder) -> Result` - Advanced configuration with custom builder +/// - `gonfig_builder() -> ConfigBuilder` - Returns a pre-configured builder for advanced use cases +/// +/// # Container Attributes +/// +/// ## `#[Gonfig(env_prefix = "PREFIX")]` +/// Sets the prefix for environment variables. Field names are automatically uppercased and +/// appended to the prefix. +/// +/// **Example:** +/// ```rust,ignore +/// #[derive(Gonfig, Deserialize)] +/// #[Gonfig(env_prefix = "APP")] +/// struct Config { +/// database_url: String, // Environment variable: APP_DATABASE_URL +/// port: u16, // Environment variable: APP_PORT +/// } +/// ``` +/// +/// ## `#[Gonfig(allow_cli)]` +/// Enables CLI argument parsing. Field names are converted to kebab-case. +/// +/// **Example:** +/// ```rust,ignore +/// #[derive(Gonfig, Deserialize)] +/// #[Gonfig(allow_cli)] +/// struct Config { +/// max_connections: u32, // CLI argument: --max-connections +/// } +/// ``` +/// +/// ## `#[Gonfig(allow_config)]` +/// Enables automatic config file loading. Checks for `config.toml`, `config.yaml`, or +/// `config.json` in the current directory. +/// +/// **Example:** +/// ```rust,ignore +/// #[derive(Gonfig, Deserialize)] +/// #[Gonfig(allow_config)] +/// struct Config { +/// // Loads from config.toml, config.yaml, or config.json if present +/// setting: String, +/// } +/// ``` +/// +/// # Field Attributes +/// +/// ## `#[gonfig(env_name = "CUSTOM_NAME")]` +/// Override the environment variable name for a specific field. +/// +/// **Example:** +/// ```rust,ignore +/// #[derive(Gonfig, Deserialize)] +/// #[Gonfig(env_prefix = "APP")] +/// struct Config { +/// #[gonfig(env_name = "DATABASE_CONNECTION_STRING")] +/// database_url: String, // Uses DATABASE_CONNECTION_STRING instead of APP_DATABASE_URL +/// } +/// ``` +/// +/// ## `#[gonfig(cli_name = "custom-name")]` +/// Override the CLI argument name for a specific field. +/// +/// **Example:** +/// ```rust,ignore +/// #[derive(Gonfig, Deserialize)] +/// #[Gonfig(allow_cli)] +/// struct Config { +/// #[gonfig(cli_name = "db-url")] +/// database_url: String, // CLI argument: --db-url instead of --database-url +/// } +/// ``` +/// +/// ## `#[gonfig(default = "value")]` +/// Specify a default value for a field. The value should be a JSON-compatible string. +/// +/// **Example:** +/// ```rust,ignore +/// #[derive(Gonfig, Deserialize)] +/// struct Config { +/// #[gonfig(default = "8080")] +/// port: u16, +/// +/// #[gonfig(default = r#"["localhost"]"#)] +/// allowed_hosts: Vec, +/// } +/// ``` +/// +/// ## `#[skip]` or `#[skip_gonfig]` +/// Exclude a field from configuration loading. Useful for non-serializable fields or +/// fields that should only be set at runtime. +/// +/// **Example:** +/// ```rust,ignore +/// #[derive(Gonfig, Deserialize)] +/// struct Config { +/// database_url: String, +/// +/// #[skip] +/// #[serde(skip)] +/// runtime_data: Option, // Not loaded from config sources +/// } +/// ``` +/// +/// # Configuration Priority +/// +/// Configuration sources are merged in the following priority order (later sources override earlier ones): +/// +/// 1. Default values (from `#[gonfig(default)]` attributes) +/// 2. Configuration files (if `allow_config` is set) +/// 3. Environment variables (always enabled) +/// 4. CLI arguments (if `allow_cli` is set) +/// +/// # Complete Example +/// +/// ```rust,ignore +/// use gonfig::Gonfig; +/// use serde::Deserialize; +/// +/// #[derive(Debug, Deserialize, Gonfig)] +/// #[Gonfig(env_prefix = "MYAPP", allow_cli, allow_config)] +/// struct AppConfig { +/// /// Database connection URL +/// /// - Environment: MYAPP_DATABASE_URL +/// /// - CLI: --database-url +/// database_url: String, +/// +/// /// Server port (default: 8080) +/// /// - Environment: MYAPP_PORT +/// /// - CLI: --port +/// #[gonfig(default = "8080")] +/// port: u16, +/// +/// /// Custom environment variable name +/// #[gonfig(env_name = "LOG_LEVEL")] +/// log_level: String, +/// +/// /// Runtime field (not loaded from config) +/// #[skip] +/// #[serde(skip)] +/// start_time: Option, +/// } +/// +/// fn main() -> gonfig::Result<()> { +/// // Simple usage +/// let config = AppConfig::from_gonfig()?; +/// +/// // Advanced usage with custom builder +/// let mut builder = AppConfig::gonfig_builder(); +/// builder = builder.with_file("custom.toml")?; +/// let config = AppConfig::from_gonfig_with_builder(builder)?; +/// +/// println!("Config: {:?}", config); +/// Ok(()) +/// } +/// ``` +/// +/// # Supported Attributes +/// +/// - `gonfig` - Field-level attribute for configuration options +/// - `skip_gonfig` - Field-level attribute to skip a field +/// - `skip` - Alternative field-level skip attribute (compatible with serde) +/// - `Gonfig` - Container-level attribute for struct-wide options #[proc_macro_derive(Gonfig, attributes(gonfig, skip_gonfig, skip, Gonfig))] pub fn derive_gonfig(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); diff --git a/src/builder.rs b/src/builder.rs index e29e259..8ce991f 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -413,7 +413,7 @@ impl ConfigBuilder { } serde_json::from_value(merged) - .map_err(|e| Error::Serialization(format!("Failed to deserialize config: {}", e))) + .map_err(|e| Error::Serialization(format!("Failed to deserialize config: {e}"))) } pub fn build_value(self) -> Result { diff --git a/src/cli.rs b/src/cli.rs index 53a936e..42d7c1b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -62,7 +62,7 @@ impl Cli { let app = T::parse(); let json_value = serde_json::to_value(&app).map_err(|e| { - crate::error::Error::Serialization(format!("Failed to serialize clap args: {}", e)) + crate::error::Error::Serialization(format!("Failed to serialize clap args: {e}")) })?; let mut parsed_values = HashMap::new(); diff --git a/src/config.rs b/src/config.rs index b075649..1e4cbef 100644 --- a/src/config.rs +++ b/src/config.rs @@ -93,14 +93,14 @@ impl ConfigFormat { pub fn parse(&self, content: &str) -> Result { match self { ConfigFormat::Json => serde_json::from_str(content) - .map_err(|e| Error::Serialization(format!("JSON parse error: {}", e))), + .map_err(|e| Error::Serialization(format!("JSON parse error: {e}"))), ConfigFormat::Yaml => serde_yaml::from_str(content) - .map_err(|e| Error::Serialization(format!("YAML parse error: {}", e))), + .map_err(|e| Error::Serialization(format!("YAML parse error: {e}"))), ConfigFormat::Toml => { let toml_value: toml::Value = toml::from_str(content) - .map_err(|e| Error::Serialization(format!("TOML parse error: {}", e)))?; + .map_err(|e| Error::Serialization(format!("TOML parse error: {e}")))?; serde_json::to_value(toml_value).map_err(|e| { - Error::Serialization(format!("TOML to JSON conversion error: {}", e)) + Error::Serialization(format!("TOML to JSON conversion error: {e}")) }) } } @@ -165,7 +165,7 @@ impl Config { .extension() .and_then(|ext| ext.to_str()) .and_then(ConfigFormat::from_extension) - .ok_or_else(|| Error::Config(format!("Unknown config format for file: {:?}", path)))?; + .ok_or_else(|| Error::Config(format!("Unknown config format for file: {path:?}")))?; let mut config = Self { path, @@ -204,7 +204,7 @@ impl Config { .extension() .and_then(|ext| ext.to_str()) .and_then(ConfigFormat::from_extension) - .ok_or_else(|| Error::Config(format!("Unknown config format for file: {:?}", path)))?; + .ok_or_else(|| Error::Config(format!("Unknown config format for file: {path:?}")))?; let path_display = path.display().to_string(); let mut config = Self { diff --git a/tests/debug_env.rs b/tests/debug_env.rs index 0c90025..af4ccc8 100644 --- a/tests/debug_env.rs +++ b/tests/debug_env.rs @@ -10,7 +10,7 @@ fn debug_environment_collection() { let env = Environment::new(); let result = env.collect().unwrap(); - println!("Collected environment: {:#?}", result); + println!("Collected environment: {result:#?}"); // Clean up env::remove_var("DATABASE_URL");