mehcode / config-rs

⚙️ Layered configuration system for Rust applications (with strong support for 12-factor applications).
Apache License 2.0
2.57k stars 213 forks source link

Missing field for default value when no environments variable set. #366

Open BratSinot opened 2 years ago

BratSinot commented 2 years ago

Greetings!

Got error:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: missing field `first`', src/main.rs:55:10
stack backtrace:
   0: rust_begin_unwind
             at /rustc/4b91a6ea7258a947e59c6522cd5898e7c0a6a88f/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/4b91a6ea7258a947e59c6522cd5898e7c0a6a88f/library/core/src/panicking.rs:142:14
   2: core::result::unwrap_failed
             at /rustc/4b91a6ea7258a947e59c6522cd5898e7c0a6a88f/library/core/src/result.rs:1805:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/4b91a6ea7258a947e59c6522cd5898e7c0a6a88f/library/core/src/result.rs:1098:23
   4: playground2::main
             at ./src/main.rs:46:18
   5: core::ops::function::FnOnce::call_once
             at /rustc/4b91a6ea7258a947e59c6522cd5898e7c0a6a88f/library/core/src/ops/function.rs:248:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Process finished with exit code 101

with this code:

use config::{Config, Environment};
use serde::{Deserialize, Deserializer};

#[derive(Debug)]
struct Foo {
    secret: Box<str>,
}

impl<'de> Deserialize<'de> for Foo {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        fn default_secret() -> Box<str> {
            "secret0".into()
        }

        #[derive(Debug, Deserialize)]
        struct _Foo {
            first: First,
        }

        #[derive(Debug, Deserialize)]
        struct First {
            second: Second,
        }

        #[derive(Debug, Deserialize)]
        struct Second {
            #[serde(default = "default_secret")]
            secret: Box<str>,
        }

        let foo = _Foo::deserialize(deserializer)?;

        Ok(Self {
            secret: foo.first.second.secret,
        })
    }
}

fn main() {
    //std::env::set_var("FOO_FIRST_SECOND_SECRET", "secret1");
    //std::env::set_var("FOO_FIRST_SECOND_NO", "NO"); // work as expected

    let config = Config::builder()
        .add_source(
            Environment::with_prefix("FOO")
                .try_parsing(true)
                .separator("_"),
        )
        .build()
        .unwrap()
        .try_deserialize::<Foo>()
        .unwrap();

    println!("{config:?}");
}

but if I add some variable (even if it didn't used) in path: std::env::set_var("FOO_FIRST_SECOND_NO", "NO");, everything work fine.

Any workaround?