mirror of
https://github.com/OMGeeky/confique.git
synced 2026-01-04 10:40:26 +01:00
Change API of parse_env functions
I think this is an overall improvement. A few things done here: - They are available as `::env::parse` instead of `::env_utils`. The `env` module gets functions of its own soon enough. - Renamed functions to be shorter: `list_by_sep`, `list_by_comma`, ... - The docs were adjusted. One example is enough. And the functions with a fixed separator don't need the full docs again. That way we can avoid the `paste` dependency. - Functions now always return `Result<C, _>`. While the previous version was slightly more flexible, I don't think anyone would benefit from that flexibility (as `parse_env` requires `Result<_, _>` anyway) and this way it's a bit clearer what the function does, especially for those who don't know the nifty `FromIterator for Result` impl. - The example was adjusted accordingly. I also changed the names to obviously dummy names as I didn't know the existing names and don't want to spend time investigating whether I want their names in my code base :D
This commit is contained in:
@@ -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 }
|
||||
|
||||
@@ -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<PathBuf>,
|
||||
#[config(
|
||||
env = "PORTS",
|
||||
parse_env = to_collection_by_semicolon
|
||||
)]
|
||||
|
||||
#[config(env = "PORTS", parse_env = confique::env::parse::list_by_comma)]
|
||||
ports: Vec<u16>,
|
||||
#[config(
|
||||
env = "NAMES",
|
||||
parse_env = to_collection_by_char_separator::<'|', _, _>
|
||||
)]
|
||||
|
||||
#[config(env = "NAMES", parse_env = confique::env::parse::list_by_sep::<'|', _, _>)]
|
||||
names: Vec<String>,
|
||||
#[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<Format>,
|
||||
}
|
||||
|
||||
@@ -45,6 +30,7 @@ enum Format {
|
||||
Yaml,
|
||||
}
|
||||
|
||||
/// Example custom parser.
|
||||
fn parse_formats(input: &str) -> Result<Vec<Format>, Infallible> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
@@ -66,11 +52,8 @@ fn parse_formats(input: &str) -> Result<Vec<Format>, Infallible> {
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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");
|
||||
|
||||
|
||||
122
src/env.rs
122
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<u16>
|
||||
/// #[config(env = "PORTS", parse_env = confique::env::parse::list_by_sep::<',', _, _>)]
|
||||
/// ports: Vec<u16>,
|
||||
/// }
|
||||
///
|
||||
/// 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::path::PathBuf>,
|
||||
/// }
|
||||
///
|
||||
/// 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<const SEP: char, T, C>(input: &str) -> Result<C, <T as FromStr>::Err>
|
||||
where
|
||||
T: FromStr,
|
||||
C: FromIterator<Result<T, <T as FromStr>::Err>>,
|
||||
>(
|
||||
input: &str,
|
||||
) -> C {
|
||||
input.split(SEPARATOR.to_owned()).map(T::from_str).collect()
|
||||
C: FromIterator<T>,
|
||||
{
|
||||
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!([<to_collection_by_ $symbol_name>],))]
|
||||
/// )]
|
||||
/// ports: Vec<u16>
|
||||
/// }
|
||||
///
|
||||
#[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!([<to_collection_by_ $symbol_name>],))]
|
||||
/// )]
|
||||
/// paths: std::collections::HashSet<std::path::PathBuf>,
|
||||
/// }
|
||||
///
|
||||
#[doc = concat!("std::env::set_var(\"PATHS\", \"/bin", $symbol, "/user/bin", $symbol, "/home/user/.cargo/bin", "\");")]
|
||||
/// println!("{:#?}", Conf::builder().env().load().unwrap())
|
||||
/// ```
|
||||
pub fn [<to_collection_by_ $symbol_name>]<T: FromStr, C: FromIterator<Result<T, <T as FromStr>::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<T, C>(input: &str) -> Result<C, <T as FromStr>::Err>
|
||||
where
|
||||
T: FromStr,
|
||||
C: FromIterator<T>,
|
||||
{
|
||||
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, ' ');
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user