TheNeikos / envious

Deserialize (potentially nested) environment variables into your custom structs
Apache License 2.0
60 stars 2 forks source link

Cannot deserialize `None` value into a unit struct #12

Open tenuous-guidance opened 1 year ago

tenuous-guidance commented 1 year ago

Consider the following types:

#[derive(Debug, Deserialize)]
struct Config {
    var: Var,
}

#[derive(Debug, Deserialize)]
struct Var(Option<usize>);

I want to set the Config to { var: Var(None) }, how do I do so?

Options I have tried:

Example test case: Dependencies:

[dependencies]
envious = { version = "0.1.1" }
serde = { version = "1", features = ["derive"], default-features = false }

[dev-dependencies]
temp-env = "0.3"

Code:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Config {
    var: Var,
}

#[derive(Debug, Deserialize)]
struct Var(Option<usize>);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn unset() {
        temp_env::with_var("var", Option::<String>::None, || {
            envious::from_env::<Config>(envious::Prefix::None).unwrap();
        })
    }

    #[test]
    fn empty() {
        temp_env::with_var("var", Some(""), || {
            envious::from_env::<Config>(envious::Prefix::None).unwrap();
        })
    }

    #[test]
    fn none() {
        temp_env::with_var("var", Some("None"), || {
            envious::from_env::<Config>(envious::Prefix::None).unwrap();
        })
    }

    #[test]
    fn set_0() {
        temp_env::with_var("var_0", Some("1"), || {
            envious::from_env::<Config>(envious::Prefix::None).unwrap();
        })
    }

    #[test]
    fn unset_0() {
        temp_env::with_var("var_0", Option::<String>::None, || {
            envious::from_env::<Config>(envious::Prefix::None).unwrap();
        })
    }

    #[test]
    fn empty_0() {
        temp_env::with_var("var_0", Some(""), || {
            envious::from_env::<Config>(envious::Prefix::None).unwrap();
        })
    }

    #[test]
    fn none_0() {
        temp_env::with_var("var_0", Some("None"), || {
            envious::from_env::<Config>(envious::Prefix::None).unwrap();
        })
    }
}
TheNeikos commented 1 year ago

This is an interesting case.

So the problem stems broadly from the fact that environment variables can either not exist or be a &str.

JSON for example has the null value, which would help deal with it here.

I am not sure how to solve it from envious's side. One thing that should work for you, is if you give it a default that returns a None?

tenuous-guidance commented 1 year ago

It looks like it can work with:

#[derive(Debug, Deserialize)]
struct Config {
    #[serde(default)] 
    var: Var,
}

#[derive(Default, Debug, Deserialize)]
struct Var(Option<usize>);

Might be that's the best that can be done.