de_env/de/mod.rs
1#![allow(clippy::needless_doctest_main)]
2
3use serde::{de::value::MapDeserializer, Deserialize};
4
5use crate::{Error, Result};
6
7use self::{key::Key, value::Value};
8
9mod key;
10mod util;
11mod value;
12
13/// Deserialize an instance of `T` from the environment variables of the current process.
14///
15/// # Example
16///
17/// Assuming we have a `TIMEOUT` and `HOST` environment variable:
18///
19/// ```rust
20/// #[derive(serde::Deserialize, Debug)]
21/// #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
22/// struct Config {
23/// timeout: u16,
24/// host: std::net::IpAddr,
25/// }
26///
27/// # std::env::set_var("TIMEOUT", "12");
28/// # std::env::set_var("HOST", "127.0.0.1");
29/// let config: Config = de_env::from_env()?;
30///
31/// println!("{config:#?}");
32/// # Ok::<(), de_env::Error>(())
33/// ```
34///
35/// # Errors
36/// This conversion can fail if trying to deserialize [unsupported types], or if `T`'s
37/// implementation of `Deserialize` decides that something is wrong with the data.
38///
39/// [unsupported types]: crate#unsupported-types
40pub fn from_env<'de, T>() -> Result<T>
41where
42 T: Deserialize<'de>,
43{
44 from_iter(std::env::vars_os())
45}
46
47/// Deserialize an instance of `T` from the environment variables of the current process with the
48/// specified prefix.
49///
50/// # Example
51///
52/// Assuming we have a `PREFIX_TIMEOUT` and `PREFIX_HOST` environment variable:
53///
54/// ```rust
55/// #[derive(serde::Deserialize, Debug)]
56/// #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
57/// struct Config {
58/// timeout: u16,
59/// host: std::net::IpAddr,
60/// }
61///
62/// # std::env::set_var("PREFIX_TIMEOUT", "12");
63/// # std::env::set_var("PREFIX_HOST", "127.0.0.1");
64/// let config: Config = de_env::from_env_prefixed("PREFIX_")?;
65///
66/// println!("{config:#?}");
67/// # Ok::<(), de_env::Error>(())
68/// ```
69///
70/// # Errors
71/// This conversion can fail if trying to deserialize [unsupported types], or if `T`'s
72/// implementation of `Deserialize` decides that something is wrong with the data.
73///
74/// [unsupported types]: crate#unsupported-types
75pub fn from_env_prefixed<'de, T>(prefix: &str) -> Result<T>
76where
77 T: Deserialize<'de>,
78{
79 from_iter(
80 std::env::vars_os().filter_map(|(name, value)| match name.to_str() {
81 Some(name) => Some((std::ffi::OsString::from(name.strip_prefix(prefix)?), value)),
82 _ => None,
83 }),
84 )
85}
86
87/// Deserialize an instance of `T` from an iterator of key-value tuple.
88///
89/// This is intended to be used when you wish to perform some operation, such as mapping or
90/// filtering, on the iterator returned by [`std::env::vars()`] or [`std::env::vars_os()`].
91///
92/// # Example
93/// ```rust
94/// #[derive(serde::Deserialize, Debug)]
95/// #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
96/// struct Config {
97/// timeout: u16,
98/// host: std::net::IpAddr,
99/// }
100///
101/// # std::env::set_var("TIMEOUT", "12");
102/// # std::env::set_var("HOST", "127.0.0.1");
103/// let vars = std::env::vars_os().filter(|(name, _value)| name == "TIMEOUT" || name == "HOST");
104///
105/// let config: Config = de_env::from_iter(vars)?;
106///
107/// println!("{config:#?}");
108/// # Ok::<(), de_env::Error>(())
109/// ```
110///
111/// # Errors
112/// This conversion can fail if trying to deserialize [unsupported types], or if `T`'s
113/// implementation of `Deserialize` decides that something is wrong with the data.
114///
115/// # Iterator Items
116/// The items must be a tuple of length 2, where the first element is the key and the second the
117/// value. The elements may be of the following types:
118/// - [`OsString`](std::ffi::OsString)
119/// - [`String`]
120/// - [`Cow<str>`](std::borrow::Cow)
121/// - [`Cow<OsStr>`](std::borrow::Cow)
122/// - [`&OsStr`](std::ffi::OsStr)
123/// - [`&str`]
124///
125/// [unsupported types]: crate#unsupported-types
126pub fn from_iter<'de, T>(
127 iter: impl Iterator<Item = (impl Into<Key<'de>>, impl Into<Value<'de>>)>,
128) -> Result<T>
129where
130 T: Deserialize<'de>,
131{
132 let mut deserializer =
133 EnvDeserializer::from_iter(iter.map(|(key, value)| (key.into(), value.into())));
134
135 T::deserialize(&mut deserializer)
136}
137
138struct EnvDeserializer<'de, I: Iterator<Item = (Key<'de>, Value<'de>)>>(
139 MapDeserializer<'de, I, Error>,
140);
141
142impl<'de, I> EnvDeserializer<'de, I>
143where
144 I: Iterator<Item = (Key<'de>, Value<'de>)>,
145{
146 pub fn from_iter(iter: I) -> Self {
147 Self(MapDeserializer::new(iter))
148 }
149}
150
151impl<'de, 'a, I> serde::de::Deserializer<'de> for &'a mut EnvDeserializer<'de, I>
152where
153 I: Iterator<Item = (Key<'de>, Value<'de>)>,
154{
155 type Error = Error;
156
157 fn deserialize_struct<V>(
158 self,
159 _name: &'static str,
160 _fields: &'static [&'static str],
161 visitor: V,
162 ) -> Result<V::Value>
163 where
164 V: serde::de::Visitor<'de>,
165 {
166 visitor.visit_map(&mut self.0)
167 }
168
169 fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
170 where
171 V: serde::de::Visitor<'de>,
172 {
173 visitor.visit_newtype_struct(self)
174 }
175
176 util::unsupported_types! {
177 bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
178 bytes byte_buf option unit unit_struct tuple
179 any tuple_struct identifier enum map seq ignored_any
180 }
181}