Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
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
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
Loading