figment/value/
value.rs

1use std::collections::BTreeMap;
2use std::num::{ParseFloatError, ParseIntError};
3use std::str::{FromStr, Split};
4
5use serde::Serialize;
6
7use crate::value::{Tag, ValueSerializer, magic::Either};
8use crate::error::{Error, Actual};
9
10/// An alias to the type of map used in [`Value::Dict`].
11pub type Map<K, V> = BTreeMap<K, V>;
12
13/// An alias to a [`Map`] from `String` to [`Value`]s.
14pub type Dict = Map<String, Value>;
15
16/// An enum representing all possible figment value variants.
17///
18/// Note that `Value` implements `From<T>` for all reasonable `T`:
19///
20/// ```
21/// use figment::value::Value;
22///
23/// let v = Value::from("hello");
24/// assert_eq!(v.as_str(), Some("hello"));
25/// ```
26#[derive(Clone)]
27pub enum Value {
28    /// A string.
29    String(Tag, String),
30    /// A character.
31    Char(Tag, char),
32    /// A boolean.
33    Bool(Tag, bool),
34    /// A numeric value.
35    Num(Tag, Num),
36    /// A value with no value.
37    Empty(Tag, Empty),
38    /// A dictionary: a map from `String` to `Value`.
39    Dict(Tag, Dict),
40    /// A sequence/array/vector.
41    Array(Tag, Vec<Value>),
42}
43
44macro_rules! conversion_fn {
45    ($RT:ty, $([$star:tt])? $Variant:ident => $T:ty, $fn_name:ident) => {
46        conversion_fn!(
47            concat!(
48                "Converts `self` into a `", stringify!($T), "` if `self` is a \
49                `Value::", stringify!($Variant), "`.\n\n",
50                "# Example\n\n",
51                "```\n",
52                "use figment::value::Value;\n\n",
53                "let value: Value = 123.into();\n",
54                "let converted = value.", stringify!($fn_name), "();\n",
55                "```"
56            ),
57            $RT, $([$star])? $Variant => $T, $fn_name
58        );
59    };
60
61    ($doc:expr, $RT:ty, $([$star:tt])? $Variant:ident => $T:ty, $fn_name:ident) => {
62        #[doc = $doc]
63        pub fn $fn_name(self: $RT) -> Option<$T> {
64            match $($star)? self {
65                Value::$Variant(_, v) => Some(v),
66                _ => None
67            }
68        }
69    };
70}
71
72impl Value {
73    /// Serialize a `Value` from any `T: Serialize`.
74    ///
75    /// ```
76    /// use figment::value::{Value, Empty};
77    ///
78    /// let value = Value::serialize(10i8).unwrap();
79    /// assert_eq!(value.to_i128(), Some(10));
80    ///
81    /// let value = Value::serialize(()).unwrap();
82    /// assert_eq!(value, Empty::Unit.into());
83    ///
84    /// let value = Value::serialize(vec![4, 5, 6]).unwrap();
85    /// assert_eq!(value, vec![4, 5, 6].into());
86    /// ```
87    pub fn serialize<T: Serialize>(value: T) -> Result<Self, Error> {
88        value.serialize(ValueSerializer)
89    }
90
91    /// Deserialize `self` into any deserializable `T`.
92    ///
93    /// ```
94    /// use figment::value::Value;
95    ///
96    /// let value = Value::from("hello");
97    /// let string: String = value.deserialize().unwrap();
98    /// assert_eq!(string, "hello");
99    /// ```
100    pub fn deserialize<'de, T: serde::Deserialize<'de>>(&self) -> Result<T, Error> {
101        T::deserialize(self)
102    }
103
104    /// Looks up and returns the value at path `path`, where `path` is of the
105    /// form `a.b.c` where `a`, `b`, and `c` are keys to dictionaries. If the
106    /// key is empty, simply returns `self`. If the key is not empty and `self`
107    /// or any of the values for non-leaf keys in the path are not dictionaries,
108    /// returns `None`.
109    ///
110    /// This method consumes `self`. See [`Value::find_ref()`] for a
111    /// non-consuming variant.
112    ///
113    /// # Example
114    ///
115    /// ```rust
116    /// use figment::{value::Value, util::map};
117    ///
118    /// let value = Value::from(map! {
119    ///     "apple" => map! {
120    ///         "bat" => map! {
121    ///             "pie" => 4usize,
122    ///         },
123    ///         "cake" => map! {
124    ///             "pumpkin" => 10usize,
125    ///         }
126    ///     }
127    /// });
128    ///
129    /// assert!(value.clone().find("apple").is_some());
130    /// assert!(value.clone().find("apple.bat").is_some());
131    /// assert!(value.clone().find("apple.cake").is_some());
132    ///
133    /// assert_eq!(value.clone().find("apple.bat.pie").unwrap().to_u128(), Some(4));
134    /// assert_eq!(value.clone().find("apple.cake.pumpkin").unwrap().to_u128(), Some(10));
135    ///
136    /// assert!(value.clone().find("apple.pie").is_none());
137    /// assert!(value.clone().find("pineapple").is_none());
138    /// ```
139    pub fn find(self, path: &str) -> Option<Value> {
140        fn find(mut keys: Split<char>, value: Value) -> Option<Value> {
141            match keys.next() {
142                Some(k) if !k.is_empty() => find(keys, value.into_dict()?.remove(k)?),
143                Some(_) | None => Some(value)
144            }
145        }
146
147        find(path.split('.'), self)
148    }
149
150    /// Exactly like [`Value::find()`] but does not consume `self`,
151    /// returning a reference to the found value, if any, instead.
152    ///
153    /// # Example
154    ///
155    /// ```rust
156    /// use figment::{value::Value, util::map};
157    ///
158    /// let value = Value::from(map! {
159    ///     "apple" => map! {
160    ///         "bat" => map! {
161    ///             "pie" => 4usize,
162    ///         },
163    ///         "cake" => map! {
164    ///             "pumpkin" => 10usize,
165    ///         }
166    ///     }
167    /// });
168    ///
169    /// assert!(value.find_ref("apple").is_some());
170    /// assert!(value.find_ref("apple.bat").is_some());
171    /// assert!(value.find_ref("apple.cake").is_some());
172    ///
173    /// assert_eq!(value.find_ref("apple.bat.pie").unwrap().to_u128(), Some(4));
174    /// assert_eq!(value.find_ref("apple.cake.pumpkin").unwrap().to_u128(), Some(10));
175    ///
176    /// assert!(value.find_ref("apple.pie").is_none());
177    /// assert!(value.find_ref("pineapple").is_none());
178    /// ```
179    pub fn find_ref<'a>(&'a self, path: &str) -> Option<&'a Value> {
180        fn find<'a, 'v>(mut keys: Split<'a, char>, value: &'v Value) -> Option<&'v Value> {
181            match keys.next() {
182                Some(k) if !k.is_empty() => find(keys, value.as_dict()?.get(k)?),
183                Some(_) | None => Some(value)
184            }
185        }
186
187        find(path.split('.'), self)
188    }
189
190    /// Returns the [`Tag`] applied to this value.
191    ///
192    /// ```
193    /// use figment::{Figment, Profile, value::Value, util::map};
194    ///
195    /// let map: Value = Figment::from(("key", "value")).extract().unwrap();
196    /// let value = map.find_ref("key").expect("value");
197    /// assert_eq!(value.as_str(), Some("value"));
198    /// assert!(!value.tag().is_default());
199    /// assert_eq!(value.tag().profile(), Some(Profile::Global));
200    ///
201    /// let map: Value = Figment::from(("key", map!["key2" => 123])).extract().unwrap();
202    /// let value = map.find_ref("key.key2").expect("value");
203    /// assert_eq!(value.to_i128(), Some(123));
204    /// assert!(!value.tag().is_default());
205    /// assert_eq!(value.tag().profile(), Some(Profile::Global));
206    /// ```
207    pub fn tag(&self) -> Tag {
208        match *self {
209            Value::String(tag, ..) => tag,
210            Value::Char(tag, ..) => tag,
211            Value::Bool(tag, ..) => tag,
212            Value::Num(tag, ..) => tag,
213            Value::Dict(tag, ..) => tag,
214            Value::Array(tag, ..) => tag,
215            Value::Empty(tag, ..) => tag,
216        }
217    }
218
219    conversion_fn!(&Value, String => &str, as_str);
220    conversion_fn!(Value, String => String, into_string);
221    conversion_fn!(&Value, [*]Char => char, to_char);
222    conversion_fn!(&Value, [*]Bool => bool, to_bool);
223    conversion_fn!(&Value, [*]Num => Num, to_num);
224    conversion_fn!(&Value, [*]Empty => Empty, to_empty);
225    conversion_fn!(&Value, Dict => &Dict, as_dict);
226    conversion_fn!(Value, Dict => Dict, into_dict);
227    conversion_fn!(&Value, Array => &[Value], as_array);
228    conversion_fn!(Value, Array => Vec<Value>, into_array);
229
230    /// Converts `self` into a `u128` if `self` is an unsigned `Value::Num`
231    /// variant.
232    ///
233    /// # Example
234    ///
235    /// ```
236    /// use figment::value::Value;
237    ///
238    /// let value: Value = 123u8.into();
239    /// let converted = value.to_u128();
240    /// assert_eq!(converted, Some(123));
241    /// ```
242    pub fn to_u128(&self) -> Option<u128> {
243        self.to_num()?.to_u128()
244    }
245
246    /// Converts `self` into an `i128` if `self` is an signed `Value::Num`
247    /// variant.
248    ///
249    /// # Example
250    ///
251    /// ```
252    /// use figment::value::Value;
253    ///
254    /// let value: Value = 123i8.into();
255    /// let converted = value.to_i128();
256    /// assert_eq!(converted, Some(123));
257    ///
258    /// let value: Value = Value::from(5000i64);
259    /// assert_eq!(value.to_i128(), Some(5000i128));
260    /// ```
261    pub fn to_i128(&self) -> Option<i128> {
262        self.to_num()?.to_i128()
263    }
264
265    /// Converts `self` into an `f64` if `self` is either a [`Num::F32`] or
266    /// [`Num::F64`].
267    ///
268    /// # Example
269    ///
270    /// ```
271    /// use figment::value::Value;
272    ///
273    /// let value: Value = 7.0f32.into();
274    /// let converted = value.to_f64();
275    /// assert_eq!(converted, Some(7.0f64));
276    ///
277    /// let value: Value = Value::from(7.0f64);
278    /// assert_eq!(value.to_f64(), Some(7.0f64));
279    /// ```
280    pub fn to_f64(&self) -> Option<f64> {
281        self.to_num()?.to_f64()
282    }
283
284    /// Converts `self` to a `bool` if it is a [`Value::Bool`], or if it is a
285    /// [`Value::String`] or a [`Value::Num`] with a boolean interpretation.
286    ///
287    /// The case-insensitive strings "true", "yes", "1", and "on", and the
288    /// signed or unsigned integers `1` are interpreted as `true`.
289    ///
290    /// The case-insensitive strings "false", "no", "0", and "off", and the
291    /// signed or unsigned integers `0` are interpreted as false.
292    ///
293    /// # Example
294    ///
295    /// ```
296    /// use figment::value::Value;
297    ///
298    /// let value = Value::from(true);
299    /// assert_eq!(value.to_bool_lossy(), Some(true));
300    ///
301    /// let value = Value::from(1);
302    /// assert_eq!(value.to_bool_lossy(), Some(true));
303    ///
304    /// let value = Value::from("YES");
305    /// assert_eq!(value.to_bool_lossy(), Some(true));
306    ///
307    /// let value = Value::from(false);
308    /// assert_eq!(value.to_bool_lossy(), Some(false));
309    ///
310    /// let value = Value::from(0);
311    /// assert_eq!(value.to_bool_lossy(), Some(false));
312    ///
313    /// let value = Value::from("no");
314    /// assert_eq!(value.to_bool_lossy(), Some(false));
315    ///
316    /// let value = Value::from("hello");
317    /// assert_eq!(value.to_bool_lossy(), None);
318    /// ```
319    pub fn to_bool_lossy(&self) -> Option<bool> {
320        match self {
321            Value::Bool(_, b) => Some(*b),
322            Value::Num(_, num) => match num.to_u128_lossy() {
323                Some(0) => Some(false),
324                Some(1) => Some(true),
325                _ => None
326            }
327            Value::String(_, s) => {
328                const TRUE: &[&str] = &["true", "yes", "1", "on"];
329                const FALSE: &[&str] = &["false", "no", "0", "off"];
330
331                if TRUE.iter().any(|v| uncased::eq(v, s)) {
332                    Some(true)
333                } else if FALSE.iter().any(|v| uncased::eq(v, s)) {
334                    Some(false)
335                } else {
336                    None
337                }
338            },
339            _ => None,
340        }
341    }
342
343    /// Converts `self` to a [`Num`] if it is a [`Value::Num`] or if it is a
344    /// [`Value::String`] that parses as a `usize` ([`Num::USize`]), `isize`
345    /// ([`Num::ISize`]), or `f64` ([`Num::F64`]), in that order of precendence.
346    ///
347    /// # Examples
348    ///
349    /// ```
350    /// use figment::value::{Value, Num};
351    ///
352    /// let value = Value::from(7_i32);
353    /// assert_eq!(value.to_num_lossy(), Some(Num::I32(7)));
354    ///
355    /// let value = Value::from("7");
356    /// assert_eq!(value.to_num_lossy(), Some(Num::U8(7)));
357    ///
358    /// let value = Value::from("-7000");
359    /// assert_eq!(value.to_num_lossy(), Some(Num::I16(-7000)));
360    ///
361    /// let value = Value::from("7000.5");
362    /// assert_eq!(value.to_num_lossy(), Some(Num::F64(7000.5)));
363    /// ```
364    pub fn to_num_lossy(&self) -> Option<Num> {
365        match self {
366            Value::Num(_, num) => Some(*num),
367            Value::String(_, s) => s.parse().ok(),
368            _ => None,
369        }
370    }
371
372    /// Converts `self` into the corresponding [`Actual`].
373    ///
374    /// See also [`Num::to_actual()`] and [`Empty::to_actual()`], which are
375    /// called internally by this method.
376    ///
377    /// # Example
378    ///
379    /// ```rust
380    /// use figment::{value::Value, error::Actual};
381    ///
382    /// assert_eq!(Value::from('a').to_actual(), Actual::Char('a'));
383    /// assert_eq!(Value::from(&[1, 2, 3]).to_actual(), Actual::Seq);
384    /// ```
385    pub fn to_actual(&self) -> Actual {
386        match self {
387            Value::String(_, s) => Actual::Str(s.into()),
388            Value::Char(_, c) => Actual::Char(*c),
389            Value::Bool(_, b) => Actual::Bool(*b),
390            Value::Num(_, n) => n.to_actual(),
391            Value::Empty(_, e) => e.to_actual(),
392            Value::Dict(_, _) => Actual::Map,
393            Value::Array(_, _) => Actual::Seq,
394        }
395    }
396
397    pub(crate) fn tag_mut(&mut self) -> &mut Tag {
398        match self {
399            Value::String(tag, ..) => tag,
400            Value::Char(tag, ..) => tag,
401            Value::Bool(tag, ..) => tag,
402            Value::Num(tag, ..) => tag,
403            Value::Dict(tag, ..) => tag,
404            Value::Array(tag, ..) => tag,
405            Value::Empty(tag, ..) => tag,
406        }
407    }
408
409    pub(crate) fn map_tag<F>(&mut self, mut f: F)
410        where F: FnMut(&mut Tag) + Copy
411    {
412        if *self.tag_mut() == Tag::Default {
413            f(self.tag_mut());
414        }
415
416        match self {
417            Value::Dict(_, v) => v.iter_mut().for_each(|(_, v)| v.map_tag(f)),
418            Value::Array(_, v) => v.iter_mut().for_each(|v| v.map_tag(f)),
419            _ => { /* already handled */ }
420        }
421    }
422}
423
424impl std::fmt::Debug for Value {
425    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
426        match self {
427            Self::String(_, v) => f.debug_tuple("String").field(v).finish(),
428            Self::Char(_, v) => f.debug_tuple("Char").field(v).finish(),
429            Self::Bool(_, v) => f.debug_tuple("Bool").field(v).finish(),
430            Self::Num(_, v) => f.debug_tuple("Num").field(v).finish(),
431            Self::Empty(_, v) => f.debug_tuple("Empty").field(v).finish(),
432            Self::Dict(_, v) => f.debug_tuple("Dict").field(v).finish(),
433            Self::Array(_, v) => f.debug_tuple("Array").field(v).finish(),
434        }
435    }
436}
437
438impl PartialEq for Value {
439    fn eq(&self, other: &Self) -> bool {
440        match (self, other) {
441            (Value::String(_, v1), Value::String(_, v2)) => v1 == v2,
442            (Value::Char(_, v1), Value::Char(_, v2)) => v1 == v2,
443            (Value::Bool(_, v1), Value::Bool(_, v2)) => v1 == v2,
444            (Value::Num(_, v1), Value::Num(_, v2)) => v1 == v2,
445            (Value::Empty(_, v1), Value::Empty(_, v2)) => v1 == v2,
446            (Value::Dict(_, v1), Value::Dict(_, v2)) => v1 == v2,
447            (Value::Array(_, v1), Value::Array(_, v2)) => v1 == v2,
448            _ => false,
449        }
450    }
451}
452
453macro_rules! impl_from_array {
454    ($($N:literal),*) => ($(impl_from_array!(@$N);)*);
455    (@$N:literal) => (
456        impl<'a, T: Into<Value> + Clone> From<&'a [T; $N]> for Value {
457            #[inline(always)]
458            fn from(value: &'a [T; $N]) -> Value {
459                Value::from(&value[..])
460            }
461        }
462    )
463}
464
465impl_from_array!(1, 2, 3, 4, 5, 6, 7, 8);
466
467impl From<&str> for Value {
468    fn from(value: &str) -> Value {
469        Value::String(Tag::Default, value.to_string())
470    }
471}
472
473impl<'a, T: Into<Value> + Clone> From<&'a [T]> for Value {
474    fn from(value: &'a [T]) -> Value {
475        Value::Array(Tag::Default, value.iter().map(|v| v.clone().into()).collect())
476    }
477}
478
479impl<'a, T: Into<Value>> From<Vec<T>> for Value {
480    fn from(vec: Vec<T>) -> Value {
481        let vector = vec.into_iter().map(|v| v.into()).collect();
482        Value::Array(Tag::Default, vector)
483    }
484}
485
486impl<K: AsRef<str>, V: Into<Value>> From<Map<K, V>> for Value {
487    fn from(map: Map<K, V>) -> Value {
488        let dict: Dict = map.into_iter()
489            .map(|(k, v)| (k.as_ref().to_string(), v.into()))
490            .collect();
491
492        Value::Dict(Tag::Default, dict)
493    }
494}
495
496macro_rules! impl_from_for_value {
497    ($($T:ty: $V:ident),*) => ($(
498        impl From<$T> for Value {
499            fn from(value: $T) -> Value { Value::$V(Tag::Default, value.into()) }
500        }
501    )*)
502}
503
504macro_rules! try_convert {
505    ($n:expr => $($T:ty),*) => {$(
506        if let Ok(n) = <$T as std::convert::TryFrom<_>>::try_from($n) {
507            return Ok(n.into());
508        }
509    )*}
510}
511
512impl FromStr for Num {
513    type Err = Either<ParseIntError, ParseFloatError>;
514
515    fn from_str(string: &str) -> Result<Self, Self::Err> {
516        let string = string.trim();
517        if string.contains('.') {
518            if string.len() <= (f32::DIGITS as usize + 1) {
519                Ok(string.parse::<f32>().map_err(Either::Right)?.into())
520            } else {
521                Ok(string.parse::<f64>().map_err(Either::Right)?.into())
522            }
523        } else if string.starts_with('-') {
524            let int = string.parse::<i128>().map_err(Either::Left)?;
525            try_convert![int => i8, i16, i32, i64];
526            Ok(int.into())
527        } else {
528            let uint = string.parse::<u128>().map_err(Either::Left)?;
529            try_convert![uint => u8, u16, u32, u64];
530            Ok(uint.into())
531        }
532    }
533}
534
535impl_from_for_value! {
536    String: String, char: Char, bool: Bool,
537    u8: Num, u16: Num, u32: Num, u64: Num, u128: Num, usize: Num,
538    i8: Num, i16: Num, i32: Num, i64: Num, i128: Num, isize: Num,
539    f32: Num, f64: Num, Num: Num, Empty: Empty
540}
541
542/// A signed or unsigned numeric value.
543#[derive(Debug, Clone, Copy)]
544pub enum Num {
545    /// An 8-bit unsigned integer.
546    U8(u8),
547    /// A 16-bit unsigned integer.
548    U16(u16),
549    /// A 32-bit unsigned integer.
550    U32(u32),
551    /// A 64-bit unsigned integer.
552    U64(u64),
553    /// A 128-bit unsigned integer.
554    U128(u128),
555    /// An unsigned integer of platform width.
556    USize(usize),
557    /// An 8-bit signed integer.
558    I8(i8),
559    /// A 16-bit signed integer.
560    I16(i16),
561    /// A 32-bit signed integer.
562    I32(i32),
563    /// A 64-bit signed integer.
564    I64(i64),
565    /// A 128-bit signed integer.
566    I128(i128),
567    /// A signed integer of platform width.
568    ISize(isize),
569    /// A 32-bit wide float.
570    F32(f32),
571    /// A 64-bit wide float.
572    F64(f64),
573}
574
575impl Num {
576    /// Converts `self` into a `u32` if `self` is an unsigned variant with `<=
577    /// 32` bits.
578    ///
579    /// # Example
580    ///
581    /// ```
582    /// use figment::value::Num;
583    ///
584    /// let num: Num = 123u8.into();
585    /// assert_eq!(num.to_u32(), Some(123));
586    ///
587    /// let num: Num = (u32::max_value() as u64 + 1).into();
588    /// assert_eq!(num.to_u32(), None);
589    /// ```
590    pub fn to_u32(self) -> Option<u32> {
591        Some(match self {
592            Num::U8(v) => v as u32,
593            Num::U16(v) => v as u32,
594            Num::U32(v) => v as u32,
595            _ => return None,
596        })
597    }
598
599    /// Converts `self` into a `u128` if `self` is an unsigned variant.
600    ///
601    /// # Example
602    ///
603    /// ```
604    /// use figment::value::Num;
605    ///
606    /// let num: Num = 123u8.into();
607    /// assert_eq!(num.to_u128(), Some(123));
608    /// ```
609    pub fn to_u128(self) -> Option<u128> {
610        Some(match self {
611            Num::U8(v) => v as u128,
612            Num::U16(v) => v as u128,
613            Num::U32(v) => v as u128,
614            Num::U64(v) => v as u128,
615            Num::U128(v) => v as u128,
616            Num::USize(v) => v as u128,
617            _ => return None,
618        })
619    }
620
621    /// Converts `self` into a `u128` if it is non-negative, even if `self` is
622    /// of a signed variant.
623    ///
624    /// # Example
625    ///
626    /// ```
627    /// use figment::value::Num;
628    ///
629    /// let num: Num = 123u8.into();
630    /// assert_eq!(num.to_u128_lossy(), Some(123));
631    ///
632    /// let num: Num = 123i8.into();
633    /// assert_eq!(num.to_u128_lossy(), Some(123));
634    /// ```
635    pub fn to_u128_lossy(self) -> Option<u128> {
636        Some(match self {
637            Num::U8(v) => v as u128,
638            Num::U16(v) => v as u128,
639            Num::U32(v) => v as u128,
640            Num::U64(v) => v as u128,
641            Num::U128(v) => v as u128,
642            Num::USize(v) => v as u128,
643            Num::I8(v) if v >= 0 => v as u128,
644            Num::I16(v) if v >= 0 => v as u128,
645            Num::I32(v) if v >= 0 => v as u128,
646            Num::I64(v) if v >= 0 => v as u128,
647            Num::I128(v) if v >= 0 => v as u128,
648            Num::ISize(v) if v >= 0 => v as u128,
649            _ => return None,
650        })
651    }
652
653    /// Converts `self` into an `i128` if `self` is a signed `Value::Num`
654    /// variant.
655    ///
656    /// # Example
657    ///
658    /// ```
659    /// use figment::value::Num;
660    ///
661    /// let num: Num = 123i8.into();
662    /// assert_eq!(num.to_i128(), Some(123));
663    /// ```
664    pub fn to_i128(self) -> Option<i128> {
665        Some(match self {
666            Num::I8(v) => v as i128,
667            Num::I16(v) => v as i128,
668            Num::I32(v) => v as i128,
669            Num::I64(v) => v as i128,
670            Num::I128(v) => v as i128,
671            Num::ISize(v) => v as i128,
672            _ => return None,
673        })
674    }
675
676    /// Converts `self` into an `f64` if `self` is either a [`Num::F32`] or
677    /// [`Num::F64`].
678    ///
679    /// # Example
680    ///
681    /// ```
682    /// use figment::value::Num;
683    ///
684    /// let num: Num = 3.0f32.into();
685    /// assert_eq!(num.to_f64(), Some(3.0f64));
686    /// ```
687    pub fn to_f64(&self) -> Option<f64> {
688        Some(match *self {
689            Num::F32(v) => v as f64,
690            Num::F64(v) => v as f64,
691            _ => return None,
692        })
693    }
694
695    /// Converts `self` into an [`Actual`]. All unsigned variants return
696    /// [`Actual::Unsigned`], signed variants [`Actual::Signed`], and float
697    /// variants [`Actual::Float`]. Values exceeding the bit-width of the target
698    /// [`Actual`] are truncated.
699    ///
700    /// # Example
701    ///
702    /// ```rust
703    /// use figment::{value::Num, error::Actual};
704    ///
705    /// assert_eq!(Num::U8(10).to_actual(), Actual::Unsigned(10));
706    /// assert_eq!(Num::U64(2380).to_actual(), Actual::Unsigned(2380));
707    ///
708    /// assert_eq!(Num::I8(127).to_actual(), Actual::Signed(127));
709    /// assert_eq!(Num::ISize(23923).to_actual(), Actual::Signed(23923));
710    ///
711    /// assert_eq!(Num::F32(2.5).to_actual(), Actual::Float(2.5));
712    /// assert_eq!(Num::F64(2.103).to_actual(), Actual::Float(2.103));
713    /// ```
714    pub fn to_actual(&self) -> Actual {
715        match *self {
716            Num::U8(v) => Actual::Unsigned(v as u128),
717            Num::U16(v) => Actual::Unsigned(v as u128),
718            Num::U32(v) => Actual::Unsigned(v as u128),
719            Num::U64(v) => Actual::Unsigned(v as u128),
720            Num::U128(v) => Actual::Unsigned(v as u128),
721            Num::USize(v) => Actual::Unsigned(v as u128),
722            Num::I8(v) => Actual::Signed(v as i128),
723            Num::I16(v) => Actual::Signed(v as i128),
724            Num::I32(v) => Actual::Signed(v as i128),
725            Num::I64(v) => Actual::Signed(v as i128),
726            Num::I128(v) => Actual::Signed(v as i128),
727            Num::ISize(v) => Actual::Signed(v as i128),
728            Num::F32(v) => Actual::Float(v as f64),
729            Num::F64(v) => Actual::Float(v as f64),
730        }
731    }
732}
733
734impl PartialEq for Num {
735    fn eq(&self, other: &Self) -> bool {
736        self.to_actual() == other.to_actual()
737    }
738}
739
740macro_rules! impl_from_for_num_value {
741    ($($T:ty: $V:ident),*) => ($(
742        impl From<$T> for Num {
743            fn from(value: $T) -> Num {
744                Num::$V(value)
745            }
746        }
747    )*)
748}
749
750impl_from_for_num_value! {
751    u8: U8, u16: U16, u32: U32, u64: U64, u128: U128, usize: USize,
752    i8: I8, i16: I16, i32: I32, i64: I64, i128: I128, isize: ISize,
753    f32: F32, f64: F64
754}
755
756/// A value with no value: `None` or `Unit`.
757#[derive(Debug, Clone, Copy, PartialEq)]
758pub enum Empty {
759    /// Like `Option::None`.
760    None,
761    /// Like `()`.
762    Unit
763}
764
765impl Empty {
766    /// Converts `self` into an [`Actual`].
767    ///
768    /// # Example
769    ///
770    /// ```rust
771    /// use figment::{value::Empty, error::Actual};
772    ///
773    /// assert_eq!(Empty::None.to_actual(), Actual::Option);
774    /// assert_eq!(Empty::Unit.to_actual(), Actual::Unit);
775    /// ```
776    pub fn to_actual(&self) -> Actual {
777        match self {
778            Empty::None => Actual::Option,
779            Empty::Unit => Actual::Unit,
780        }
781    }
782}