figment/
util.rs

1//! Useful functions and macros for writing figments.
2//!
3//! # `map!` macro
4//!
5//! The `map!` macro constructs a [`Map`](crate::value::Map) from key-value
6//! pairs and is particularly useful during testing:
7//!
8//! ```rust
9//! use figment::util::map;
10//!
11//! let map = map! {
12//!     "name" => "Bob",
13//!     "age" => "100"
14//! };
15//!
16//! assert_eq!(map.get("name"), Some(&"Bob"));
17//! assert_eq!(map.get("age"), Some(&"100"));
18//!
19//! let map = map! {
20//!     100 => "one hundred",
21//!     23 => "twenty-three"
22//! };
23//!
24//! assert_eq!(map.get(&100), Some(&"one hundred"));
25//! assert_eq!(map.get(&23), Some(&"twenty-three"));
26//!
27//! ```
28use std::fmt;
29use std::path::{Path, PathBuf, Component};
30
31use serde::de::{self, Unexpected, Deserializer};
32
33/// A helper function to determine the relative path to `path` from `base`.
34///
35/// Returns `None` if there is no relative path from `base` to `path`, that is,
36/// `base` and `path` do not share a common ancestor. `path` and `base` must be
37/// either both absolute or both relative; returns `None` if one is relative and
38/// the other absolute.
39///
40/// ```
41/// use std::path::Path;
42/// use figment::util::diff_paths;
43///
44/// // Paths must be both relative or both absolute.
45/// assert_eq!(diff_paths("/a/b/c", "b/c"), None);
46/// assert_eq!(diff_paths("a/b/c", "/b/c"), None);
47///
48/// // The root/relative root is always a common ancestor.
49/// assert_eq!(diff_paths("/a/b/c", "/b/c"), Some("../../a/b/c".into()));
50/// assert_eq!(diff_paths("c/a", "b/c/a"), Some("../../../c/a".into()));
51///
52/// let bar = "/foo/bar";
53/// let baz = "/foo/bar/baz";
54/// let quux = "/foo/bar/quux";
55///
56/// assert_eq!(diff_paths(bar, baz), Some("../".into()));
57/// assert_eq!(diff_paths(baz, bar), Some("baz".into()));
58/// assert_eq!(diff_paths(quux, baz), Some("../quux".into()));
59/// assert_eq!(diff_paths(baz, quux), Some("../baz".into()));
60/// assert_eq!(diff_paths(bar, quux), Some("../".into()));
61/// assert_eq!(diff_paths(baz, bar), Some("baz".into()));
62/// ```
63// Copyright 2012-2015 The Rust Project Developers.
64// Copyright 2017 The Rust Project Developers.
65// Adapted from `pathdiff`, which itself adapted from rustc's path_relative_from.
66pub fn diff_paths<P, B>(path: P, base: B) -> Option<PathBuf>
67     where P: AsRef<Path>, B: AsRef<Path>
68{
69    let (path, base) = (path.as_ref(), base.as_ref());
70    if path.has_root() != base.has_root() {
71        return None;
72    }
73
74    let mut ita = path.components();
75    let mut itb = base.components();
76    let mut comps: Vec<Component> = vec![];
77    loop {
78        match (ita.next(), itb.next()) {
79            (None, None) => break,
80            (Some(a), None) => {
81                comps.push(a);
82                comps.extend(ita.by_ref());
83                break;
84            }
85            (None, _) => comps.push(Component::ParentDir),
86            (Some(a), Some(b)) if comps.is_empty() && a == b => (),
87            (Some(a), Some(b)) if b == Component::CurDir => comps.push(a),
88            (Some(_), Some(b)) if b == Component::ParentDir => return None,
89            (Some(a), Some(_)) => {
90                comps.push(Component::ParentDir);
91                for _ in itb {
92                    comps.push(Component::ParentDir);
93                }
94                comps.push(a);
95                comps.extend(ita.by_ref());
96                break;
97            }
98        }
99    }
100
101    Some(comps.iter().map(|c| c.as_os_str()).collect())
102}
103
104/// A helper to deserialize `0/false` as `false` and `1/true` as `true`.
105///
106/// Serde's default deserializer for `bool` only parses the strings `"true"` and
107/// `"false"` as the booleans `true` and `false`, respectively. By contract,
108/// this function _case-insensitively_ parses both the strings `"true"/"false"`
109/// and the integers `1/0` as the booleans `true/false`, respectively.
110///
111/// # Example
112///
113/// ```rust
114/// use figment::Figment;
115///
116/// #[derive(serde::Deserialize)]
117/// struct Config {
118///     #[serde(deserialize_with = "figment::util::bool_from_str_or_int")]
119///     cli_colors: bool,
120/// }
121///
122/// let c0: Config = Figment::from(("cli_colors", "true")).extract().unwrap();
123/// let c1: Config = Figment::from(("cli_colors", "TRUE")).extract().unwrap();
124/// let c2: Config = Figment::from(("cli_colors", 1)).extract().unwrap();
125/// assert_eq!(c0.cli_colors, true);
126/// assert_eq!(c1.cli_colors, true);
127/// assert_eq!(c2.cli_colors, true);
128///
129/// let c0: Config = Figment::from(("cli_colors", "false")).extract().unwrap();
130/// let c1: Config = Figment::from(("cli_colors", "fAlSe")).extract().unwrap();
131/// let c2: Config = Figment::from(("cli_colors", 0)).extract().unwrap();
132/// assert_eq!(c0.cli_colors, false);
133/// assert_eq!(c1.cli_colors, false);
134/// assert_eq!(c2.cli_colors, false);
135/// ```
136pub fn bool_from_str_or_int<'de, D: Deserializer<'de>>(de: D) -> Result<bool, D::Error> {
137    struct Visitor;
138
139    impl<'de> de::Visitor<'de> for Visitor {
140        type Value = bool;
141
142        fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143            f.write_str("a boolean")
144        }
145
146        fn visit_str<E: de::Error>(self, val: &str) -> Result<bool, E> {
147            match val {
148                v if uncased::eq(v, "true") => Ok(true),
149                v if uncased::eq(v, "false") => Ok(false),
150                s => Err(E::invalid_value(Unexpected::Str(s), &"true or false"))
151            }
152        }
153
154        fn visit_u64<E: de::Error>(self, n: u64) -> Result<bool, E> {
155            match n {
156                0 | 1 => Ok(n != 0),
157                n => Err(E::invalid_value(Unexpected::Unsigned(n), &"0 or 1"))
158            }
159        }
160
161        fn visit_i64<E: de::Error>(self, n: i64) -> Result<bool, E> {
162            match n {
163                0 | 1 => Ok(n != 0),
164                n => Err(E::invalid_value(Unexpected::Signed(n), &"0 or 1"))
165            }
166        }
167
168        fn visit_bool<E: de::Error>(self, b: bool) -> Result<bool, E> {
169            Ok(b)
170        }
171    }
172
173    de.deserialize_any(Visitor)
174}
175
176/// A helper to serialize and deserialize a map as a vector of `(key, value)`
177/// pairs.
178///
179/// ```
180/// use figment::{Figment, util::map};
181/// use serde::{Serialize, Deserialize};
182///
183/// #[derive(Debug, Clone, Serialize, Deserialize)]
184/// pub struct Config {
185///     #[serde(with = "figment::util::vec_tuple_map")]
186///     pairs: Vec<(String, usize)>
187/// }
188///
189/// let map = map!["key" => 1, "value" => 100, "name" => 20];
190/// let c: Config = Figment::from(("pairs", map)).extract().unwrap();
191/// assert_eq!(c.pairs.len(), 3);
192///
193/// let mut pairs = c.pairs;
194/// pairs.sort_by_key(|(_, v)| *v);
195///
196/// assert_eq!(pairs[0], ("key".into(), 1));
197/// assert_eq!(pairs[1], ("name".into(), 20));
198/// assert_eq!(pairs[2], ("value".into(), 100));
199/// ```
200pub mod vec_tuple_map {
201    use std::fmt;
202    use serde::{de, Deserialize, Serialize, Deserializer, Serializer};
203
204    /// The serializer half.
205    pub fn serialize<S, K, V>(vec: &[(K, V)], se: S) -> Result<S::Ok, S::Error>
206        where S: Serializer, K: Serialize, V: Serialize
207    {
208        se.collect_map(vec.iter().map(|(ref k, ref v)| (k, v)))
209    }
210
211    /// The deserializer half.
212    pub fn deserialize<'de, K, V, D>(de: D) -> Result<Vec<(K, V)>, D::Error>
213        where D: Deserializer<'de>, K: Deserialize<'de>, V: Deserialize<'de>
214    {
215        struct Visitor<K, V>(std::marker::PhantomData<Vec<(K, V)>>);
216
217        impl<'de, K, V> de::Visitor<'de> for Visitor<K, V>
218            where K: Deserialize<'de>, V: Deserialize<'de>,
219        {
220            type Value = Vec<(K, V)>;
221
222            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
223                formatter.write_str("a map")
224            }
225
226            fn visit_map<A>(self, mut map: A) -> Result<Vec<(K, V)>, A::Error>
227                where A: de::MapAccess<'de>
228            {
229                let mut vec = Vec::with_capacity(map.size_hint().unwrap_or(0));
230                while let Some((k, v)) = map.next_entry()? {
231                    vec.push((k, v));
232                }
233
234                Ok(vec)
235            }
236        }
237
238        de.deserialize_map(Visitor(std::marker::PhantomData))
239    }
240}
241
242use crate::value::{Value, Dict};
243
244/// Given a key path `key` of the form `a.b.c`, creates nested dictionaries for
245/// for every path component delimited by `.` in the path string (3 in `a.b.c`),
246/// each a parent of the next, and the leaf mapping to `value` (`a` -> `b` ->
247/// `c` -> `value`).
248///
249/// If `key` is empty, simply returns `value`. Otherwise, `Value` will be a
250/// dictionary with the nested mappings.
251///
252/// # Example
253///
254/// ```rust
255/// use figment::{util::nest, value::Value};
256///
257/// let leaf = Value::from("I'm a leaf!");
258///
259/// let dict = nest("tea", leaf.clone());
260/// assert_eq!(dict.find_ref("tea").unwrap(), &leaf);
261///
262/// let dict = nest("tea.leaf", leaf.clone());
263/// let tea = dict.find_ref("tea").unwrap();
264/// let found_leaf = tea.find_ref("leaf").unwrap();
265/// assert_eq!(found_leaf, &leaf);
266/// assert_eq!(dict.find_ref("tea.leaf").unwrap(), &leaf);
267///
268/// let just_leaf = nest("", leaf.clone());
269/// assert_eq!(just_leaf, leaf);
270/// ```
271pub fn nest(key: &str, value: Value) -> Value {
272    fn value_from(mut keys: std::str::Split<'_, char>, value: Value) -> Value {
273        match keys.next() {
274            Some(k) if !k.is_empty() => {
275                let mut dict = Dict::new();
276                dict.insert(k.into(), value_from(keys, value));
277                dict.into()
278            }
279            Some(_) | None => value
280        }
281    }
282
283    value_from(key.split('.'), value)
284}
285
286#[doc(hidden)]
287#[macro_export]
288/// This is a macro.
289macro_rules! map {
290    ($($key:expr => $value:expr),* $(,)?) => ({
291        let mut map = $crate::value::Map::new();
292        $(map.insert($key, $value);)*
293        map
294    });
295}
296
297pub use map;
298
299#[doc(hidden)]
300#[macro_export]
301macro_rules! make_cloneable {
302    ($Trait:path: $Cloneable:ident) => {
303        trait $Cloneable {
304            fn box_clone(&self) -> Box<dyn $Trait>;
305        }
306
307        impl std::clone::Clone for Box<dyn $Trait> {
308            fn clone(&self) -> Box<dyn $Trait> {
309                (&**self).box_clone()
310            }
311        }
312
313        impl std::fmt::Debug for Box<dyn $Trait> {
314            fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
315                Ok(())
316            }
317        }
318
319        impl<T: $Trait + Clone> $Cloneable for T {
320            fn box_clone(&self) -> Box<dyn $Trait> {
321                Box::new(self.clone())
322            }
323        }
324    }
325}
326
327#[doc(hidden)]
328#[macro_export]
329macro_rules! cloneable_fn_trait {
330    ($Name:ident: $($rest:tt)*) => {
331        trait $Name: $($rest)* + Cloneable + 'static { }
332        impl<F: Clone + 'static> $Name for F where F: $($rest)* { }
333        $crate::make_cloneable!($Name: Cloneable);
334    }
335}
336
337pub(crate) use cloneable_fn_trait;