Attribute Macro serde_env_field::env_field_wrap

source ·
#[env_field_wrap]
Expand description

The env_field_wrap wraps all the fields of a struct or an enum with the EnvField type.

The Option<T> fields will remain optional, with only the T type wrapped with the EnvField.

Similarly, the Vec<T> fields will remain vectors, with only the T type wrapped.

It is possible to skip a field using the #[env_field_wrap(skip)] attribute. The fields that already have the EnvField type skipped automatically.

Also, one can wrap a generic type similarly to an Option field using the #[env_field_wrap(generics_only)] attribute.

NOTE: If you are using the #[derive(Deserialize)], the #[env_field_wrap] attribute must appear before it. Otherwise, it won’t work.

Examples

Basic
#[env_field_wrap]
#[derive(Serialize, Deserialize)]
struct Example {
    name: String,
    size: usize,
    num: i32,
}

std::env::set_var("SIZE", "100");

let de: Example = toml::from_str(r#"
    name = "${NAME:-Default Name}"

    size = "$SIZE"

    num = 42
"#).unwrap();

assert_eq!(&de.name, "Default Name");
assert_eq!(de.size, 100);
assert_eq!(de.num, 42);
Optional fields
#[env_field_wrap]
#[derive(Serialize, Deserialize)]
struct Example {
    required: i32,
    optional: Option<i32>,
}

let de: Example = toml::from_str(r#"
    required = 512
"#).unwrap();

assert_eq!(de.required, 512);
assert!(de.optional.is_none());

std::env::set_var("OPTIONAL", "-1024");
let de: Example = toml::from_str(r#"
    required = 512
    optional = "$OPTIONAL"
"#).unwrap();

assert_eq!(de.required, 512);
assert_eq!(de.optional.unwrap(), -1024);

let de: Example = toml::from_str(r#"
    required = 512
    optional = 42
"#).unwrap();

assert_eq!(de.required, 512);
assert_eq!(de.optional.unwrap(), 42);
Vector fields
#[env_field_wrap]
#[derive(Serialize, Deserialize)]
struct Example {
    seq: Vec<i32>,
}

std::env::set_var("NUM", "1000");
let de: Example = toml::from_str(r#"
    seq = [
        12, "$NUM", 145,
    ]
"#).unwrap();

assert_eq!(de.seq[0], 12);
assert_eq!(de.seq[1], 1000);
assert_eq!(de.seq[2], 145);
Skip a field
#[env_field_wrap]
#[derive(Serialize, Deserialize)]
struct Example {
   wrapped: String,

   #[env_field_wrap(skip)]
   skipped: String,
}

std::env::set_var("WRAPPED", "From Env");
let de: Example = toml::from_str(r#"
    wrapped = "$WRAPPED"
    skipped = "$SKIPPED"
"#).unwrap();

assert_eq!(&de.wrapped, "From Env");
assert_eq!(&de.skipped, "$SKIPPED");
Skip an enum variant
#[env_field_wrap]
#[derive(Serialize, Deserialize)]
enum Example {
    Wrapped(String),

    #[env_field_wrap(skip)]
    Skipped {
        inner_str: String,
    },
}

std::env::set_var("WRAPPED", "From Env");
let de: Example = serde_json::from_str(r#"
    {
        "Wrapped": "$WRAPPED"
    }
"#).unwrap();

assert!(matches![de, Example::Wrapped(s) if &s == "From Env"]);

let de: Example = serde_json::from_str(r#"
    {
        "Skipped": {
            "inner_str": "$WRAPPED"
        }
    }
"#).unwrap();

assert!(matches![de, Example::Skipped { inner_str } if inner_str == "$WRAPPED"]);
Wrap generics only
#[env_field_wrap]
#[derive(Serialize, Deserialize)]
struct Example {
    // Will become
    //    `Generics<EnvField<String>, EnvField<i32>, EnvField<Variants, UseDeserialize>>`
    // instead of
    //    `EnvField<Generics<String, i32, EnvField<Variants, UseDeserialize>>>`.
    //
    // Note:
    //  * if a generic is already wrapped into the `EnvField`, it *won't* be wrapped again.
    //  * the `Generics` don't need to implement the `FromStr` in this case.
    #[env_field_wrap(generics_only)]
    generics: Generics<String, i32, EnvField<Variants, UseDeserialize>>,
}

#[derive(Serialize, Deserialize)]
struct Generics<A, B, C> {
    a: A,
    b: B,
    c: C,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
enum Variants {
    FirstVariant,
    SecondVariant,
}

std::env::set_var("GENERICS_STR", "env string");
std::env::set_var("GENERICS_I32", "517");
std::env::set_var("GENERICS_VARIANT", "first-variant");
let de: Example = toml::from_str(r#"
    [generics]
    a = "$GENERICS_STR"
    b = "$GENERICS_I32"
    c = "$GENERICS_VARIANT"
"#).unwrap();

assert_eq!(&de.generics.a, "env string");
assert_eq!(de.generics.b, 517);
assert!(matches!(*de.generics.c, Variants::FirstVariant));