config-rs icon indicating copy to clipboard operation
config-rs copied to clipboard

How to merge sources with existing default values?

Open cbeuw opened this issue 1 year ago • 3 comments

Suppose I have this config struct

#[derive(Debug, Deserialize)]
struct MyConfig {
    // ...
}

impl Default for MyConfig {
    fn default() -> MyConfig {
        // some complex logic
    }
}

I want to produce a MyConfig, where values explicitly set in config.toml are used, otherwise, the default implementation is used.

Something like

Config::builder().
    .add_source(File::with_name("config.toml"))
    .set_default(???, MyConfig::default())
    .build()?;

But I couldn't figure out a way to do this. This seems like a very common use case, so I feel like I'm missing something.

cbeuw avatar Dec 27 '24 18:12 cbeuw

Does the following work

#[derive(Debug, Deserialize)]
+#[serde(default)]
struct MyConfig {
    // ...
}

impl Default for MyConfig {
    fn default() -> MyConfig {
        // some complex logic
    }
}

See https://serde.rs/container-attrs.html#default

epage avatar Jan 13 '25 18:01 epage

I achieved it with the following pattern:

let defaults = Config::try_from(&MyConfig::default())?;

Config::builder()
    .add_source(defaults)
    .add_source(config::File::with_name("config.toml"))
    .build()?

You just need to derive Serialize on MyConfig.

tcbennun avatar Feb 21 '25 14:02 tcbennun

For those wondering, you can also apply #[serde(default = "default_addr")] on individual fields of the struct

iTrooz avatar Jun 29 '25 11:06 iTrooz