//! jealousy is a wrapper crate around the [envy] crate.
//! It doesn't add much on top of it, but rather gives a simple Trait that can be implemented
//! or a derive Macro, for even easier use.
//!
//! This crate additionally adds logging around envy, using the log crate.
//! This makes it easier to debug in production environments.
//!
//! ## Usage
//!
//! ### Using the trait
//! ```
//! use jealousy::FromEnv;
//! use serde::Deserialize;
//!
//! #[derive(Deserialize)]
//! struct Config {
//!     some_var: String
//! }
//!
//! // no need for changing the default impl, unless you need different
//! // logging logic.
//! impl FromEnv for Config {
//!     const PREFIX: &'static str = "APP";
//! }
//! ```
//!
//! ### Using the derive Macro
//! To use the derive macro, you have to enable the `derive` feature
//!
//! ```ignore
//! use jealousy::FromEnv;
//! use serde::Deserialize;
//!
//! #[derive(Deserialize, FromEnv)]
//! #[from_env(prefix = "APP")]
//! struct Config {
//!     some_var: String
//! }
//! ```
use log::*;
use serde::de::DeserializeOwned;

pub use envy::Error;
#[cfg(feature = "derive")]
pub use jealousy_derive::FromEnv;

/// Allow any type to be Deserialized from the environment using [envy::prefixed]
///
/// The following should be sufficient for most cases:
/// ```
/// use serde::Deserialize;
/// use jealousy::FromEnv;
///
/// #[derive(Deserialize)]
/// struct Config {
///    some_field: String
/// }
///
/// // this can now be parsed from CONFIG_SOME_FIELD
/// impl FromEnv for Config {
///    const PREFIX: &'static str = "CONFIG";
/// }
/// ```
pub trait FromEnv
where
    Self: DeserializeOwned + Sized,
{
    /// The prefix used to parse this type from the environment
    /// this will later be transformed into `PREFIX_`
    const PREFIX: &'static str;

    /// Deserialize the type from the environment with the prefix from
    /// [FromEnv::PREFIX]
    ///
    /// This function is essentially a wrapper around envy,
    /// so that we can do loads of logging
    ///
    /// For most types the default implementation should be sufficient
    fn from_env() -> envy::Result<Self> {
        info!("Parsing config for prefix {}", Self::PREFIX);
        match envy::prefixed(&format!("{}_", Self::PREFIX)).from_env() {
            Ok(val) => {
                info!("Config for prefix {} parsed successfully", Self::PREFIX);
                Ok(val)
            }
            Err(err) => {
                match &err {
                    envy::Error::MissingValue(field) => {
                        error!("Missing Value {}_{}", Self::PREFIX, field.to_uppercase())
                    }
                    envy::Error::Custom(err) => {
                        error!(
                            "An unexpected Error happened while parsing prefix {}: {err}",
                            Self::PREFIX
                        )
                    }
                }
                Err(err)
            }
        }
    }
}
