Skip to content

Commit 6cec5f3

Browse files
authored
Merge branch 'main' into refactor/simplify-derive-macro-implementation
2 parents 093dfcb + a31f6d2 commit 6cec5f3

13 files changed

Lines changed: 206 additions & 33 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Add to your `Cargo.toml`:
2323

2424
```toml
2525
[dependencies]
26-
gonfig = "0.1"
26+
gonfig = "0.1.6"
2727
serde = { version = "1.0", features = ["derive"] }
2828
```
2929

examples/complex.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,12 @@ fn main() -> gonfig::Result<()> {
8585
);
8686
}
8787
}
88-
Err(e) => eprintln!("Configuration error: {}", e),
88+
Err(e) => eprintln!("Configuration error: {e}"),
8989
}
9090
println!("\nRaw merged configuration:");
9191
match serde_json::to_string_pretty(&value) {
92-
Ok(json_str) => println!("{}", json_str),
93-
Err(e) => eprintln!("Failed to serialize to JSON: {}", e),
92+
Ok(json_str) => println!("{json_str}"),
93+
Err(e) => eprintln!("Failed to serialize to JSON: {e}"),
9494
}
9595

9696
Ok(())

examples/comprehensive_skip.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ fn main() -> gonfig::Result<()> {
9797
println!(" Runtime Connection: {:?}", config.runtime_connection);
9898
println!(" Internal Cache: {:?}", config.internal_cache);
9999
}
100-
Err(e) => println!("❌ Error loading AppConfig: {}", e),
100+
Err(e) => println!("❌ Error loading AppConfig: {e}"),
101101
}
102102

103103
println!("\n2. Loading DatabaseConfig:");
@@ -115,7 +115,7 @@ fn main() -> gonfig::Result<()> {
115115
println!("\n After setting password from secure vault:");
116116
println!(" Password: [SET FROM VAULT]");
117117
}
118-
Err(e) => println!("❌ Error loading DatabaseConfig: {}", e),
118+
Err(e) => println!("❌ Error loading DatabaseConfig: {e}"),
119119
}
120120

121121
println!("\n3. Skip vs Include Comparison:");

examples/madara.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ fn main() -> gonfig::Result<()> {
4444
std::env::set_var("MDR_SERVER_WORKERS", "4");
4545

4646
let config = Madara::from_gonfig()?;
47-
println!("Loaded config from environment: {:#?}", config);
47+
println!("Loaded config from environment: {config:#?}");
4848

4949
let builder = ConfigBuilder::new()
5050
.with_merge_strategy(MergeStrategy::Deep)
@@ -63,14 +63,14 @@ fn main() -> gonfig::Result<()> {
6363

6464
match builder.build::<Madara>() {
6565
Ok(config) => {
66-
println!("\nValidated config: {:#?}", config);
66+
println!("\nValidated config: {config:#?}");
6767
println!("\nMongo URI: {}", config.mongo.uri);
6868
println!("Server: {}:{}", config.server.host, config.server.port);
6969
if let Some(workers) = config.server.worker_threads {
70-
println!("Workers: {}", workers);
70+
println!("Workers: {workers}");
7171
}
7272
}
73-
Err(e) => eprintln!("Config error: {}", e),
73+
Err(e) => eprintln!("Config error: {e}"),
7474
}
7575

7676
Ok(())

examples/madara_usecase.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ fn main() -> gonfig::Result<()> {
5252
println!("✅ Loaded config from environment:");
5353
print_madara_config(&config);
5454
}
55-
Err(e) => println!("❌ Error: {}", e),
55+
Err(e) => println!("❌ Error: {e}"),
5656
}
5757

5858
println!("\n2. Loading with custom builder (advanced approach):");
@@ -104,14 +104,14 @@ fn main() -> gonfig::Result<()> {
104104
);
105105

106106
if let Some(workers) = config.server.worker_threads {
107-
println!("Worker Threads: {}", workers);
107+
println!("Worker Threads: {workers}");
108108
}
109109

110110
if let Some(timeout) = config.mongo.connection_timeout {
111-
println!("Connection Timeout: {}s", timeout);
111+
println!("Connection Timeout: {timeout}s");
112112
}
113113
}
114-
Err(e) => println!("❌ Validation failed: {}", e),
114+
Err(e) => println!("❌ Validation failed: {e}"),
115115
}
116116

117117
println!("\n4. Testing different environment variable patterns:");
@@ -150,20 +150,20 @@ fn print_madara_config(config: &Madara) {
150150
println!(" URI: {}", config.mongo.uri);
151151
println!(" Database: {}", config.mongo.database);
152152
if let Some(timeout) = config.mongo.connection_timeout {
153-
println!(" Timeout: {}s", timeout);
153+
println!(" Timeout: {timeout}s");
154154
}
155155
if let Some(pool_size) = config.mongo.max_pool_size {
156-
println!(" Pool Size: {}", pool_size);
156+
println!(" Pool Size: {pool_size}");
157157
}
158158

159159
println!(" 🌐 Server:");
160160
println!(" Host: {}", config.server.host);
161161
println!(" Port: {}", config.server.port);
162162
if let Some(workers) = config.server.worker_threads {
163-
println!(" Workers: {}", workers);
163+
println!(" Workers: {workers}");
164164
}
165165
if let Some(cors) = config.server.enable_cors {
166-
println!(" CORS: {}", cors);
166+
println!(" CORS: {cors}");
167167
}
168168
}
169169

examples/simple.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ fn main() -> gonfig::Result<()> {
2727
println!("Port: {}", config.port);
2828
println!("Debug: {}", config.debug);
2929
}
30-
Err(e) => eprintln!("Error: {}", e),
30+
Err(e) => eprintln!("Error: {e}"),
3131
}
3232

3333
Ok(())

examples/skip_attributes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ fn main() -> gonfig::Result<()> {
9090
println!("\n2. After manual initialization of skipped fields:");
9191
print_app_config_with_skipped(&config);
9292
}
93-
Err(e) => println!("❌ Error: {}", e),
93+
Err(e) => println!("❌ Error: {e}"),
9494
}
9595

9696
println!("\n3. Loading DatabaseConfig with selective skipping:");
@@ -107,7 +107,7 @@ fn main() -> gonfig::Result<()> {
107107
println!(" Password: [MANUALLY SET]");
108108
println!(" Pool: [MANUALLY INITIALIZED]");
109109
}
110-
Err(e) => println!("❌ Database config error: {}", e),
110+
Err(e) => println!("❌ Database config error: {e}"),
111111
}
112112

113113
println!("\n4. Skip attribute use cases:");

examples/your_usecase.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ fn main() -> gonfig::Result<()> {
4141
println!("✅ Successfully loaded configuration:");
4242
print_config(&config);
4343
}
44-
Err(e) => println!("❌ Error loading config: {}", e),
44+
Err(e) => println!("❌ Error loading config: {e}"),
4545
}
4646

4747
println!("\n2. Testing individual component loading:");
@@ -56,7 +56,7 @@ fn main() -> gonfig::Result<()> {
5656
println!(" Username: {}", mongo.username);
5757
println!(" Password: [REDACTED]");
5858
}
59-
Err(e) => println!(" Error: {}", e),
59+
Err(e) => println!(" Error: {e}"),
6060
}
6161

6262
// Test Application component
@@ -67,7 +67,7 @@ fn main() -> gonfig::Result<()> {
6767
println!(" Password: [REDACTED]");
6868
println!(" Client: {:?} (skipped in gonfig)", app.client);
6969
}
70-
Err(e) => println!(" Error: {}", e),
70+
Err(e) => println!(" Error: {e}"),
7171
}
7272

7373
println!("\n3. Environment variable mapping demonstration:");

gonfig_derive/src/lib.rs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,179 @@ struct GonfigField {
5050
default: Option<String>,
5151
}
5252

53+
/// Derive macro for the `Gonfig` trait, enabling declarative configuration management.
54+
///
55+
/// This macro generates configuration loading methods for your struct, supporting multiple
56+
/// configuration sources: environment variables, CLI arguments, and configuration files.
57+
///
58+
/// # Generated Methods
59+
///
60+
/// The macro generates three public methods on your struct:
61+
///
62+
/// - `from_gonfig() -> Result<Self>` - Loads configuration from all enabled sources
63+
/// - `from_gonfig_with_builder(builder: ConfigBuilder) -> Result<Self>` - Advanced configuration with custom builder
64+
/// - `gonfig_builder() -> ConfigBuilder` - Returns a pre-configured builder for advanced use cases
65+
///
66+
/// # Container Attributes
67+
///
68+
/// ## `#[Gonfig(env_prefix = "PREFIX")]`
69+
/// Sets the prefix for environment variables. Field names are automatically uppercased and
70+
/// appended to the prefix.
71+
///
72+
/// **Example:**
73+
/// ```rust,ignore
74+
/// #[derive(Gonfig, Deserialize)]
75+
/// #[Gonfig(env_prefix = "APP")]
76+
/// struct Config {
77+
/// database_url: String, // Environment variable: APP_DATABASE_URL
78+
/// port: u16, // Environment variable: APP_PORT
79+
/// }
80+
/// ```
81+
///
82+
/// ## `#[Gonfig(allow_cli)]`
83+
/// Enables CLI argument parsing. Field names are converted to kebab-case.
84+
///
85+
/// **Example:**
86+
/// ```rust,ignore
87+
/// #[derive(Gonfig, Deserialize)]
88+
/// #[Gonfig(allow_cli)]
89+
/// struct Config {
90+
/// max_connections: u32, // CLI argument: --max-connections
91+
/// }
92+
/// ```
93+
///
94+
/// ## `#[Gonfig(allow_config)]`
95+
/// Enables automatic config file loading. Checks for `config.toml`, `config.yaml`, or
96+
/// `config.json` in the current directory.
97+
///
98+
/// **Example:**
99+
/// ```rust,ignore
100+
/// #[derive(Gonfig, Deserialize)]
101+
/// #[Gonfig(allow_config)]
102+
/// struct Config {
103+
/// // Loads from config.toml, config.yaml, or config.json if present
104+
/// setting: String,
105+
/// }
106+
/// ```
107+
///
108+
/// # Field Attributes
109+
///
110+
/// ## `#[gonfig(env_name = "CUSTOM_NAME")]`
111+
/// Override the environment variable name for a specific field.
112+
///
113+
/// **Example:**
114+
/// ```rust,ignore
115+
/// #[derive(Gonfig, Deserialize)]
116+
/// #[Gonfig(env_prefix = "APP")]
117+
/// struct Config {
118+
/// #[gonfig(env_name = "DATABASE_CONNECTION_STRING")]
119+
/// database_url: String, // Uses DATABASE_CONNECTION_STRING instead of APP_DATABASE_URL
120+
/// }
121+
/// ```
122+
///
123+
/// ## `#[gonfig(cli_name = "custom-name")]`
124+
/// Override the CLI argument name for a specific field.
125+
///
126+
/// **Example:**
127+
/// ```rust,ignore
128+
/// #[derive(Gonfig, Deserialize)]
129+
/// #[Gonfig(allow_cli)]
130+
/// struct Config {
131+
/// #[gonfig(cli_name = "db-url")]
132+
/// database_url: String, // CLI argument: --db-url instead of --database-url
133+
/// }
134+
/// ```
135+
///
136+
/// ## `#[gonfig(default = "value")]`
137+
/// Specify a default value for a field. The value should be a JSON-compatible string.
138+
///
139+
/// **Example:**
140+
/// ```rust,ignore
141+
/// #[derive(Gonfig, Deserialize)]
142+
/// struct Config {
143+
/// #[gonfig(default = "8080")]
144+
/// port: u16,
145+
///
146+
/// #[gonfig(default = r#"["localhost"]"#)]
147+
/// allowed_hosts: Vec<String>,
148+
/// }
149+
/// ```
150+
///
151+
/// ## `#[skip]` or `#[skip_gonfig]`
152+
/// Exclude a field from configuration loading. Useful for non-serializable fields or
153+
/// fields that should only be set at runtime.
154+
///
155+
/// **Example:**
156+
/// ```rust,ignore
157+
/// #[derive(Gonfig, Deserialize)]
158+
/// struct Config {
159+
/// database_url: String,
160+
///
161+
/// #[skip]
162+
/// #[serde(skip)]
163+
/// runtime_data: Option<String>, // Not loaded from config sources
164+
/// }
165+
/// ```
166+
///
167+
/// # Configuration Priority
168+
///
169+
/// Configuration sources are merged in the following priority order (later sources override earlier ones):
170+
///
171+
/// 1. Default values (from `#[gonfig(default)]` attributes)
172+
/// 2. Configuration files (if `allow_config` is set)
173+
/// 3. Environment variables (always enabled)
174+
/// 4. CLI arguments (if `allow_cli` is set)
175+
///
176+
/// # Complete Example
177+
///
178+
/// ```rust,ignore
179+
/// use gonfig::Gonfig;
180+
/// use serde::Deserialize;
181+
///
182+
/// #[derive(Debug, Deserialize, Gonfig)]
183+
/// #[Gonfig(env_prefix = "MYAPP", allow_cli, allow_config)]
184+
/// struct AppConfig {
185+
/// /// Database connection URL
186+
/// /// - Environment: MYAPP_DATABASE_URL
187+
/// /// - CLI: --database-url
188+
/// database_url: String,
189+
///
190+
/// /// Server port (default: 8080)
191+
/// /// - Environment: MYAPP_PORT
192+
/// /// - CLI: --port
193+
/// #[gonfig(default = "8080")]
194+
/// port: u16,
195+
///
196+
/// /// Custom environment variable name
197+
/// #[gonfig(env_name = "LOG_LEVEL")]
198+
/// log_level: String,
199+
///
200+
/// /// Runtime field (not loaded from config)
201+
/// #[skip]
202+
/// #[serde(skip)]
203+
/// start_time: Option<std::time::Instant>,
204+
/// }
205+
///
206+
/// fn main() -> gonfig::Result<()> {
207+
/// // Simple usage
208+
/// let config = AppConfig::from_gonfig()?;
209+
///
210+
/// // Advanced usage with custom builder
211+
/// let mut builder = AppConfig::gonfig_builder();
212+
/// builder = builder.with_file("custom.toml")?;
213+
/// let config = AppConfig::from_gonfig_with_builder(builder)?;
214+
///
215+
/// println!("Config: {:?}", config);
216+
/// Ok(())
217+
/// }
218+
/// ```
219+
///
220+
/// # Supported Attributes
221+
///
222+
/// - `gonfig` - Field-level attribute for configuration options
223+
/// - `skip_gonfig` - Field-level attribute to skip a field
224+
/// - `skip` - Alternative field-level skip attribute (compatible with serde)
225+
/// - `Gonfig` - Container-level attribute for struct-wide options
53226
#[proc_macro_derive(Gonfig, attributes(gonfig, skip_gonfig, skip, Gonfig))]
54227
pub fn derive_gonfig(input: TokenStream) -> TokenStream {
55228
let input = parse_macro_input!(input as DeriveInput);

src/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ impl ConfigBuilder {
413413
}
414414

415415
serde_json::from_value(merged)
416-
.map_err(|e| Error::Serialization(format!("Failed to deserialize config: {}", e)))
416+
.map_err(|e| Error::Serialization(format!("Failed to deserialize config: {e}")))
417417
}
418418

419419
pub fn build_value(self) -> Result<Value> {

0 commit comments

Comments
 (0)