Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "gonfig"
version = "0.1.5"
version = "0.1.6"
edition = "2021"
authors = ["Vasanthkumar Kalaiselvan<itsparser@gmail.com>"]
description = "A unified configuration management library for Rust that seamlessly integrates environment variables, config files, and CLI arguments"
Expand Down
6 changes: 3 additions & 3 deletions examples/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand Down
4 changes: 2 additions & 2 deletions examples/comprehensive_skip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:");
Expand All @@ -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:");
Expand Down
8 changes: 4 additions & 4 deletions examples/madara.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -63,14 +63,14 @@ fn main() -> gonfig::Result<()> {

match builder.build::<Madara>() {
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(())
Expand Down
16 changes: 8 additions & 8 deletions examples/madara_usecase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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):");
Expand Down Expand Up @@ -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:");
Expand Down Expand Up @@ -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}");
}
}

Expand Down
2 changes: 1 addition & 1 deletion examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand Down
4 changes: 2 additions & 2 deletions examples/skip_attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:");
Expand All @@ -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:");
Expand Down
6 changes: 3 additions & 3 deletions examples/your_usecase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:");
Expand All @@ -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
Expand All @@ -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:");
Expand Down
2 changes: 1 addition & 1 deletion gonfig_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "gonfig_derive"
version = "0.1.5"
version = "0.1.6"
edition = "2021"
authors = ["Vasanthkumar Kalaiselvan<itsparser@gmail.com>"]
description = "Derive macros for the gonfig configuration management library"
Expand Down
173 changes: 173 additions & 0 deletions gonfig_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,179 @@ struct GonfigField {
default: Option<String>,
}

/// 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<Self>` - Loads configuration from all enabled sources
/// - `from_gonfig_with_builder(builder: ConfigBuilder) -> Result<Self>` - 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<String>,
/// }
/// ```
///
/// ## `#[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<String>, // 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<std::time::Instant>,
/// }
///
/// 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);
Expand Down
2 changes: 1 addition & 1 deletion src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value> {
Expand Down
2 changes: 1 addition & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading