diff --git a/Cargo.lock b/Cargo.lock index 246104a..0add5a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,6 +31,7 @@ dependencies = [ "serde", "serde_yaml", "simple_logger", + "strum", ] [[package]] @@ -54,6 +55,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "humantime" version = "2.1.0" @@ -133,6 +140,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "ryu" version = "1.0.10" @@ -184,6 +197,28 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "syn" version = "2.0.77" diff --git a/README.md b/README.md index 0f1d6c0..3d9c63d 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,26 @@ There are some existing crates that provide similar features but `econf` is uniq * 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. diff --git a/econf-derive/src/lib.rs b/econf-derive/src/lib.rs index f459f18..76e3aeb 100644 --- a/econf-derive/src/lib.rs +++ b/econf-derive/src/lib.rs @@ -113,21 +113,8 @@ fn content(name: &Ident, data: &Data) -> TokenStream2 { Fields::Unit => {} }); - let enums0 = data.variants.iter().map(|_| &name); - let enums1 = data.variants.iter().map(|f| &f.ident); - let enums2 = data.variants.iter().map(|f| &f.ident); - quote! { - match String::default().load(path, loader).as_ref() { - #( - stringify!(#enums1) => #enums0::#enums2, - )* - "" => self, - x => { - error!("econf: couldn't find variant: {}", x); - self - } - } + loader.load_from_str(self, path) } } Data::Union(_) => unimplemented!("Unions are not supported"), diff --git a/econf/Cargo.toml b/econf/Cargo.toml index 1bfbca5..dc7a3dc 100644 --- a/econf/Cargo.toml +++ b/econf/Cargo.toml @@ -19,3 +19,4 @@ humantime = "2.1" [dev-dependencies] simple_logger = "5.0" +strum = { version = "0.26", features = ["derive"] } diff --git a/econf/examples/alltypes.rs b/econf/examples/alltypes.rs index b1a636b..43c6dff 100644 --- a/econf/examples/alltypes.rs +++ b/econf/examples/alltypes.rs @@ -2,7 +2,7 @@ use econf::LoadEnv; use log::*; use std::collections::{HashMap, HashSet}; -#[derive(Debug, LoadEnv)] +#[derive(Debug, strum::EnumString, LoadEnv)] enum X { V1, V2, diff --git a/econf/examples/sample.rs b/econf/examples/sample.rs index 857d80e..2a5aeec 100644 --- a/econf/examples/sample.rs +++ b/econf/examples/sample.rs @@ -2,7 +2,7 @@ use econf::LoadEnv; use log::*; use std::collections::HashMap; -#[derive(Debug, LoadEnv)] +#[derive(Debug, strum::EnumString, LoadEnv)] enum X { V1, V2, diff --git a/econf/examples/strum.rs b/econf/examples/strum.rs new file mode 100644 index 0000000..c4e5d7f --- /dev/null +++ b/econf/examples/strum.rs @@ -0,0 +1,34 @@ +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, +} + +#[derive(Debug, LoadEnv)] +struct Config { + auth_mode: AuthMode, + data: String, + passwd: String, +} + +fn main() { + simple_logger::init().unwrap(); + + let c = Config { + auth_mode: AuthMode::ApiKey, + data: "foo".into(), + passwd: "bar".into(), + }; + println!("Before loading env: {c:?}"); + + let c = econf::load(c, "app"); + println!("After loading env: {c:?}"); +} diff --git a/econf/src/lib.rs b/econf/src/lib.rs index dc325d0..ca7cabc 100644 --- a/econf/src/lib.rs +++ b/econf/src/lib.rs @@ -66,6 +66,26 @@ //! * 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. +//! +//! ``` +//! 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.