From 4ec2bd603548a56fbf8c1a54104556b8cdfc4694 Mon Sep 17 00:00:00 2001 From: simon-an <26556185+simon-an@users.noreply.github.com> Date: Fri, 3 Dec 2021 16:13:50 +0100 Subject: [PATCH] feat: env contains list of strings Signed-off-by: simon-an <26556185+simon-an@users.noreply.github.com> --- examples/env-list/main.rs | 25 +++++++++++++++++++++++++ src/env.rs | 14 ++++++++++++++ tests/env.rs | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 examples/env-list/main.rs diff --git a/examples/env-list/main.rs b/examples/env-list/main.rs new file mode 100644 index 00000000..f567419b --- /dev/null +++ b/examples/env-list/main.rs @@ -0,0 +1,25 @@ +use config::Config; +#[derive(Debug, Default, serde_derive::Deserialize, PartialEq)] +struct AppConfig { + list: Vec, +} + +fn main() { + std::env::set_var("APP_LIST", "Hello World"); + + let config = Config::builder() + .add_source( + config::Environment::with_prefix("APP") + .try_parsing(true) + .separator("_") + .list_separator(" "), + ) + .build() + .unwrap(); + + let app: AppConfig = config.try_deserialize().unwrap(); + + assert_eq!(app.list, vec![String::from("Hello"), String::from("World")]); + + std::env::remove_var("APP_LIST"); +} diff --git a/src/env.rs b/src/env.rs index 854b9a89..8056c166 100644 --- a/src/env.rs +++ b/src/env.rs @@ -21,6 +21,9 @@ pub struct Environment { /// an environment key of `REDIS_PASSWORD` to match. separator: Option, + /// Optional character sequence that separates each env value into a vector. only works when try_parsing is set to true + list_separator: Option, + /// Ignore empty env values (treat as unset). ignore_empty: bool, @@ -87,6 +90,11 @@ impl Environment { self } + pub fn list_separator(mut self, s: &str) -> Self { + self.list_separator = Some(s.into()); + self + } + pub fn ignore_empty(mut self, ignore: bool) -> Self { self.ignore_empty = ignore; self @@ -155,6 +163,12 @@ impl Source for Environment { ValueKind::I64(parsed) } else if let Ok(parsed) = value.parse::() { ValueKind::Float(parsed) + } else if let Some(separator) = &self.list_separator { + let v: Vec = value + .split(separator) + .map(|s| Value::new(Some(&uri), ValueKind::String(s.to_string()))) + .collect(); + ValueKind::Array(v) } else { ValueKind::String(value) } diff --git a/tests/env.rs b/tests/env.rs index 1f7a263b..ee447fc3 100644 --- a/tests/env.rs +++ b/tests/env.rs @@ -412,6 +412,42 @@ fn test_parse_string() { env::remove_var("STRING_VAL"); } +#[test] +fn test_parse_string_list() { + // using a struct in an enum here to make serde use `deserialize_any` + #[derive(Deserialize, Debug)] + #[serde(tag = "tag")] + enum TestListEnum { + StringList(TestList), + } + + #[derive(Deserialize, Debug)] + struct TestList { + string_list: Vec, + } + + env::set_var("STRING_LIST", "test string"); + + let environment = Environment::default().try_parsing(true).list_separator(" "); + + let config = Config::builder() + .set_default("tag", "StringList") + .unwrap() + .add_source(environment) + .build() + .unwrap(); + + let config: TestListEnum = config.try_deserialize().unwrap(); + + let test_string = vec![String::from("test"), String::from("string")]; + + match config { + TestListEnum::StringList(TestList { string_list }) => assert_eq!(test_string, string_list), + } + + env::remove_var("STRING_LIST"); +} + #[test] fn test_parse_off_string() { // using a struct in an enum here to make serde use `deserialize_any`