softprops / envy

deserialize env vars into typesafe structs with rust
MIT License
856 stars 39 forks source link

Add "separator" to parse nested structs #55

Open dbofmmbt opened 3 years ago

dbofmmbt commented 3 years ago

💡 Feature description

In config-rs, there's an option to define a separator which allow users to structure their env vars. I think an example will illustrate better:

#[derive(Deserialize)]
struct Config {
    database: Database
}

#[derive(Deserialize)]
struct Database {
    name: String,
}

If we define a separator, let's say "", we could create this Config by setting `DATABASENAME=foo`.

Would it be possible and desirable for envy to include it?

Alternatives

It is possible to get the same behavior using the flatten attribute from serde (as noticed in #15). The drawback is that it demands a good amount of boilerplate because we would have to serde's rename every field in the nested struct if we wanted to use this separator idea.

jonasbb commented 3 years ago

I want to mention one alternative using serde_with::with_prefix. It's a bit less boilerplate than applying rename on every field. It is not a replacement for having it integrated directly into envy though.

serde_with::with_prefix solution ```rust serde_with::with_prefix!(pdatabase "database__"); #[derive(Debug, serde::Deserialize)] struct Config { #[serde(flatten, with = "pdatabase")] database: Database, } #[derive(Debug, serde::Deserialize)] struct Database { name: String, } fn main() { std::env::set_var("DATABASE__NAME", "foobar"); let config: Config = envy::from_env().unwrap(); dbg!(config); } ```
jayvdb commented 8 months ago

Worth noting that flatten doesnt work with non-String datatypes. c.f. https://github.com/softprops/envy/issues/26

gammelalf commented 1 day ago

I would be really interested in such a feature and have even considered implementing on my own. But if this crate could do it for me, I would really appreciate it.

Slightly more context: I'd be interested in a solution which doesn't require any #[serde(rename)] or #[serde(renameflatten] because I want to declare a config struct (with sub structs) and be able to choose whether I want to deserialize it from toml, json or env. But when using a structured format like toml or json I want to preserve the nesting structure.

Using __ as separator would be fine for me, but there should probably be an API where you configure a deserializer to use any separator you'd like.

jayvdb commented 1 day ago

fwiw, I collated a list of similar Rust crates, trying to find ones which do nested structs, at https://github.com/Xuanwo/serde-env/discussions/54