cargo/util/context/
error.rs

1use std::fmt;
2
3use crate::util::ConfigValue;
4use crate::util::context::ConfigKey;
5use crate::util::context::Definition;
6use crate::util::context::key::ArrayItemKeyPath;
7
8/// Internal error for serde errors.
9#[derive(Debug)]
10pub struct ConfigError {
11    error: anyhow::Error,
12    definition: Option<Definition>,
13}
14
15impl ConfigError {
16    pub(super) fn new(message: String, definition: Definition) -> ConfigError {
17        ConfigError {
18            error: anyhow::Error::msg(message),
19            definition: Some(definition),
20        }
21    }
22
23    pub(super) fn expected(key: &ConfigKey, expected: &str, found: &ConfigValue) -> ConfigError {
24        ConfigError {
25            error: anyhow::anyhow!(
26                "`{}` expected {}, but found a {}",
27                key,
28                expected,
29                found.desc()
30            ),
31            definition: Some(found.definition().clone()),
32        }
33    }
34
35    pub(super) fn is_missing_field(&self) -> bool {
36        self.error.downcast_ref::<MissingFieldError>().is_some()
37    }
38
39    pub(super) fn missing(key: &ConfigKey) -> ConfigError {
40        ConfigError {
41            error: anyhow::anyhow!("missing config key `{}`", key),
42            definition: None,
43        }
44    }
45
46    pub(super) fn with_key_context(
47        self,
48        key: &ConfigKey,
49        definition: Option<Definition>,
50    ) -> ConfigError {
51        ConfigError {
52            error: anyhow::Error::from(self)
53                .context(format!("could not load config key `{}`", key)),
54            definition: definition,
55        }
56    }
57
58    pub(super) fn with_array_item_key_context(
59        self,
60        key: &ArrayItemKeyPath,
61        definition: Option<Definition>,
62    ) -> ConfigError {
63        ConfigError {
64            error: anyhow::Error::from(self).context(format!("failed to parse config at `{key}`")),
65            definition,
66        }
67    }
68}
69
70impl std::error::Error for ConfigError {
71    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
72        self.error.source()
73    }
74}
75
76impl fmt::Display for ConfigError {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        if let Some(definition) = &self.definition {
79            write!(f, "error in {}: {}", definition, self.error)
80        } else {
81            self.error.fmt(f)
82        }
83    }
84}
85
86#[derive(Debug)]
87struct MissingFieldError(String);
88
89impl fmt::Display for MissingFieldError {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        write!(f, "missing field `{}`", self.0)
92    }
93}
94
95impl std::error::Error for MissingFieldError {}
96
97impl serde::de::Error for ConfigError {
98    fn custom<T: fmt::Display>(msg: T) -> Self {
99        ConfigError {
100            error: anyhow::Error::msg(msg.to_string()),
101            definition: None,
102        }
103    }
104
105    fn missing_field(field: &'static str) -> Self {
106        ConfigError {
107            error: anyhow::Error::new(MissingFieldError(field.to_string())),
108            definition: None,
109        }
110    }
111}
112
113impl From<anyhow::Error> for ConfigError {
114    fn from(error: anyhow::Error) -> Self {
115        ConfigError {
116            error,
117            definition: None,
118        }
119    }
120}