Skip to main content

confik/
std_impls.rs

1//! Implementations of [`Configuration`](crate::Configuration) for standard library types.
2
3use std::{
4    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
5    ffi::OsString,
6    fmt::Display,
7    hash::{BuildHasher, Hash},
8    marker::PhantomData,
9    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
10    path::PathBuf,
11    sync::atomic::{
12        AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32,
13        AtomicU64, AtomicU8, AtomicUsize,
14    },
15    time::{Duration, SystemTime},
16};
17
18use serde::{de::DeserializeOwned, Deserialize};
19
20use crate::{
21    helpers::{
22        BuilderOf, KeyedContainer, KeyedContainerBuilder, TargetOf, UnkeyedContainerBuilder,
23    },
24    Configuration, ConfigurationBuilder, Error, MissingValue, UnexpectedSecret,
25};
26
27/// Convenience macro for the large number of foreign library types to implement the
28/// [`Configuration`] using an [`Option`] as their [`ConfigurationBuilder`].
29macro_rules! impl_multi_source_via_option {
30    ($type:ty) => {
31        impl Configuration for $type {
32            type Builder = Option<Self>;
33        }
34    };
35
36    ($($type:ty),* $(,)?) => {
37        $(
38            impl_multi_source_via_option! { $type }
39        )*
40    };
41}
42
43impl_multi_source_via_option! {
44    // Signed integers
45    i8, i16, i32, i64, i128, isize,
46
47    // Unsigned integers
48    u8, u16, u32, u64, u128, usize,
49
50    // Floats
51    f32, f64,
52
53    // Networking types
54    SocketAddr, SocketAddrV4, SocketAddrV6, IpAddr, Ipv4Addr, Ipv6Addr,
55
56    // Time
57    Duration, SystemTime,
58
59    // Other standard types
60    String, OsString, PathBuf, char, bool,
61
62    // Atomic types
63    AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize,
64    AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize,
65    AtomicBool,
66}
67
68// Containers
69impl<T> Configuration for Vec<T>
70where
71    T: Configuration,
72    BuilderOf<T>: 'static,
73{
74    type Builder = UnkeyedContainerBuilder<Vec<BuilderOf<T>>, Self>;
75}
76
77impl<T> Configuration for BTreeSet<T>
78where
79    T: Configuration + Ord,
80    BuilderOf<T>: Ord + 'static,
81{
82    type Builder = UnkeyedContainerBuilder<BTreeSet<BuilderOf<T>>, Self>;
83}
84
85impl<T, S> Configuration for HashSet<T, S>
86where
87    T: Configuration + Eq + Hash,
88    BuilderOf<T>: Hash + Eq + 'static,
89    S: BuildHasher + Default + 'static,
90{
91    type Builder = UnkeyedContainerBuilder<HashSet<BuilderOf<T>, S>, Self>;
92}
93
94impl<K, V> KeyedContainer for BTreeMap<K, V>
95where
96    K: Ord,
97{
98    type Key = K;
99    type Value = V;
100
101    fn insert(&mut self, k: Self::Key, v: Self::Value) {
102        self.insert(k, v);
103    }
104
105    fn remove(&mut self, k: &Self::Key) -> Option<Self::Value> {
106        self.remove(k)
107    }
108}
109
110impl<K, V> Configuration for BTreeMap<K, V>
111where
112    K: Ord + Display + DeserializeOwned + 'static,
113    V: Configuration,
114    BuilderOf<V>: 'static,
115{
116    type Builder = KeyedContainerBuilder<BTreeMap<K, BuilderOf<V>>, Self>;
117}
118
119impl<K, V, S> KeyedContainer for HashMap<K, V, S>
120where
121    K: Hash + Eq,
122    S: BuildHasher + Default,
123{
124    type Key = K;
125    type Value = V;
126
127    fn insert(&mut self, k: Self::Key, v: Self::Value) {
128        self.insert(k, v);
129    }
130
131    fn remove(&mut self, k: &Self::Key) -> Option<Self::Value> {
132        self.remove(k)
133    }
134}
135
136impl<K, V, S> Configuration for HashMap<K, V, S>
137where
138    K: Hash + Eq + Display + DeserializeOwned + 'static,
139    V: Configuration,
140    BuilderOf<V>: 'static,
141    S: Default + BuildHasher + 'static,
142{
143    type Builder = KeyedContainerBuilder<HashMap<K, BuilderOf<V>, S>, Self>;
144}
145
146impl<T, const N: usize> Configuration for [T; N]
147where
148    [BuilderOf<T>; N]: DeserializeOwned + Default,
149    T: Configuration,
150{
151    type Builder = [BuilderOf<T>; N];
152}
153
154impl<T, const N: usize> ConfigurationBuilder for [T; N]
155where
156    Self: DeserializeOwned + Default,
157    T: ConfigurationBuilder,
158{
159    type Target = [TargetOf<T>; N];
160
161    fn merge(self, other: Self) -> Self {
162        let mut iter = other.into_iter();
163        self.map(|us| us.merge(iter.next().unwrap()))
164    }
165
166    fn try_build(self) -> Result<Self::Target, Error> {
167        self.into_iter()
168            .enumerate()
169            .map(|(index, val)| {
170                val.try_build().map_err(|err| match err {
171                    Error::MissingValue(err) => Error::MissingValue(err.prepend(index.to_string())),
172                    err => err,
173                })
174            })
175            .collect::<Result<Vec<_>, _>>()?
176            .try_into()
177            .map_err(|vec: Vec<_>| {
178                Error::MissingValue(MissingValue::default().prepend(vec.len().to_string()))
179            })
180    }
181
182    fn contains_non_secret_data(&self) -> Result<bool, UnexpectedSecret> {
183        self.iter()
184            .map(ConfigurationBuilder::contains_non_secret_data)
185            .enumerate()
186            .try_fold(false, |has_secret, (index, val)| {
187                Ok(val.map_err(|err| err.prepend(index.to_string()))? || has_secret)
188            })
189    }
190}
191
192/// `PhantomData` does not need a builder, however we cannot use `()` as that would make `T`
193/// unconstrained. Instead just making it use itself as a builder and rely on serde handling it
194/// alright.
195impl<T> Configuration for PhantomData<T> {
196    type Builder = Self;
197}
198
199/// `PhantomData` does not need a builder, however we cannot use `()` as that would make `T`
200/// unconstrained. Instead just making it use itself as a builder and rely on serde handling it
201/// alright.
202impl<T> ConfigurationBuilder for PhantomData<T> {
203    type Target = Self;
204
205    fn merge(self, _other: Self) -> Self {
206        self
207    }
208
209    fn try_build(self) -> Result<Self::Target, Error> {
210        Ok(self)
211    }
212
213    fn contains_non_secret_data(&self) -> Result<bool, UnexpectedSecret> {
214        Ok(false)
215    }
216}
217
218/// Build an `Option<T>` with a custom structure as we want `None` to be an explicit value that will
219/// not be overwritten.
220impl<T: Configuration> Configuration for Option<T>
221where
222    OptionBuilder<BuilderOf<T>>: DeserializeOwned,
223{
224    type Builder = OptionBuilder<BuilderOf<T>>;
225}
226
227/// Build an `Option<T>` with a custom structure as we want `None` to be an explicit value that will
228/// not be overwritten.
229#[derive(Debug, Default, Deserialize, Hash, PartialEq, PartialOrd, Eq, Ord)]
230#[serde(from = "Option<T>")]
231pub enum OptionBuilder<T> {
232    /// No item has been provided yet.
233    ///
234    /// Default to `None` but allow overwriting by later [`merge`][ConfigurationBuilder::merge]s.
235    #[default]
236    Unspecified,
237
238    /// Explicit `None`.
239    ///
240    /// Will not be overwritten by later [`merge`][ConfigurationBuilder::merge]s.
241    None,
242
243    /// Explicit `Some`.
244    ///
245    /// Will not be overwritten by later [`merge`][ConfigurationBuilder::merge]s.
246    Some(T),
247}
248
249impl<T> From<Option<T>> for OptionBuilder<T> {
250    fn from(opt: Option<T>) -> Self {
251        opt.map_or(Self::None, |val| Self::Some(val))
252    }
253}
254
255impl<T: ConfigurationBuilder> ConfigurationBuilder for OptionBuilder<T>
256where
257    Self: DeserializeOwned,
258{
259    type Target = Option<TargetOf<T>>;
260
261    fn merge(self, other: Self) -> Self {
262        match (self, other) {
263            // If both `Some` then merge the contained builders
264            (Self::Some(us), Self::Some(other)) => Self::Some(us.merge(other)),
265            // If we don't have a value then always take the other
266            (Self::Unspecified, other) => other,
267            // Either:
268            // - We're explicitly `None`
269            // - We're explicitly `Some` and the other is `Unspecified` or `None`
270            //
271            // In either case, just take our value, which should be preferred to other.
272            (us, _) => us,
273        }
274    }
275
276    fn try_build(self) -> Result<Self::Target, Error> {
277        match self {
278            Self::Unspecified | Self::None => Ok(None),
279            Self::Some(val) => Ok(Some(val.try_build()?)),
280        }
281    }
282
283    fn contains_non_secret_data(&self) -> Result<bool, UnexpectedSecret> {
284        match self {
285            Self::Some(data) => data.contains_non_secret_data(),
286
287            // An explicit `None` is counted as data, overriding any default.
288            Self::None => Ok(true),
289
290            Self::Unspecified => Ok(false),
291        }
292    }
293}