# econf Loads environment variables into your structs in one shot. [![Latest version](https://img.shields.io/crates/v/econf.svg)](https://crates.io/crates/econf) [![Documentation](https://docs.rs/econf/badge.svg)](https://docs.rs/econf) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Actions Status](https://github.com/YushiOMOTE/econf/workflows/test/badge.svg)](https://github.com/YushiOMOTE/econf/actions) ![](https://github.com/YushiOMOTE/econf/blob/master/assets/logo.png?raw=true) `econf` allows to override struct fields with environment variables easily. This is useful to build up applications that optionally overrides some configuration with environment variables. Here is the basic usage: ```rust use econf::LoadEnv; #[derive(Debug, LoadEnv)] struct A { x: bool, y: u64, } let a = A { x: true, y: 42, }; println!("Before: {:?}", a); let a = econf::load(a, "PREFIX"); println!("After: {:?}", a); ``` ```sh $ ./app Before: A { x: true, y: 42 } After: A { x: true, y: 42 } $ PREFIX_X=false ./app Before: A { x: true, y: 42 } After: A { x: false, y: 42 } ``` In this example, * `PREFIX_X` is loaded to `x` * `PREFIX_Y` is loaded to `y` The environment variables are all upper-case with `_` separated. ## Why econf? There are some existing crates that provide similar features but `econf` is unique in the following ways: * **Supports nesting:** Supports nested structs in an intutive manner with a little constraint. * **Supports containers:** Supports `Vec`, `HashMap` and various types. * **Supplemental:** Loads into existing variables in the code without changing the original logic. * **Contributor friendly:** Simple code base. Comprehensible with a little study on basic macro usage. ## Supported types * Boolean: `bool` * Integer: `isize`, `usize`, `i8`, `i16`,`i32`,`i64`,`i128`, `u8`,`u16`,`u32`,`u64`,`u128` * String: `char`, `String` * Float: `f32`, `f64` * Network: `IpAddr`,`Ipv4Addr`,`Ipv6Addr`,`SocketAddr`,`SocketAddrV4`,`SocketAddrV6` * Non-zero types: `NonZeroI128`,`NonZeroI16`,`NonZeroI32`,`NonZeroI64`,`NonZeroI8`,`NonZeroIsize`,`NonZeroU128`, `NonZeroU16`,`NonZeroU32`,`NonZeroU64`,`NonZeroU8`, `NonZeroUsize` * File system: `PathBuf` * Containers: `Vec`, `HashSet`, `HashMap`, `Option`, `BTreeMap`, `BTreeSet`, `BinaryHeap`, `LinkedList`, `VecDeque`, `tuple` * Containers are parsed as YAML format. See [the tests](https://github.com/YushiOMOTE/econf/blob/master/econf/tests/basics.rs). ## Enums Since v0.3.0, econf requires enums to implement [FromStr](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait. Without this implementation, your program will fail to compile. While you can write the `FromStr` implementation manually, you can alternatively use [strum](https://github.com/Peternator7/strum) crate to automatically generate it. `strum` provides several useful features, making it a generally recommended choice. See [econf/examples/strum.rs](https://github.com/YushiOMOTE/econf/tree/master/econf/examples/strum.rs) for example code. ```rust use econf::LoadEnv; #[derive(Debug, strum::EnumString, LoadEnv)] #[strum(serialize_all = "kebab-case")] enum AuthMode { ApiKey, BasicAuth, #[strum(ascii_case_insensitive)] BearerToken, #[strum(serialize = "oauth", serialize = "OAuth")] OAuth, JWT, } ``` ## Nesting Nested structs are supported. ```rust #[derive(LoadEnv)] struct A { v1: usize, v2: B, } #[derive(LoadEnv)] struct B { v1: usize, v2: usize, } let a = A { v1: 1, v2: B { v1: 2, v2: 3, }, }; let a = econf::load(a, "PREFIX"); ``` In this example, * `PREFIX_V1` is loaded to `a.v1` * `PREFIX_V2_V1` is loaded to `a.v2.v1` * `PREFIX_V2_V2` is loaded to `a.v2.v2` Fields in child structs can be specified by chaining the field names with `_` as a separator. However, there're cases that names conflict. For example, ```rust #[derive(LoadEnv)] struct A { v2_v1: usize, v2: B, } #[derive(LoadEnv)] struct B { v1: usize, v2: usize, } let a = A { v2_v1: 1, v2: B { v1: 2, v2: 3, }, }; let a = econf::load(a, "PREFIX"); ``` Here `PREFIX_V2_V1` corresponds to both `a.v2_v1` and `a.v2.v1`. In this case, `econf` prints warning through [`log facade`](https://docs.rs/log/latest/log/) and the value is loaded to both `a.v2_v1` and `a.v2.v1`. ## Skipping fields Fields that do not implement LoadEnv or simply should not be loaded by econf can be skipped by adding the `#[econf(skip)]` helper attribute: ```rust #[derive(LoadEnv)] struct A { x: bool, #[econf(skip)] y: u64, // will not be loaded by econf } ``` ## Renaming fields Load a field with the given name instead of its Rust's field name. This is helpful if the environment variable name and Rust's field name don't match: ```rust #[derive(LoadEnv)] struct A { x: bool, #[econf(rename = "ANOTHER_Y")] y: u64, // will be loaded from an environment variable `ANOTHER_Y` } ``` License: MIT