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}