figment/
error.rs

1//! Error values produces when extracting configurations.
2
3use std::fmt::{self, Display};
4use std::borrow::Cow;
5
6use serde::{ser, de};
7
8use crate::{Figment, Profile, Metadata, value::Tag};
9
10/// A simple alias to `Result` with an error type of [`Error`].
11pub type Result<T> = std::result::Result<T, Error>;
12
13/// An error that occured while producing data or extracting a configuration.
14///
15/// # Constructing Errors
16///
17/// An `Error` will generally be constructed indirectly via its implementations
18/// of serde's [`de::Error`] and [`ser::Error`], that is, as a result of
19/// serialization or deserialization errors. When implementing [`Provider`],
20/// however, it may be necessary to construct an `Error` directly.
21///
22/// [`Provider`]: crate::Provider
23///
24/// Broadly, there are two ways to construct an `Error`:
25///
26///   * With an error message, as `Error` impls `From<String>` and `From<&str>`:
27///
28///     ```
29///     use figment::Error;
30///
31///     Error::from(format!("{} is invalid", 1));
32///
33///     Error::from("whoops, something went wrong!");
34///     ```
35///
36///   * With a [`Kind`], as `Error` impls `From<Kind>`:
37///
38///     ```
39///     use figment::{error::{Error, Kind}, value::Value};
40///
41///     let value = Value::serialize(&100).unwrap();
42///     if !value.as_str().is_some() {
43///         let kind = Kind::InvalidType(value.to_actual(), "string".into());
44///         let error = Error::from(kind);
45///     }
46///     ```
47///
48/// As always, `?` can be used to automatically convert into an `Error` using
49/// the available `From` implementations:
50///
51/// ```
52/// use std::fs::File;
53///
54/// fn try_read() -> Result<(), figment::Error> {
55///     let x = File::open("/tmp/foo.boo").map_err(|e| e.to_string())?;
56///     Ok(())
57/// }
58/// ```
59///
60/// # Display
61///
62/// By default, `Error` uses all of the available information about the error,
63/// including the `Metadata`, `path`, and `profile` to display a message that
64/// resembles the following, where `$` is `error.` for some `error: Error`:
65///
66/// ```text
67/// $kind: `$metadata.interpolate($path)` in $($metadata.sources())*
68/// ```
69///
70/// Concretely, such an error may look like:
71///
72/// ```text
73/// invalid type: found sequence, expected u16: `staging.port` in TOML file Config.toml
74/// ```
75///
76/// # Iterator
77///
78/// An `Error` may contain more than one error. To process all errors, iterate
79/// over an `Error`:
80///
81/// ```rust
82/// fn with_error(error: figment::Error) {
83///     for error in error {
84///         println!("error: {}", error);
85///     }
86/// }
87/// ```
88#[derive(Clone, Debug, PartialEq)]
89pub struct Error {
90    /// The tag of the value that errored. We use this to lookup the `metadata`.
91    tag: Tag,
92    /// The profile that was selected when the error occured, if any.
93    pub profile: Option<Profile>,
94    /// The metadata for the provider of the value that errored, if known.
95    pub metadata: Option<Metadata>,
96    /// The path to the configuration key that errored, if known.
97    pub path: Vec<String>,
98    /// The error kind.
99    pub kind: Kind,
100    prev: Option<Box<Error>>,
101}
102
103/// An error kind, encapsulating serde's [`serde::de::Error`].
104#[derive(Clone, Debug, PartialEq)]
105pub enum Kind {
106    /// A custom error message.
107    Message(String),
108
109    /// An invalid type: (actual, expected). See
110    /// [`serde::de::Error::invalid_type()`].
111    InvalidType(Actual, String),
112    /// An invalid value: (actual, expected). See
113    /// [`serde::de::Error::invalid_value()`].
114    InvalidValue(Actual, String),
115    /// Too many or too few items: (actual, expected). See
116    /// [`serde::de::Error::invalid_length()`].
117    InvalidLength(usize, String),
118
119    /// A variant with an unrecognized name: (actual, expected). See
120    /// [`serde::de::Error::unknown_variant()`].
121    UnknownVariant(String, &'static [&'static str]),
122    /// A field with an unrecognized name: (actual, expected). See
123    /// [`serde::de::Error::unknown_field()`].
124    UnknownField(String, &'static [&'static str]),
125    /// A field was missing: (name). See [`serde::de::Error::missing_field()`].
126    MissingField(Cow<'static, str>),
127    /// A field appeared more than once: (name). See
128    /// [`serde::de::Error::duplicate_field()`].
129    DuplicateField(&'static str),
130
131    /// The `isize` was not in range of any known sized signed integer.
132    ISizeOutOfRange(isize),
133    /// The `usize` was not in range of any known sized unsigned integer.
134    USizeOutOfRange(usize),
135
136    /// The serializer or deserializer does not support the `Actual` type.
137    Unsupported(Actual),
138
139    /// The type `.0` cannot be used for keys, need a `.1`.
140    UnsupportedKey(Actual, Cow<'static, str>),
141}
142
143impl Error {
144    pub(crate) fn prefixed(mut self, key: &str) -> Self {
145        self.path.insert(0, key.into());
146        self
147    }
148
149    pub(crate) fn retagged(mut self, tag: Tag) -> Self {
150        if self.tag.is_default() {
151            self.tag = tag;
152        }
153
154        self
155    }
156
157    pub(crate) fn resolved(mut self, config: &Figment) -> Self {
158        let mut error = Some(&mut self);
159        while let Some(e) = error {
160            e.metadata = config.get_metadata(e.tag).cloned();
161            e.profile = e.tag.profile()
162                .or_else(|| Some(config.profile().clone()));
163
164            error = e.prev.as_deref_mut();
165        }
166
167        self
168    }
169}
170
171impl Error {
172    /// Returns `true` if the error's kind is `MissingField`.
173    ///
174    /// # Example
175    ///
176    /// ```rust
177    /// use figment::error::{Error, Kind};
178    ///
179    /// let error = Error::from(Kind::MissingField("path".into()));
180    /// assert!(error.missing());
181    /// ```
182    pub fn missing(&self) -> bool {
183        matches!(self.kind, Kind::MissingField(..))
184    }
185
186    /// Append the string `path` to the error's path.
187    ///
188    /// # Example
189    ///
190    /// ```rust
191    /// use figment::Error;
192    ///
193    /// let error = Error::from("an error message").with_path("some_path");
194    /// assert_eq!(error.path, vec!["some_path"]);
195    ///
196    /// let error = Error::from("an error message").with_path("some.path");
197    /// assert_eq!(error.path, vec!["some", "path"]);
198    /// ```
199    pub fn with_path(mut self, path: &str) -> Self {
200        let paths = path.split('.')
201            .filter(|v| !v.is_empty())
202            .map(|v| v.to_string());
203
204        self.path.extend(paths);
205        self
206    }
207
208    /// Prepends `self` to `error` and returns `error`.
209    ///
210    /// ```rust
211    /// use figment::error::Error;
212    ///
213    /// let e1 = Error::from("1");
214    /// let e2 = Error::from("2");
215    /// let e3 = Error::from("3");
216    ///
217    /// let error = e1.chain(e2).chain(e3);
218    /// assert_eq!(error.count(), 3);
219    ///
220    /// let unchained = error.into_iter()
221    ///     .map(|e| e.to_string())
222    ///     .collect::<Vec<_>>();
223    /// assert_eq!(unchained, vec!["3", "2", "1"]);
224    ///
225    /// let e1 = Error::from("1");
226    /// let e2 = Error::from("2");
227    /// let e3 = Error::from("3");
228    /// let error = e3.chain(e2).chain(e1);
229    /// assert_eq!(error.count(), 3);
230    ///
231    /// let unchained = error.into_iter()
232    ///     .map(|e| e.to_string())
233    ///     .collect::<Vec<_>>();
234    /// assert_eq!(unchained, vec!["1", "2", "3"]);
235    /// ```
236    pub fn chain(self, mut error: Error) -> Self {
237        error.prev = Some(Box::new(self));
238        error
239    }
240
241    /// Returns the number of errors represented by `self`.
242    ///
243    /// # Example
244    ///
245    /// ```rust
246    /// use figment::{Figment, providers::{Format, Toml}};
247    ///
248    /// figment::Jail::expect_with(|jail| {
249    ///     jail.create_file("Base.toml", r#"
250    ///         # oh no, an unclosed array!
251    ///         cat = [1
252    ///     "#)?;
253    ///
254    ///     jail.create_file("Release.toml", r#"
255    ///         # and now an unclosed string!?
256    ///         cat = "
257    ///     "#)?;
258    ///
259    ///     let figment = Figment::from(Toml::file("Base.toml"))
260    ///         .merge(Toml::file("Release.toml"));
261    ///
262    ///     let error = figment.extract_inner::<String>("cat").unwrap_err();
263    ///     assert_eq!(error.count(), 2);
264    ///
265    ///     Ok(())
266    /// });
267    /// ```
268    pub fn count(&self) -> usize {
269        1 + self.prev.as_ref().map_or(0, |e| e.count())
270    }
271}
272
273/// An iterator over all errors in an [`Error`].
274pub struct IntoIter(Option<Error>);
275
276impl Iterator for IntoIter {
277    type Item = Error;
278
279    fn next(&mut self) -> Option<Self::Item> {
280        if let Some(mut error) = self.0.take() {
281            self.0 = error.prev.take().map(|e| *e);
282            Some(error)
283        } else {
284            None
285        }
286    }
287}
288
289impl IntoIterator for Error {
290    type Item = Error;
291    type IntoIter = IntoIter;
292
293    fn into_iter(self) -> Self::IntoIter {
294        IntoIter(Some(self))
295    }
296}
297
298/// A type that enumerates all of serde's types, used to indicate that a value
299/// of the given type was received.
300#[allow(missing_docs)]
301#[derive(Clone, Debug, PartialEq)]
302pub enum Actual {
303    Bool(bool),
304    Unsigned(u128),
305    Signed(i128),
306    Float(f64),
307    Char(char),
308    Str(String),
309    Bytes(Vec<u8>),
310    Unit,
311    Option,
312    NewtypeStruct,
313    Seq,
314    Map,
315    Enum,
316    UnitVariant,
317    NewtypeVariant,
318    TupleVariant,
319    StructVariant,
320    Other(String),
321}
322
323impl fmt::Display for Actual {
324    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325        match self {
326            Actual::Bool(v) => write!(f, "bool {}", v),
327            Actual::Unsigned(v) => write!(f, "unsigned int `{}`", v),
328            Actual::Signed(v) => write!(f, "signed int `{}`", v),
329            Actual::Float(v) => write!(f, "float `{}`", v),
330            Actual::Char(v) => write!(f, "char {:?}", v),
331            Actual::Str(v) => write!(f, "string {:?}", v),
332            Actual::Bytes(v) => write!(f, "bytes {:?}", v),
333            Actual::Unit => write!(f, "unit"),
334            Actual::Option => write!(f, "option"),
335            Actual::NewtypeStruct => write!(f, "new-type struct"),
336            Actual::Seq => write!(f, "sequence"),
337            Actual::Map => write!(f, "map"),
338            Actual::Enum => write!(f, "enum"),
339            Actual::UnitVariant => write!(f, "unit variant"),
340            Actual::NewtypeVariant => write!(f, "new-type variant"),
341            Actual::TupleVariant => write!(f, "tuple variant"),
342            Actual::StructVariant => write!(f, "struct variant"),
343            Actual::Other(v) => v.fmt(f),
344        }
345    }
346}
347
348impl From<de::Unexpected<'_>> for Actual {
349    fn from(value: de::Unexpected<'_>) -> Actual {
350        match value {
351            de::Unexpected::Bool(v) => Actual::Bool(v),
352            de::Unexpected::Unsigned(v) => Actual::Unsigned(v as u128),
353            de::Unexpected::Signed(v) => Actual::Signed(v as i128),
354            de::Unexpected::Float(v) => Actual::Float(v),
355            de::Unexpected::Char(v) => Actual::Char(v),
356            de::Unexpected::Str(v) => Actual::Str(v.into()),
357            de::Unexpected::Bytes(v) => Actual::Bytes(v.into()),
358            de::Unexpected::Unit => Actual::Unit,
359            de::Unexpected::Option => Actual::Option,
360            de::Unexpected::NewtypeStruct => Actual::NewtypeStruct,
361            de::Unexpected::Seq => Actual::Seq,
362            de::Unexpected::Map => Actual::Map,
363            de::Unexpected::Enum => Actual::Enum,
364            de::Unexpected::UnitVariant => Actual::UnitVariant,
365            de::Unexpected::NewtypeVariant => Actual::NewtypeVariant,
366            de::Unexpected::TupleVariant => Actual::TupleVariant,
367            de::Unexpected::StructVariant => Actual::StructVariant,
368            de::Unexpected::Other(v) => Actual::Other(v.into())
369        }
370    }
371}
372
373impl de::Error for Error {
374    fn custom<T: Display>(msg: T) -> Self {
375        Kind::Message(msg.to_string()).into()
376    }
377
378    fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
379        Kind::InvalidType(unexp.into(), exp.to_string()).into()
380    }
381
382    fn invalid_value(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
383        Kind::InvalidValue(unexp.into(), exp.to_string()).into()
384    }
385
386    fn invalid_length(len: usize, exp: &dyn de::Expected) -> Self {
387        Kind::InvalidLength(len, exp.to_string()).into()
388    }
389
390    fn unknown_variant(variant: &str, expected: &'static [&'static str]) -> Self {
391        Kind::UnknownVariant(variant.into(), expected).into()
392    }
393
394    fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self {
395        Kind::UnknownField(field.into(), expected).into()
396    }
397
398    fn missing_field(field: &'static str) -> Self {
399        Kind::MissingField(field.into()).into()
400    }
401
402    fn duplicate_field(field: &'static str) -> Self {
403        Kind::DuplicateField(field).into()
404    }
405}
406
407impl ser::Error for Error {
408    fn custom<T: Display>(msg: T) -> Self {
409        Kind::Message(msg.to_string()).into()
410    }
411}
412
413impl From<Kind> for Error {
414    fn from(kind: Kind) -> Error {
415        Error {
416            tag: Tag::Default,
417            path: vec![],
418            profile: None,
419            metadata: None,
420            prev: None,
421            kind,
422        }
423    }
424}
425
426impl From<&str> for Error {
427    fn from(string: &str) -> Error {
428        Kind::Message(string.into()).into()
429    }
430}
431
432impl From<String> for Error {
433    fn from(string: String) -> Error {
434        Kind::Message(string).into()
435    }
436}
437
438impl Display for Kind {
439    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
440        match self {
441            Kind::Message(msg) => f.write_str(&msg),
442            Kind::InvalidType(v, exp) => {
443                write!(f, "invalid type: found {}, expected {}", v, exp)
444            }
445            Kind::InvalidValue(v, exp) => {
446                write!(f, "invalid value {}, expected {}", v, exp)
447            },
448            Kind::InvalidLength(v, exp) => {
449                write!(f, "invalid length {}, expected {}", v, exp)
450            },
451            Kind::UnknownVariant(v, exp) => {
452                write!(f, "unknown variant: found `{}`, expected `{}`", v, OneOf(exp))
453            }
454            Kind::UnknownField(v, exp) => {
455                write!(f, "unknown field: found `{}`, expected `{}`", v, OneOf(exp))
456            }
457            Kind::MissingField(v) => {
458                write!(f, "missing field `{}`", v)
459            }
460            Kind::DuplicateField(v) => {
461                write!(f, "duplicate field `{}`", v)
462            }
463            Kind::ISizeOutOfRange(v) => {
464                write!(f, "signed integer `{}` is out of range", v)
465            }
466            Kind::USizeOutOfRange(v) => {
467                write!(f, "unsigned integer `{}` is out of range", v)
468            }
469            Kind::Unsupported(v) => {
470                write!(f, "unsupported type `{}`", v)
471            }
472            Kind::UnsupportedKey(a, e) => {
473                write!(f, "unsupported type `{}` for key: must be `{}`", a, e)
474            }
475        }
476    }
477}
478
479impl Display for Error {
480    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
481        self.kind.fmt(f)?;
482
483        if let (Some(profile), Some(md)) = (&self.profile, &self.metadata) {
484            if !self.path.is_empty() {
485                let key = md.interpolate(profile, &self.path);
486                write!(f, " for key {:?}", key)?;
487            }
488        }
489
490        if let Some(md) = &self.metadata {
491            if let Some(source) = &md.source {
492                write!(f, " in {} {}", source, md.name)?;
493            } else {
494                write!(f, " in {}", md.name)?;
495            }
496        }
497
498        if let Some(prev) = &self.prev {
499            write!(f, "\n{}", prev)?;
500        }
501
502        Ok(())
503    }
504}
505
506impl std::error::Error for Error {}
507
508/// A structure that implements [`de::Expected`] signaling that one of the types
509/// in the slice was expected.
510pub struct OneOf(pub &'static [&'static str]);
511
512impl fmt::Display for OneOf {
513    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
514        match self.0.len() {
515            0 => write!(f, "none"),
516            1 => write!(f, "`{}`", self.0[0]),
517            2 => write!(f, "`{}` or `{}`", self.0[0], self.0[1]),
518            _ => {
519                write!(f, "one of ")?;
520                for (i, alt) in self.0.iter().enumerate() {
521                    if i > 0 { write!(f, ", ")?; }
522                    write!(f, "`{}`", alt)?;
523                }
524
525                Ok(())
526            }
527        }
528    }
529}
530
531impl de::Expected for OneOf {
532    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
533        Display::fmt(self, f)
534    }
535}