confroid 0.0.4

The n+1-st config reader for your environment-based configs.
Documentation
//! Public helper macros for teaching confroid about custom scalar types.

/// Internal: implement [`FromEnv`](crate::FromEnv) for a type via its
/// [`FromStr`](core::str::FromStr). Not part of the public API.
#[doc(hidden)]
#[macro_export]
macro_rules! __impl_from_env_via_from_str {
    ($ty:ty) => {
        impl $crate::FromEnv for $ty {
            fn from_env(ctx: &$crate::Ctx, env: &$crate::Env) -> $crate::Result<Self> {
                match env.get(&ctx.var) {
                    ::core::option::Option::None => {
                        ::core::result::Result::Err($crate::ConfroidError::EnvVarNotFound {
                            var_name: ctx.var.clone(),
                            field: ctx.path.clone(),
                        })
                    }
                    ::core::option::Option::Some(raw) => {
                        raw.parse::<$ty>()
                            .map_err(|e| $crate::ConfroidError::EnvVarInvalid {
                                var_name: ctx.var.clone(),
                                field: ctx.path.clone(),
                                value: ::std::string::ToString::to_string(raw),
                                parser_error: ::std::boxed::Box::new(e),
                            })
                    }
                }
            }

            fn is_present(ctx: &$crate::Ctx, env: &$crate::Env) -> bool {
                env.get(&ctx.var).is_some()
            }
        }
    };
}

/// Register one or more types as confroid scalars, read from a single
/// environment variable via their [`FromStr`](core::str::FromStr) impl.
///
/// Prefer [`ConfigValue`](crate::ConfigValue) when the type definition can be
/// annotated directly. This macro remains useful for types where adding a
/// derive is inconvenient.
///
/// This is the escape hatch for custom scalar types (enums, newtypes, …) that a
/// blanket `impl` cannot cover. Each type must implement `FromStr`; its error
/// must implement [`Error`](std::error::Error), `Send`, and `Sync`, and must be
/// `'static` so confroid can preserve it as an error source.
///
/// ```
/// use std::str::FromStr;
///
/// #[derive(Debug, PartialEq)]
/// enum Mode { Standalone, Cluster }
///
/// impl FromStr for Mode {
///     type Err = std::io::Error;
///     fn from_str(s: &str) -> Result<Self, Self::Err> {
///         match s {
///             "standalone" => Ok(Mode::Standalone),
///             "cluster" => Ok(Mode::Cluster),
///             other => Err(std::io::Error::new(
///                 std::io::ErrorKind::InvalidInput,
///                 format!("unknown mode `{other}`"),
///             )),
///         }
///     }
/// }
///
/// confroid::from_str_scalar!(Mode);
///
/// #[derive(confroid::Config)]
/// struct Config {
///     mode: Mode,
/// }
///
/// let config: Config = confroid::from_pairs([("MODE", "cluster")]).unwrap();
/// assert_eq!(config.mode, Mode::Cluster);
/// ```
#[cfg(not(feature = "docs"))]
#[macro_export]
macro_rules! from_str_scalar {
    ($($ty:ty),+ $(,)?) => {
        $( $crate::__impl_from_env_via_from_str!($ty); )+
    };
}

/// Register one or more types as confroid scalars (see the `docs`-disabled
/// variant for details). With the `docs` feature this also implements
/// [`Documented`](crate::Documented), so the type can appear in a config that
/// generates documentation. In that case the type should also implement
/// `Display` if it is used with a `default`/`example`.
#[cfg(feature = "docs")]
#[macro_export]
macro_rules! from_str_scalar {
    ($($ty:ty),+ $(,)?) => {
        $(
            $crate::__impl_from_env_via_from_str!($ty);

            impl $crate::Documented for $ty {
                fn doc_fields() -> ::core::option::Option<::std::vec::Vec<$crate::FieldDoc>> {
                    ::core::option::Option::None
                }
            }
        )+
    };
}