diff --git a/Cargo.toml b/Cargo.toml index 9581475..88988a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,6 @@ yaml = ["serde_yaml"] [dependencies] confique-macro = { version = "=0.0.5", path = "macro" } json5 = { version = "0.4.1", optional = true } -paste = "1.0.9" serde = { version = "1", features = ["derive"] } serde_yaml = { version = "0.8", optional = true } toml = { version = "0.5", optional = true } diff --git a/examples/parse_env.rs b/examples/parse_env.rs index 266a493..11af4b0 100644 --- a/examples/parse_env.rs +++ b/examples/parse_env.rs @@ -1,39 +1,24 @@ #![allow(dead_code)] -use confique::{ - env_utils::{ - to_collection_by_char_separator, to_collection_by_comma, to_collection_by_semicolon, - }, - Config, -}; +use confique::Config; use std::{collections::HashSet, num::NonZeroU64, path::PathBuf, str::FromStr, convert::Infallible}; + #[derive(Debug, Config)] struct Conf { - #[config( - env = "PATHS", - parse_env = to_collection_by_comma - )] + #[config(env = "PATHS", parse_env = confique::env::parse::list_by_colon)] paths: HashSet, - #[config( - env = "PORTS", - parse_env = to_collection_by_semicolon - )] + + #[config(env = "PORTS", parse_env = confique::env::parse::list_by_comma)] ports: Vec, - #[config( - env = "NAMES", - parse_env = to_collection_by_char_separator::<'|', _, _> - )] + + #[config(env = "NAMES", parse_env = confique::env::parse::list_by_sep::<'|', _, _>)] names: Vec, - #[config( - env = "TIMEOUT", - parse_env = NonZeroU64::from_str, - )] + + #[config(env = "TIMEOUT", parse_env = NonZeroU64::from_str)] timeout_seconds: NonZeroU64, - #[config( - env = "FORMATS", - parse_env = parse_formats, - )] + + #[config(env = "FORMATS", parse_env = parse_formats)] formats: Vec, } @@ -45,6 +30,7 @@ enum Format { Yaml, } +/// Example custom parser. fn parse_formats(input: &str) -> Result, Infallible> { let mut result = Vec::new(); @@ -66,11 +52,8 @@ fn parse_formats(input: &str) -> Result, Infallible> { fn main() -> Result<(), Box> { std::env::set_var("PATHS", "/bin/ls,/usr/local/bin,/usr/bin/ls"); - std::env::set_var("PORTS", "8080;8888;8000"); - std::env::set_var( - "NAMES", - "Mervinc Harmon|Alfreda Valenzuela|Arlen Cabrera|Damon Rice|Willie Schwartz", - ); + std::env::set_var("PORTS", "8080,8888,8000"); + std::env::set_var("NAMES", "Alex|Peter|Mary"); std::env::set_var("TIMEOUT", "100"); std::env::set_var("FORMATS", "json5,yaml;.env"); diff --git a/src/env.rs b/src/env.rs index 36c6648..4580661 100644 --- a/src/env.rs +++ b/src/env.rs @@ -11,6 +11,7 @@ use serde::de::IntoDeserializer; /// module. Gets converted into `ErrorKind::EnvDeserialization` before reaching /// the real public API. #[derive(PartialEq, Eq)] +#[doc(hidden)] pub struct DeError(pub(crate) String); impl std::error::Error for DeError {} @@ -38,6 +39,7 @@ impl serde::de::Error for DeError { /// Deserializer type. Semantically private (see `DeError`). +#[doc(hidden)] pub struct Deserializer { value: String, } @@ -167,104 +169,60 @@ mod tests { } } -/// This module contains helper methods to simplify configuration via environment variables -pub mod env_utils { +/// Functions for the `#[config(parse_env = ...)]` attribute. +pub mod parse { use std::str::FromStr; - /// Helper function to parse any type that implements [`std::str::FromStr`] - /// into a collection that implements [`std::iter::FromIterator`], spliting original string by - /// const char separator. + /// Splits the environment variable by separator `SEP`, parses each element + /// with [`FromStr`] and collects everything via [`FromIterator`]. + /// + /// To avoid having to specify the separator via `::<>` syntax, see the + /// other functions in this module. + /// + /// [`FromStr`]: std::str::FromStr + /// [`FromIterator`]: std::iter::FromIterator + /// + /// + /// # Example /// - /// # To Vec /// ``` - /// use crate::confique::Config; + /// use confique::Config; + /// /// #[derive(Debug, confique::Config)] /// struct Conf { - /// #[config( - /// env = "PORTS", - /// parse_env = confique::env_utils::to_collection_by_char_separator::<',', _, _> - /// )] - /// ports: Vec + /// #[config(env = "PORTS", parse_env = confique::env::parse::list_by_sep::<',', _, _>)] + /// ports: Vec, /// } /// /// std::env::set_var("PORTS", "8080,8000,8888"); - /// println!("{:?}", Conf::builder().env().load().unwrap()) + /// let conf = Conf::builder().env().load()?; + /// assert_eq!(conf.ports, vec![8080, 8000, 8888]); + /// # Ok::<_, confique::Error>(()) /// ``` - /// - /// # To HashSet - /// ``` - /// use crate::confique::Config; - /// #[derive(Debug, confique::Config)] - /// struct Conf { - /// #[config( - /// env = "PATHS", - /// parse_env = confique::env_utils::to_collection_by_char_separator::<';', _, _> - /// )] - /// paths: std::collections::HashSet, - /// } - /// - /// std::env::set_var("PATHS", "/bin;/user/bin;/home/user/.cargo/bin"); - /// println!("{:?}", Conf::builder().env().load().unwrap()) - /// ``` - pub fn to_collection_by_char_separator< - const SEPARATOR: char, + pub fn list_by_sep(input: &str) -> Result::Err> + where T: FromStr, - C: FromIterator::Err>>, - >( - input: &str, - ) -> C { - input.split(SEPARATOR.to_owned()).map(T::from_str).collect() + C: FromIterator, + { + input.split(SEP).map(T::from_str).collect() } + macro_rules! specify_fn_wrapper { - ($symbol_name:ident, $symbol:tt) => { - ::paste::paste! { - /// Helper function to parse any type that implements [`std::str::FromStr`] - /// into a collection that implements [`std::iter::FromIterator`], spliting original string by - #[doc = stringify!($symbol_name)] - /// . - /// - /// # To Vec - /// ``` - /// use crate::confique::Config; - /// #[derive(Debug, confique::Config)] - /// struct Conf { - /// #[config( - /// env = "PORTS", - #[doc = concat!(" parse_env = confique::env_utils::", stringify!([],))] - /// )] - /// ports: Vec - /// } - /// - #[doc = concat!("std::env::set_var(\"PORTS\", \"8080", $symbol, "8000", $symbol, "8888", "\");")] - /// println!("{:#?}", Conf::builder().env().load().unwrap()) - /// ``` - /// - /// # To HashSet - /// ``` - /// use crate::confique::Config; - /// #[derive(Debug, confique::Config)] - /// struct Conf { - /// #[config( - /// env = "PATHS", - #[doc = concat!(" parse_env = confique::env_utils::", stringify!([],))] - /// )] - /// paths: std::collections::HashSet, - /// } - /// - #[doc = concat!("std::env::set_var(\"PATHS\", \"/bin", $symbol, "/user/bin", $symbol, "/home/user/.cargo/bin", "\");")] - /// println!("{:#?}", Conf::builder().env().load().unwrap()) - /// ``` - pub fn []::Err>>>( - input: &str, - ) -> C { - to_collection_by_char_separator::<$symbol, _, _>(input) - } + ($fn_name:ident, $sep:literal) => { + #[doc = concat!("Like [`list_by_sep`] with `", $sep, "` separator.")] + pub fn $fn_name(input: &str) -> Result::Err> + where + T: FromStr, + C: FromIterator, + { + list_by_sep::<$sep, _, _>(input) } } } - specify_fn_wrapper!(comma, ','); - specify_fn_wrapper!(semicolon, ';'); - specify_fn_wrapper!(space, ' '); + specify_fn_wrapper!(list_by_comma, ','); + specify_fn_wrapper!(list_by_semicolon, ';'); + specify_fn_wrapper!(list_by_colon, ':'); + specify_fn_wrapper!(list_by_space, ' '); } diff --git a/src/lib.rs b/src/lib.rs index 31cdce9..f09873d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -175,8 +175,7 @@ use serde::Deserialize; pub mod internal; mod builder; -mod env; -pub use env::env_utils; +pub mod env; mod error; pub mod meta;