Skip to main content

deser_core/
error.rs

1//! Error interface.
2use alloc::borrow::Cow;
3use alloc::boxed::Box;
4use alloc::format;
5use alloc::string::String;
6use alloc::vec::Vec;
7use core::any::{Any, TypeId};
8use core::fmt;
9
10use crate::{Position, State};
11
12/// Describes the kind of error.
13#[derive(Debug, Eq, PartialEq, Copy, Clone)]
14pub enum ErrorKind {
15    UnsupportedType,
16    Unexpected,
17    MissingField,
18    OutOfRange,
19    WrongLength,
20    EndOfFile,
21    /// Reading or writing failed (see `deser::io`).  The IO error is
22    /// the [`source`](std::error::Error::source) of the error.
23    Io,
24}
25
26/// Additional information attached to an [`Error`].
27///
28/// Besides the location in the input, which is built into errors, layers
29/// and other code can attach typed values to errors with
30/// [`Error::with_attachment`] and retrieve them with
31/// [`Error::attachment`].  An error holds at most one attachment per type.
32/// For instance the `deser-path` crate attaches the path of the value an
33/// error refers to.
34///
35/// Attachments can contribute to the [`Display`](fmt::Display) output of
36/// the error with [`fmt_context`](Self::fmt_context).
37///
38/// ```
39/// use std::fmt;
40/// use deser::{Error, ErrorAttachment, ErrorKind};
41///
42/// #[derive(Debug)]
43/// struct FileName(String);
44///
45/// impl ErrorAttachment for FileName {
46///     fn fmt_context(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47///         write!(f, " in {}", self.0)
48///     }
49/// }
50///
51/// let err = Error::new(ErrorKind::Unexpected, "unexpected string")
52///     .with_position(12, 2, 5)
53///     .with_attachment(FileName("config.json".into()));
54/// assert_eq!(err.attachment::<FileName>().unwrap().0, "config.json");
55/// assert_eq!(
56///     err.to_string(),
57///     "Unexpected: unexpected string at line 2 column 5 in config.json"
58/// );
59/// ```
60pub trait ErrorAttachment: Any + fmt::Debug + Send + Sync {
61    /// Writes the attachment as part of the error message.
62    ///
63    /// The output is appended to the message and the location of the
64    /// error, so it typically starts with a space.  By default attachments
65    /// are not shown.
66    fn fmt_context(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        let _ = f;
68        Ok(())
69    }
70}
71
72/// Adds context to errors, see [`State::add_error_context`](crate::State::add_error_context).
73///
74/// This is typically implemented by the extension type which holds the
75/// information that is attached to errors.
76pub trait ErrorContext: 'static {
77    /// Adds context to an error.
78    ///
79    /// This is invoked with the state as it was when the error happened.
80    /// Context that is already attached to the error should not be
81    /// replaced.
82    fn add_context(err: Error, state: &State) -> Error;
83}
84
85/// An error for deser.
86///
87/// Besides a kind and a message an error can carry context: the location
88/// in the input it refers to (see [`offset`](Self::offset),
89/// [`line`](Self::line) and [`column`](Self::column)) and typed
90/// attachments (see [`ErrorAttachment`]).  The context is part of the
91/// [`Display`](fmt::Display) output:
92///
93/// ```
94/// use deser::{Error, ErrorKind};
95///
96/// let err = Error::new(ErrorKind::Unexpected, "unexpected string")
97///     .with_position(12, 2, 5);
98/// assert_eq!(
99///     err.to_string(),
100///     "Unexpected: unexpected string at line 2 column 5"
101/// );
102/// ```
103///
104/// Errors raised while deserializing a value (for instance by a
105/// [`Sink`](crate::de::Sink)) get the context attached by the
106/// [`DeserializeDriver`](crate::de::DeserializeDriver): the start of the
107/// input range of the event (see [`State::input_range`](crate::State::input_range))
108/// and the context of the types registered with
109/// [`State::add_error_context`](crate::State::add_error_context).  Formats
110/// resolve the offsets into lines and columns.
111///
112/// # Multiple Errors
113///
114/// An error can hold multiple errors, for instance if deserialization
115/// continued after an error to report all problems of the input at once
116/// (see [`State::set_collect_errors`](crate::State::set_collect_errors)).
117/// The accessors ([`kind`](Self::kind), [`message`](Self::message), the
118/// location and the attachments) refer to the first of them, all of them
119/// are iterated with [`errors`](Self::errors).  The
120/// [`Display`](fmt::Display) output mentions how many more errors there
121/// are, with the alternate flag (`{:#}`) it lists all of them, one per
122/// line:
123///
124/// ```
125/// use deser::{Error, ErrorKind};
126///
127/// let err = Error::from_errors([
128///     Error::new(ErrorKind::MissingField, "missing field `a`")
129///         .with_offset(0),
130///     Error::new(ErrorKind::Unexpected, "unexpected string")
131///         .with_offset(9),
132/// ])
133/// .unwrap()
134/// .resolve_position(b"{\n  \"b\": \"x\"}");
135/// assert_eq!(err.error_count(), 2);
136/// assert_eq!(err.kind(), ErrorKind::MissingField);
137/// assert_eq!(
138///     err.to_string(),
139///     "MissingField: missing field `a` at line 1 column 1 \
140///      (and 1 more error)"
141/// );
142/// assert_eq!(
143///     format!("{:#}", err),
144///     "MissingField: missing field `a` at line 1 column 1\n\
145///      Unexpected: unexpected string at line 2 column 8"
146/// );
147/// ```
148pub struct Error {
149    // boxed so that results stay small.  Errors are rare but results are
150    // passed around for every single value.
151    inner: Box<ErrorInner>,
152}
153
154enum ErrorInner {
155    Single(ErrorData),
156    // at least two errors, all of them are single errors.  The first one
157    // is the error the accessors refer to.
158    Multiple(Vec<Error>),
159}
160
161#[derive(Debug)]
162struct ErrorData {
163    kind: ErrorKind,
164    msg: Cow<'static, str>,
165    source: Option<Box<dyn core::error::Error + Send + Sync>>,
166    offset: Option<usize>,
167    // line and column (1-based)
168    line_column: Option<(usize, usize)>,
169    // in the order they were attached, at most one per type
170    attachments: Vec<Attachment>,
171    // `true` once the driver attached the context of the current event.
172    has_context: bool,
173    // `true` once the error was collected (see `CollectedErrors`)
174    collected: bool,
175}
176
177#[derive(Debug)]
178struct Attachment {
179    // Invariant: the type of the value
180    type_id: TypeId,
181    value: Box<dyn ErrorAttachment>,
182}
183
184impl Error {
185    /// Creates a new error.
186    #[cold]
187    pub fn new<M: Into<Cow<'static, str>>>(kind: ErrorKind, msg: M) -> Error {
188        Error {
189            inner: Box::new(ErrorInner::Single(ErrorData {
190                kind,
191                msg: msg.into(),
192                source: None,
193                offset: None,
194                line_column: None,
195                attachments: Vec::new(),
196                has_context: false,
197                collected: false,
198            })),
199        }
200    }
201
202    /// Combines errors into one.
203    ///
204    /// Errors that hold multiple errors are flattened (see
205    /// [`push_error`](Self::push_error)).  Returns `None` if there are no
206    /// errors.
207    pub fn from_errors<I: IntoIterator<Item = Error>>(errors: I) -> Option<Error> {
208        let mut errors = errors.into_iter();
209        let mut rv = errors.next()?;
210        for err in errors {
211            rv.push_error(err);
212        }
213        Some(rv)
214    }
215
216    /// Creates the error for a value that is serialized while another one
217    /// is only partially written.
218    ///
219    /// Stream serializers return this once they are
220    /// [in progress](crate::ser::StreamSerializer::in_progress) and are asked
221    /// to serialize another value.
222    #[cold]
223    pub fn in_progress() -> Error {
224        Error::new(
225            ErrorKind::Unexpected,
226            "a value was only partially written, the stream cannot continue",
227        )
228    }
229
230    /// Adds an error to this error.
231    ///
232    /// If the error that is added holds multiple errors, they are added
233    /// individually: errors do not nest (see [`errors`](Self::errors)).
234    pub fn push_error(&mut self, err: Error) {
235        let errors = self.make_multiple();
236        match *err.inner {
237            ErrorInner::Single(data) => errors.push(Error {
238                inner: Box::new(ErrorInner::Single(data)),
239            }),
240            ErrorInner::Multiple(others) => errors.extend(others),
241        }
242    }
243
244    /// Turns the error into one that holds multiple errors.
245    fn make_multiple(&mut self) -> &mut Vec<Error> {
246        if let ErrorInner::Single(_) = *self.inner {
247            let first = core::mem::replace(&mut *self.inner, ErrorInner::Multiple(Vec::new()));
248            if let ErrorInner::Multiple(ref mut errors) = *self.inner {
249                errors.push(Error {
250                    inner: Box::new(first),
251                });
252            }
253        }
254        match *self.inner {
255            ErrorInner::Multiple(ref mut errors) => errors,
256            ErrorInner::Single(_) => unreachable!(),
257        }
258    }
259
260    /// Iterates over the errors this error holds.
261    ///
262    /// For an error that holds a single error, this is the error itself.
263    /// The errors that are returned hold a single error each.
264    pub fn errors(&self) -> impl Iterator<Item = &Error> {
265        match *self.inner {
266            ErrorInner::Single(_) => core::slice::from_ref(self).iter(),
267            ErrorInner::Multiple(ref errors) => errors.iter(),
268        }
269    }
270
271    /// Returns the number of errors this error holds.
272    pub fn error_count(&self) -> usize {
273        match *self.inner {
274            ErrorInner::Single(_) => 1,
275            ErrorInner::Multiple(ref errors) => errors.len(),
276        }
277    }
278
279    /// Returns the data of the (first) error.
280    fn data(&self) -> &ErrorData {
281        match *self.inner {
282            ErrorInner::Single(ref data) => data,
283            ErrorInner::Multiple(ref errors) => errors[0].data(),
284        }
285    }
286
287    /// Returns the data of the (first) error mutably.
288    fn data_mut(&mut self) -> &mut ErrorData {
289        match *self.inner {
290            ErrorInner::Single(ref mut data) => data,
291            ErrorInner::Multiple(ref mut errors) => errors[0].data_mut(),
292        }
293    }
294
295    /// Applies a function to every error this error holds.
296    pub(crate) fn map_each(mut self, mut f: impl FnMut(Error) -> Error) -> Error {
297        if let ErrorInner::Multiple(ref mut errors) = *self.inner {
298            for err in errors.iter_mut() {
299                let taken = core::mem::replace(err, Error::new(ErrorKind::Unexpected, ""));
300                *err = f(taken);
301            }
302            self
303        } else {
304            f(self)
305        }
306    }
307
308    /// Returns the number of errors this error holds that were not
309    /// collected yet.
310    pub(crate) fn uncollected_count(&self) -> usize {
311        self.errors().filter(|err| !err.data().collected).count()
312    }
313
314    /// Marks all errors this error holds as collected.
315    pub(crate) fn mark_collected(mut self) -> Error {
316        self = self.map_each(|mut err| {
317            err.data_mut().collected = true;
318            err
319        });
320        self
321    }
322
323    /// Attaches another error as source to this error.
324    pub fn with_source<E: core::error::Error + Send + Sync + 'static>(mut self, source: E) -> Self {
325        self.data_mut().source = Some(Box::new(source));
326        self
327    }
328
329    /// Returns the kind of the error.
330    pub fn kind(&self) -> ErrorKind {
331        self.data().kind
332    }
333
334    /// Returns the message of the error (without context).
335    pub fn message(&self) -> &str {
336        &self.data().msg
337    }
338
339    /// Sets the byte offset in the input the error refers to.
340    ///
341    /// A previously set line and column are discarded.
342    pub fn with_offset(mut self, offset: usize) -> Self {
343        let data = self.data_mut();
344        data.offset = Some(offset);
345        data.line_column = None;
346        self
347    }
348
349    /// Sets the byte offset together with its line and column (1-based).
350    pub fn with_position(mut self, offset: usize, line: usize, column: usize) -> Self {
351        let data = self.data_mut();
352        data.offset = Some(offset);
353        data.line_column = Some((line, column));
354        self
355    }
356
357    /// Resolves the offset into line and column.
358    ///
359    /// The source is the input the offset refers to.  Columns are counted
360    /// in characters (bytes that are not UTF-8 continuation bytes).  If the
361    /// error has no offset or already has a line and column, it's returned
362    /// unchanged.  Text formats call this for the errors they return.
363    ///
364    /// ```
365    /// use deser::{Error, ErrorKind};
366    ///
367    /// let err = Error::new(ErrorKind::Unexpected, "bad value")
368    ///     .with_offset(7)
369    ///     .resolve_position(b"[1,\n  x]");
370    /// assert_eq!((err.line(), err.column()), (Some(2), Some(4)));
371    /// ```
372    ///
373    /// The positions of further errors (see [`errors`](Self::errors)) are
374    /// resolved as well.
375    pub fn resolve_position(self, source: &[u8]) -> Self {
376        self.map_each(|mut err| {
377            let data = err.data_mut();
378            if let (Some(offset), None) = (data.offset, data.line_column) {
379                let pos = Position::of(source, offset);
380                data.line_column = Some((pos.line, pos.column));
381            }
382            err
383        })
384    }
385
386    /// Moves the position of the error by the position of the input it
387    /// refers to.
388    ///
389    /// This is used for errors of inputs which are part of a larger input,
390    /// the base is the position of the start of the part.
391    pub(crate) fn shift_position(self, base: Position) -> Self {
392        self.map_each(|mut err| {
393            let data = err.data_mut();
394            if let Some(ref mut error_offset) = data.offset {
395                *error_offset += base.offset;
396            }
397            if let Some((ref mut error_line, ref mut error_column)) = data.line_column {
398                if *error_line == 1 {
399                    *error_column += base.column - 1;
400                }
401                *error_line += base.line - 1;
402            }
403            err
404        })
405    }
406
407    /// Returns the byte offset in the input the error refers to.
408    pub fn offset(&self) -> Option<usize> {
409        self.data().offset
410    }
411
412    /// Returns the line (1-based) the error refers to.
413    pub fn line(&self) -> Option<usize> {
414        self.data().line_column.map(|x| x.0)
415    }
416
417    /// Returns the column (1-based, in characters) the error refers to.
418    pub fn column(&self) -> Option<usize> {
419        self.data().line_column.map(|x| x.1)
420    }
421
422    /// Attaches a value to the error.
423    ///
424    /// An attachment of the same type is replaced but keeps its position
425    /// in the [`Display`](fmt::Display) output.  See [`ErrorAttachment`].
426    pub fn with_attachment<T: ErrorAttachment>(mut self, value: T) -> Self {
427        let type_id = TypeId::of::<T>();
428        let value = Box::new(value);
429        let attachments = &mut self.data_mut().attachments;
430        match attachments.iter_mut().find(|x| x.type_id == type_id) {
431            Some(attachment) => attachment.value = value,
432            None => attachments.push(Attachment { type_id, value }),
433        }
434        self
435    }
436
437    /// Returns the attachment of the given type.
438    pub fn attachment<T: ErrorAttachment>(&self) -> Option<&T> {
439        let type_id = TypeId::of::<T>();
440        let attachment = self
441            .data()
442            .attachments
443            .iter()
444            .find(|x| x.type_id == type_id)?;
445        (&*attachment.value as &dyn Any).downcast_ref()
446    }
447
448    /// Returns the attachment of the given type mutably.
449    pub fn attachment_mut<T: ErrorAttachment>(&mut self) -> Option<&mut T> {
450        let type_id = TypeId::of::<T>();
451        let attachment = self
452            .data_mut()
453            .attachments
454            .iter_mut()
455            .find(|x| x.type_id == type_id)?;
456        (&mut *attachment.value as &mut dyn Any).downcast_mut()
457    }
458
459    /// Iterates over the attachments in the order they were attached.
460    pub fn attachments(&self) -> impl Iterator<Item = &dyn ErrorAttachment> {
461        self.data().attachments.iter().map(|x| &*x.value)
462    }
463
464    /// Returns `true` if the context of an event was attached.
465    pub(crate) fn has_context(&self) -> bool {
466        self.data().has_context
467    }
468
469    /// Marks the context of an event as attached.
470    pub(crate) fn set_has_context(&mut self) {
471        self.data_mut().has_context = true;
472    }
473}
474
475impl fmt::Debug for Error {
476    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477        let data = match *self.inner {
478            ErrorInner::Single(ref data) => data,
479            ErrorInner::Multiple(ref errors) => {
480                return f.debug_tuple("Errors").field(errors).finish();
481            }
482        };
483        let mut s = f.debug_struct("Error");
484        s.field("kind", &data.kind).field("msg", &data.msg);
485        if let Some(offset) = data.offset {
486            s.field("offset", &offset);
487        }
488        if let Some((line, column)) = data.line_column {
489            s.field("line", &line).field("column", &column);
490        }
491        if !data.attachments.is_empty() {
492            s.field("attachments", &DebugAttachments(&data.attachments));
493        }
494        s.field("source", &data.source).finish()
495    }
496}
497
498impl ErrorData {
499    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500        write!(f, "{:?}: {}", self.kind, self.msg)?;
501        match (self.line_column, self.offset) {
502            (Some((line, column)), _) => write!(f, " at line {} column {}", line, column)?,
503            (None, Some(offset)) => write!(f, " at offset {}", offset)?,
504            (None, None) => {}
505        }
506        for attachment in self.attachments.iter() {
507            attachment.value.fmt_context(f)?;
508        }
509        Ok(())
510    }
511}
512
513impl fmt::Display for Error {
514    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
515        let errors = match *self.inner {
516            ErrorInner::Single(ref data) => return data.fmt(f),
517            ErrorInner::Multiple(ref errors) => errors,
518        };
519        errors[0].data().fmt(f)?;
520        if f.alternate() {
521            for err in &errors[1..] {
522                writeln!(f)?;
523                err.data().fmt(f)?;
524            }
525        } else if errors.len() == 2 {
526            write!(f, " (and 1 more error)")?;
527        } else {
528            write!(f, " (and {} more errors)", errors.len() - 1)?;
529        }
530        Ok(())
531    }
532}
533
534struct DebugAttachments<'a>(&'a [Attachment]);
535
536impl fmt::Debug for DebugAttachments<'_> {
537    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
538        f.debug_list()
539            .entries(self.0.iter().map(|x| &x.value))
540            .finish()
541    }
542}
543
544#[cfg(feature = "std")]
545impl From<std::io::Error> for Error {
546    fn from(err: std::io::Error) -> Error {
547        Error::new(ErrorKind::Io, err.to_string()).with_source(err)
548    }
549}
550
551impl core::error::Error for Error {
552    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
553        self.data().source.as_ref().map(|err| err.as_ref() as _)
554    }
555}
556
557/// Creates an error that is thrown away.
558///
559/// While errors are discarded (see `State::discard_errors`) the common
560/// errors are created with this instead of building a message nobody reads.
561#[cold]
562#[inline(never)]
563pub(crate) fn discarded_error(kind: ErrorKind) -> Error {
564    Error::new(kind, "discarded error")
565}
566
567/// Creates the error for a value that failed to convert or validate.
568#[cold]
569pub(crate) fn conversion_error<E: fmt::Display>(err: E) -> Error {
570    Error::new(ErrorKind::Unexpected, format!("invalid value: {}", err))
571}
572
573/// Creates the error for an unknown variant.
574///
575/// `tag` is the name that was given (if it can be a name), `type_name` the
576/// name of the enum and `names` are the names of the variants.
577#[cold]
578pub fn unknown_variant(tag: Option<&str>, type_name: &str, names: &[&str]) -> Error {
579    let mut msg = String::from("unknown variant");
580    if let Some(tag) = tag {
581        msg.push_str(" `");
582        msg.push_str(tag);
583        msg.push('`');
584    }
585    msg.push_str(" of ");
586    msg.push_str(type_name);
587    push_expected(&mut msg, names, "variants");
588    Error::new(ErrorKind::Unexpected, msg)
589}
590
591/// Appends the expected names to an error message.
592///
593/// `what` is what the names are, for the message if there are none.
594pub(crate) fn push_expected(msg: &mut String, names: &[&str], what: &str) {
595    match names {
596        [] => {
597            msg.push_str(", there are no ");
598            msg.push_str(what);
599        }
600        [name] => {
601            msg.push_str(", expected `");
602            msg.push_str(name);
603            msg.push('`');
604        }
605        [first, second] => {
606            msg.push_str(", expected `");
607            msg.push_str(first);
608            msg.push_str("` or `");
609            msg.push_str(second);
610            msg.push('`');
611        }
612        names => {
613            msg.push_str(", expected one of ");
614            for (idx, name) in names.iter().enumerate() {
615                if idx > 0 {
616                    msg.push_str(", ");
617                }
618                msg.push('`');
619                msg.push_str(name);
620                msg.push('`');
621            }
622        }
623    }
624}