1use std::{fmt, sync::Arc};
4
5use serde::{de, de::Error};
6
7use crate::{
8 metadata::{ConfigMetadata, ParamMetadata},
9 value::{ValueOrigin, WithOrigin},
10};
11
12#[derive(Debug)]
15pub struct DeserializeConfigError(());
16
17impl DeserializeConfigError {
18 pub(crate) fn new() -> Self {
19 Self(())
20 }
21}
22
23#[derive(Debug, Clone, Copy)]
24pub(crate) enum LocationInConfig {
25 Param(usize),
26}
27
28#[doc(hidden)] #[derive(Debug, Clone, Copy)]
30#[non_exhaustive]
31pub enum ParseErrorCategory {
32 Generic,
34 MissingField,
36}
37
38#[derive(Debug)]
40#[non_exhaustive]
41pub enum LowLevelError {
42 Json {
44 err: serde_json::Error,
45 category: ParseErrorCategory,
46 },
47 #[doc(hidden)] InvalidArray,
49 #[doc(hidden)] InvalidObject,
51 #[doc(hidden)] Validation,
53}
54
55impl From<serde_json::Error> for LowLevelError {
56 fn from(err: serde_json::Error) -> Self {
57 Self::Json {
58 err,
59 category: ParseErrorCategory::Generic,
60 }
61 }
62}
63
64impl fmt::Display for LowLevelError {
65 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
66 match self {
67 Self::Json { err, .. } => fmt::Display::fmt(err, formatter),
68 Self::InvalidArray => formatter.write_str("error(s) deserializing array items"),
69 Self::InvalidObject => formatter.write_str("error(s) deserializing object entries"),
70 Self::Validation => formatter.write_str("validation failed"),
71 }
72 }
73}
74
75pub type ErrorWithOrigin = WithOrigin<LowLevelError>;
77
78impl ErrorWithOrigin {
79 pub(crate) fn json(err: serde_json::Error, origin: Arc<ValueOrigin>) -> Self {
80 Self::new(err.into(), origin)
81 }
82
83 pub fn custom(message: impl fmt::Display) -> Self {
85 Self::json(de::Error::custom(message), Arc::default())
86 }
87}
88
89impl de::Error for ErrorWithOrigin {
90 fn custom<T: fmt::Display>(msg: T) -> Self {
91 Self::json(de::Error::custom(msg), Arc::default())
92 }
93
94 fn missing_field(field: &'static str) -> Self {
95 let err = LowLevelError::Json {
96 err: de::Error::missing_field(field),
97 category: ParseErrorCategory::MissingField,
98 };
99 Self::new(err, Arc::default())
100 }
101}
102
103impl fmt::Display for ErrorWithOrigin {
104 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
105 write!(formatter, "[{}]: {}", self.origin, self.inner)
106 }
107}
108
109impl std::error::Error for ErrorWithOrigin {
110 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
111 match &self.inner {
112 LowLevelError::Json { err, .. } => Some(err),
113 LowLevelError::InvalidArray
114 | LowLevelError::InvalidObject
115 | LowLevelError::Validation => None,
116 }
117 }
118}
119
120pub struct ParseError {
122 pub(crate) inner: serde_json::Error,
123 pub(crate) category: ParseErrorCategory,
124 pub(crate) path: String,
125 pub(crate) origin: Arc<ValueOrigin>,
126 pub(crate) config: &'static ConfigMetadata,
127 pub(crate) location_in_config: Option<LocationInConfig>,
128 pub(crate) validation: Option<String>,
129}
130
131impl fmt::Debug for ParseError {
132 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
133 formatter
134 .debug_struct("ParseError")
135 .field("inner", &self.inner)
136 .field("origin", &self.origin)
137 .field("path", &self.path)
138 .field("config.ty", &self.config.ty)
139 .field("location_in_config", &self.location_in_config)
140 .field("validation", &self.validation)
141 .finish_non_exhaustive()
142 }
143}
144
145impl fmt::Display for ParseError {
146 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
147 let field = self.location_in_config.and_then(|location| {
148 Some(match location {
149 LocationInConfig::Param(idx) => {
150 let param = self.config.params.get(idx)?;
151 format!("param `{}` in ", param.name)
152 }
153 })
154 });
155 let field = field.as_deref().unwrap_or("");
156
157 let origin = if matches!(self.origin(), ValueOrigin::Unknown) {
158 String::new()
159 } else {
160 format!(" [origin: {}]", self.origin)
161 };
162
163 let failed_action = if let Some(validation) = &self.validation {
164 format!("validating '{validation}' for")
165 } else {
166 "parsing".to_owned()
167 };
168
169 write!(
170 formatter,
171 "error {failed_action} {field}`{config}` at `{path}`{origin}: {err}",
172 err = self.inner,
173 config = self.config.ty.name_in_code(),
174 path = self.path
175 )
176 }
177}
178
179impl std::error::Error for ParseError {
180 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
181 Some(&self.inner)
182 }
183}
184
185impl ParseError {
186 pub(crate) fn generic(path: String, config: &'static ConfigMetadata) -> Self {
187 Self {
188 inner: serde_json::Error::custom("unspecified error deserializing configuration"),
189 category: ParseErrorCategory::Generic,
190 path,
191 origin: Arc::default(),
192 config,
193 location_in_config: None,
194 validation: None,
195 }
196 }
197
198 pub fn inner(&self) -> &serde_json::Error {
200 &self.inner
201 }
202
203 #[doc(hidden)]
204 pub fn category(&self) -> ParseErrorCategory {
205 self.category
206 }
207
208 pub fn path(&self) -> &str {
210 &self.path
211 }
212
213 pub fn origin(&self) -> &ValueOrigin {
215 &self.origin
216 }
217
218 pub fn validation(&self) -> Option<&str> {
220 self.validation.as_deref()
221 }
222
223 pub fn config(&self) -> &'static ConfigMetadata {
225 self.config
226 }
227
228 pub fn param(&self) -> Option<&'static ParamMetadata> {
231 let LocationInConfig::Param(idx) = self.location_in_config?;
232 self.config.params.get(idx)
233 }
234}
235
236#[derive(Debug, Default)]
238pub struct ParseErrors {
239 errors: Vec<ParseError>,
240}
241
242impl ParseErrors {
243 pub(crate) fn push(&mut self, err: ParseError) {
244 self.errors.push(err);
245 }
246
247 pub fn iter(&self) -> impl Iterator<Item = &ParseError> + '_ {
249 self.errors.iter()
250 }
251
252 #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize {
255 self.errors.len()
256 }
257
258 #[allow(clippy::missing_panics_doc)] pub fn first(&self) -> &ParseError {
261 self.errors.first().expect("no errors")
262 }
263
264 pub(crate) fn truncate(&mut self, len: usize) {
265 self.errors.truncate(len);
266 }
267}
268
269impl IntoIterator for ParseErrors {
270 type Item = ParseError;
271 type IntoIter = std::vec::IntoIter<ParseError>;
272
273 fn into_iter(self) -> Self::IntoIter {
274 self.errors.into_iter()
275 }
276}
277
278impl fmt::Display for ParseErrors {
279 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
280 for err in &self.errors {
281 writeln!(formatter, "{err}")?;
282 }
283 Ok(())
284 }
285}
286
287impl std::error::Error for ParseErrors {}
288
289impl FromIterator<ParseError> for Result<(), ParseErrors> {
290 fn from_iter<I: IntoIterator<Item = ParseError>>(iter: I) -> Self {
291 let errors: Vec<_> = iter.into_iter().collect();
292 if errors.is_empty() {
293 Ok(())
294 } else {
295 Err(ParseErrors { errors })
296 }
297 }
298}