Skip to main content

verify/impls/schemars/
errors.rs

1//! Error definitions used during Schema-related validation.
2
3use crate::span::Span;
4use schemars_crate::schema::{InstanceType, Metadata, SingleOrVec};
5use std::ops::AddAssign;
6
7/// A validation error.
8///
9/// It contains an optional span of the invalid value and optional information about the schema that caused the error.
10#[derive(Debug, Clone, PartialEq)]
11pub struct Error<S: Span> {
12    /// Information about the schema that caused the validation
13    /// error.
14    pub meta: Option<Box<Metadata>>,
15
16    /// The span of the invalid value.
17    pub span: Option<S>,
18
19    /// The actual error details.
20    pub value: ErrorValue<S>,
21}
22
23impl<S: Span> Error<S> {
24    pub(crate) fn new(meta: Option<Box<Metadata>>, span: Option<S>, value: ErrorValue<S>) -> Self {
25        Self { meta, span, value }
26    }
27}
28
29impl<S: Span> core::fmt::Display for Error<S> {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        let mut start_paren = false;
32
33        if f.alternate() {
34            if let Some(span) = &self.span {
35                write!(f, "({:?}", span)?;
36                start_paren = true;
37            }
38
39            if let Some(meta) = &self.meta {
40                if let Some(title) = &meta.title {
41                    if start_paren {
42                        write!(f, r#", schema: "{}""#, title)?;
43                    } else {
44                        write!(f, r#"(schema: "{}""#, title)?;
45                    }
46                    start_paren = true;
47                }
48            }
49        }
50
51        if start_paren {
52            write!(f, ") ")?;
53        }
54
55        write!(f, "{}", self.value)
56    }
57}
58
59/// All the validation errors that can occur.
60#[derive(Debug, Clone, PartialEq)]
61// TODO maybe prefix or group them by type?
62pub enum ErrorValue<S: Span> {
63    /// Indicates that the schema will never match any value.
64    Never,
65
66    /// Indicates that the schema denies unknown properties.
67    UnknownProperty,
68
69    /// Indicates that the schema itself is invalid.
70    InvalidSchema(InvalidSchema),
71
72    /// Indicates incompatible value that cannot be validated
73    /// by a schema.
74    UnsupportedValue(UnsupportedValue),
75
76    /// Indicates invalid type.
77    InvalidType {
78        expected: SingleOrVec<InstanceType>,
79        actual: InstanceType,
80    },
81
82    /// Indicates invalid enum value.
83    InvalidEnumValue { expected: Vec<serde_json::Value> },
84
85    /// Indicates that the number is not multiple of the given value.
86    NotMultipleOf { multiple_of: f64 },
87
88    /// Indicates that the number is less than the given minimum value.
89    LessThanExpected { min: f64, exclusive: bool },
90
91    /// Indicates that the number is more than the given maximum value.
92    MoreThanExpected { max: f64, exclusive: bool },
93
94    /// Indicates that the string doesn't match the given pattern.
95    NoPatternMatch { pattern: String },
96
97    /// Indicates that the string is too long.
98    TooLong { max_length: u32 },
99
100    /// Indicates that the string is too short.
101    TooShort { min_length: u32 },
102
103    /// Indicates that none of the subschemas matched.
104    ///
105    /// Exclusive indicates that exactly one of them must have matched.
106    NoneValid {
107        exclusive: bool,
108        schemas: Vec<Option<Box<Metadata>>>,
109        errors: Vec<Errors<S>>,
110    },
111
112    /// Indicates that more than one of the subschemas matched.
113    MoreThanOneValid { schemas: Vec<Option<Box<Metadata>>>, matched: Vec<Option<Box<Metadata>>> },
114
115    /// Indicates that a not schema matched.
116    ValidNot { matched: Option<Box<Metadata>> },
117
118    /// Indicates that the items in the array are not unique.
119    NotUnique {
120        first: Option<S>,
121        duplicate: Option<S>,
122    },
123
124    /// Indicates that the array doesn't contain the value of a given schema.
125    MustContain { schema: Option<Box<Metadata>> },
126
127    /// Indicates that the array doesn't have enough items.
128    NotEnoughItems { min: usize },
129
130    /// Indicates that the array has too many items.
131    TooManyItems { max: usize },
132
133    /// Indicates that the object has too few properties.
134    NotEnoughProperties { min: usize },
135
136    /// Indicates that the object has too many properties.
137    TooManyProperties { max: usize },
138
139    /// Indicates that a required property is missing.
140    RequiredProperty { name: String },
141
142    /// Any error that does not originate from the validator.
143    Custom(String),
144}
145
146/// Error that occurs when a value cannot be validated
147/// by a schema.
148#[derive(Debug, Clone, PartialEq)]
149pub enum UnsupportedValue {
150    /// Indicates that key of a map is not a string.
151    KeyNotString,
152}
153
154impl core::fmt::Display for UnsupportedValue {
155    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156        match self {
157            UnsupportedValue::KeyNotString => write!(f, "map key must be a string"),
158        }
159    }
160}
161
162/// All errors related to the schema being invalid.
163///
164/// A schema must be valid in order to validate anything with it.
165/// This error occurs if that is not the case.
166///
167/// It is also returned by calling [verify](crate::Verify::verify) on a schema.
168#[derive(Debug, Clone, PartialEq)]
169pub enum InvalidSchema {
170    /// Indicates a missing local definition.
171    MissingDefinition(String),
172
173    /// Indicates an invalid regex pattern in the schema.
174    InvalidPattern {
175        pattern: String,
176        error: regex::Error,
177    },
178
179    /// Indicates an unresolved external reference in the schema.
180    ExternalReference(String),
181}
182
183impl core::fmt::Display for InvalidSchema {
184    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185        match self {
186            InvalidSchema::MissingDefinition(s) => write!(f, r#"missing local definition "{}""#, s),
187            InvalidSchema::InvalidPattern { pattern, error } => {
188                write!(f, r#"invalid regex pattern "{}": {}"#, pattern, error)
189            }
190            InvalidSchema::ExternalReference(r) => write!(
191                f,
192                r#"the schema contains unresolved external reference: "{}""#,
193                r
194            ),
195        }
196    }
197}
198
199impl<S: Span> core::fmt::Display for ErrorValue<S> {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        match self {
202            ErrorValue::Never => write!(f, "no values allowed"),
203            ErrorValue::UnknownProperty => write!(f, "unknown property"),
204            ErrorValue::InvalidSchema(err) => write!(f, "invalid schema: {}", err),
205            ErrorValue::UnsupportedValue(err) => write!(f, "unsupported value: {}", err),
206            ErrorValue::InvalidType { expected, actual } => write!(
207                f,
208                r#"invalid type, expected {}, not "{:?}""#,
209                match expected {
210                    SingleOrVec::Single(s) => {
211                        format!(r#""{:?}""#, s)
212                    }
213                    SingleOrVec::Vec(v) => {
214                        let mut s = "one of {".into();
215
216                        for (i, t) in v.iter().enumerate() {
217                            s += format!(r#""{:?}""#, t).as_str();
218                            if i != v.len() - 1 {
219                                s += ", "
220                            }
221                        }
222                        s += "}";
223
224                        s
225                    }
226                },
227                actual
228            ),
229            ErrorValue::InvalidEnumValue { expected } => {
230                let enum_vals: Vec<String> = expected.iter().map(|v| v.to_string()).collect();
231                write!(
232                    f,
233                    "invalid enum value, expected to be one of {{{}}}",
234                    enum_vals.join(", ")
235                )
236            }
237            ErrorValue::NotMultipleOf { multiple_of } => {
238                write!(f, "the value is expected to be multiple of {}", multiple_of)
239            }
240            ErrorValue::LessThanExpected { min, exclusive } => {
241                if *exclusive {
242                    write!(f, "the value is expected to be more than {}", min)
243                } else {
244                    write!(f, "the value is expected to be at least {}", min)
245                }
246            }
247            ErrorValue::MoreThanExpected { max, exclusive } => {
248                if *exclusive {
249                    write!(f, "the value is expected to be less than {}", max)
250                } else {
251                    write!(f, "the value is expected to be at most {}", max)
252                }
253            }
254            ErrorValue::NoPatternMatch { pattern } => {
255                write!(f, r#"the string must match the pattern "{}""#, pattern)
256            }
257            ErrorValue::TooLong { max_length } => write!(
258                f,
259                r#"the string must not be longer than {} characters"#,
260                max_length
261            ),
262            ErrorValue::TooShort { min_length } => write!(
263                f,
264                r#"the string must must be at least {} characters long"#,
265                min_length
266            ),
267            ErrorValue::NoneValid {
268                exclusive: _,
269                schemas: _,
270                errors,
271            } => {
272                writeln!(f, r#"no subschema matched the value:"#)?;
273
274                for (i, e) in errors.iter().enumerate() {
275                    write!(f, "{}", e)?;
276
277                    if i != errors.len() - 1 {
278                        writeln!(f, "\n")?;
279                    }
280                }
281
282                Ok(())
283            }
284            ErrorValue::MoreThanOneValid { schemas: _, matched } => writeln!(
285                f,
286                r#"expected exactly one schema to match, but {} schemas matched"#,
287                matched.len()
288            ),
289            ErrorValue::ValidNot { matched } => {
290                if let Some(meta) = matched {
291                    if let Some(title) = &meta.title {
292                        return writeln!(f, r#"the value must not be a "{}""#, title);
293                    }
294                }
295
296                writeln!(f, r#"the value is disallowed by a "not" schema"#)
297            }
298            ErrorValue::NotUnique { first: _, duplicate: _ } => {
299                writeln!(f, r#"all items in the array must be unique"#)
300            }
301            ErrorValue::MustContain { schema } => {
302                if let Some(meta) = schema {
303                    if let Some(title) = &meta.title {
304                        return writeln!(
305                            f,
306                            r#"at least one of the items in the array must be "{}""#,
307                            title
308                        );
309                    }
310                }
311
312                writeln!(
313                    f,
314                    r#"at least one of the items in the array must match the given schema"#
315                )
316            }
317            ErrorValue::NotEnoughItems { min } => {
318                write!(f, "the array must have at least {} items", min)
319            }
320            ErrorValue::TooManyItems { max } => {
321                write!(f, "the array cannot have more than {} items", max)
322            }
323            ErrorValue::NotEnoughProperties { min } => {
324                write!(f, "the object must have at least {} properties", min)
325            }
326            ErrorValue::TooManyProperties { max } => {
327                write!(f, "the object cannot have more than {} properties", max)
328            }
329            ErrorValue::RequiredProperty { name } => {
330                write!(f, r#"the required property "{}" is missing"#, name)
331            }
332            ErrorValue::Custom(err) => err.fmt(f),
333        }
334    }
335}
336
337#[cfg(feature = "smallvec")]
338type SmallVecArray<S> = [Error<S>; 10];
339
340#[cfg(feature = "smallvec")]
341/// In a lot of cases there are only 1 or 2 errors
342/// so using smallvec probably helps a bit by removing unnecessary allocations.
343pub(super) type ErrorsInner<S> = smallvec_crate::SmallVec<SmallVecArray<S>>;
344
345#[cfg(not(feature = "smallvec"))]
346pub(super) type ErrorsInner<S> = Vec<Error<S>>;
347
348/// A collection of [Errors](Error), this type is returned from validation.
349#[derive(Debug, Clone, PartialEq)]
350#[repr(transparent)]
351pub struct Errors<S: Span>(pub(crate) ErrorsInner<S>);
352
353impl<S: Span> Errors<S> {
354    pub fn is_empty(&self) -> bool {
355        self.0.is_empty()
356    }
357
358    pub fn len(&self) -> usize {
359        self.0.len()
360    }
361
362    pub fn iter(&self) -> impl Iterator<Item = &Error<S>> {
363        self.0.iter()
364    }
365
366    pub(super) fn new() -> Self {
367        Errors(ErrorsInner::new())
368    }
369
370    pub(super) fn one(error: Error<S>) -> Self {
371        let mut v = ErrorsInner::new();
372        v.push(error);
373        Errors(v)
374    }
375}
376
377impl<S: Span> IntoIterator for Errors<S> {
378    type Item = <ErrorsInner<S> as IntoIterator>::Item;
379
380    #[cfg(feature = "smallvec")]
381    type IntoIter = smallvec_crate::IntoIter<SmallVecArray<S>>;
382
383    #[cfg(not(feature = "smallvec"))]
384    type IntoIter = std::vec::IntoIter<Error<S>>;
385
386    fn into_iter(self) -> Self::IntoIter {
387        self.0.into_iter()
388    }
389}
390
391impl<S: Span> core::fmt::Display for Errors<S> {
392    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
393        for e in &self.0 {
394            writeln!(f, "{}", e)?;
395        }
396        Ok(())
397    }
398}
399
400impl<S: Span> std::error::Error for Errors<S> {}
401impl<S: Span> crate::Error for Errors<S> {
402    fn custom<T: core::fmt::Display>(error: T) -> Self {
403        Self::one(Error::new(
404            None,
405            None,
406            ErrorValue::Custom(error.to_string()),
407        ))
408    }
409}
410
411impl<S: Span> AddAssign for Errors<S> {
412    fn add_assign(&mut self, rhs: Self) {
413        self.0.extend(rhs.0.into_iter());
414    }
415}