# `tenvy`
Parse environment variables into type-safe structures.
```rs
use tenvy::Tenvy;
#[derive(Debug, Tenvy)]
struct Environment {
database_url: String,
server: Server,
}
#[derive(Debug, Tenvy)]
struct Server {
addr: std::net::SocketAddr,
workers: Option<std::num::NonZeroUsize>,
}
fn main() -> anyhow::Result<()> {
let env: Environment = tenvy::from_env()?;
println!("{env:#?}");
Ok(())
}
```
## How is it different from [`envy`] and [`serde-env`]?
[`envy`]: https://crates.io/crates/envy
[`serde-env`]: https://crates.io/crates/serde-env
Both [`envy`] and [`serde-env`] make use of `serde` to extract environment
variables into types. Although the idea to use `serde` is rather nice and allows
to reuse features from `serde`, it faces several challenges in practice:
- Nested structures: [`envy`] does not support them at all, while [`serde-env`]
provides [suboptimal error messages if a variable is
missing][serde-env-issue-60];
- Flattening: `#[serde(flatten)]` is [hard to support][serde-env-issue-15]
because it uses `deserialize_any`;
- Renaming: counterintuitively you have to use lowercase names in
`#[serde(rename)]`, and that also means you cannot differentiate between
lowercase-named and uppercase-named variables;
- Customized deserialization: shall you need parse variables in a way that
cannot be achieved using `serde_derive`, you have to resort to very
verbose `Deserialize` implementation.
- Genericity: empty substructures are not handled well. For example,
deserializing `Environment<SubsystemStub>` successfully requires some variable
that starts with `SUBSYSTEMS` to be present, although its value is never used:
```rs
#[derive(Deserialize)]
struct Environment<Subsystem> {
subsystem: Subsystem,
}
#[derive(Deserialize)]
struct SubsystemStub {}
```
Many of such issues arise because `serde` is designed to parse various kinds of
data by giving everything it reads to the deserializer. However, when parsing
environment variables, we usually want to ask for data we need instead.
Moreover, we know that environment variables is a key-value structure with
basically random access.
So `tenvy` takes this other approach when we know that we're dealing with a
key-value map and ask it for data we need. This approach should allow for much
more pleasant API with less obstacles and surprises.
[serde-env-issue-60]: https://github.com/Xuanwo/serde-env/issues/60
[serde-env-issue-15]: https://github.com/Xuanwo/serde-env/issues/15