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}