jealousy/
lib.rs

1//! jealousy is a wrapper crate around the [envy] crate.
2//! It doesn't add much on top of it, but rather gives a simple Trait that can be implemented
3//! or a derive Macro, for even easier use.
4//!
5//! This crate additionally adds logging around envy, using the log crate.
6//! This makes it easier to debug in production environments.
7//!
8//! ## Usage
9//!
10//! ### Using the trait
11//! ```
12//! use jealousy::FromEnv;
13//! use serde::Deserialize;
14//!
15//! #[derive(Deserialize)]
16//! struct Config {
17//!     some_var: String
18//! }
19//!
20//! // no need for changing the default impl, unless you need different
21//! // logging logic.
22//! impl FromEnv for Config {
23//!     const PREFIX: &'static str = "APP";
24//! }
25//! ```
26//!
27//! ### Using the derive Macro
28//! To use the derive macro, you have to enable the `derive` feature
29//!
30//! ```ignore
31//! use jealousy::FromEnv;
32//! use serde::Deserialize;
33//!
34//! #[derive(Deserialize, FromEnv)]
35//! #[from_env(prefix = "APP")]
36//! struct Config {
37//!     some_var: String
38//! }
39//! ```
40use log::*;
41use serde::de::DeserializeOwned;
42
43pub use envy::Error;
44#[cfg(feature = "derive")]
45pub use jealousy_derive::FromEnv;
46
47/// Allow any type to be Deserialized from the environment using [envy::prefixed]
48///
49/// The following should be sufficient for most cases:
50/// ```
51/// use serde::Deserialize;
52/// use jealousy::FromEnv;
53///
54/// #[derive(Deserialize)]
55/// struct Config {
56///    some_field: String
57/// }
58///
59/// // this can now be parsed from CONFIG_SOME_FIELD
60/// impl FromEnv for Config {
61///    const PREFIX: &'static str = "CONFIG";
62/// }
63/// ```
64pub trait FromEnv
65where
66    Self: DeserializeOwned + Sized,
67{
68    /// The prefix used to parse this type from the environment
69    /// this will later be transformed into `PREFIX_`
70    const PREFIX: &'static str;
71
72    /// Deserialize the type from the environment with the prefix from
73    /// [FromEnv::PREFIX]
74    ///
75    /// This function is essentially a wrapper around envy,
76    /// so that we can do loads of logging
77    ///
78    /// For most types the default implementation should be sufficient
79    fn from_env() -> envy::Result<Self> {
80        info!("Parsing config for prefix {}", Self::PREFIX);
81        match envy::prefixed(&format!("{}_", Self::PREFIX)).from_env() {
82            Ok(val) => {
83                info!("Config for prefix {} parsed successfully", Self::PREFIX);
84                Ok(val)
85            }
86            Err(err) => {
87                match &err {
88                    envy::Error::MissingValue(field) => {
89                        error!("Missing Value {}_{}", Self::PREFIX, field.to_uppercase())
90                    }
91                    envy::Error::Custom(err) => {
92                        error!(
93                            "An unexpected Error happened while parsing prefix {}: {err}",
94                            Self::PREFIX
95                        )
96                    }
97                }
98                Err(err)
99            }
100        }
101    }
102}