diff options
Diffstat (limited to 'vendor/winnow/src/error.rs')
-rw-r--r-- | vendor/winnow/src/error.rs | 459 |
1 files changed, 452 insertions, 7 deletions
diff --git a/vendor/winnow/src/error.rs b/vendor/winnow/src/error.rs index d40e8244b..449bebc55 100644 --- a/vendor/winnow/src/error.rs +++ b/vendor/winnow/src/error.rs @@ -17,6 +17,7 @@ //! - [`ErrorKind`] //! - [`InputError`] (mostly for testing) //! - [`ContextError`] +//! - [`TreeError`] (mostly for testing) //! - [Custom errors][crate::_topic::error] #[cfg(feature = "alloc")] @@ -88,7 +89,7 @@ impl Needed { } } -/// The `Err` enum indicates the parser was not successful +/// Add parse error state to [`ParserError`]s #[derive(Debug, Clone, PartialEq)] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub enum ErrMode<E> { @@ -120,6 +121,7 @@ pub enum ErrMode<E> { impl<E> ErrMode<E> { /// Tests if the result is Incomplete + #[inline] pub fn is_incomplete(&self) -> bool { matches!(self, ErrMode::Incomplete(_)) } @@ -164,6 +166,7 @@ impl<E> ErrMode<E> { /// /// Returns `None` for [`ErrMode::Incomplete`] #[cfg_attr(debug_assertions, track_caller)] + #[inline(always)] pub fn into_inner(self) -> Option<E> { match self { ErrMode::Backtrack(e) | ErrMode::Cut(e) => Some(e), @@ -173,11 +176,13 @@ impl<E> ErrMode<E> { } impl<I, E: ParserError<I>> ParserError<I> for ErrMode<E> { + #[inline(always)] fn from_error_kind(input: &I, kind: ErrorKind) -> Self { ErrMode::Backtrack(E::from_error_kind(input, kind)) } #[cfg_attr(debug_assertions, track_caller)] + #[inline(always)] fn assert(input: &I, message: &'static str) -> Self where I: crate::lib::std::fmt::Debug, @@ -185,6 +190,7 @@ impl<I, E: ParserError<I>> ParserError<I> for ErrMode<E> { ErrMode::Backtrack(E::assert(input, message)) } + #[inline] fn append(self, input: &I, kind: ErrorKind) -> Self { match self { ErrMode::Backtrack(e) => ErrMode::Backtrack(e.append(input, kind)), @@ -205,11 +211,19 @@ impl<I, EXT, E> FromExternalError<I, EXT> for ErrMode<E> where E: FromExternalError<I, EXT>, { + #[inline(always)] fn from_external_error(input: &I, kind: ErrorKind, e: EXT) -> Self { ErrMode::Backtrack(E::from_external_error(input, kind, e)) } } +impl<I, C, E: AddContext<I, C>> AddContext<I, C> for ErrMode<E> { + #[inline(always)] + fn add_context(self, input: &I, ctx: C) -> Self { + self.map(|err| err.add_context(input, ctx)) + } +} + impl<T: Clone> ErrMode<InputError<T>> { /// Maps `ErrMode<InputError<T>>` to `ErrMode<InputError<U>>` with the given `F: T -> U` pub fn map_input<U: Clone, F>(self, f: F) -> ErrMode<InputError<U>> @@ -276,6 +290,7 @@ pub trait ParserError<I>: Sized { /// /// For example, this would be used by [`alt`][crate::combinator::alt] to report the error from /// each case. + #[inline] fn or(self, other: Self) -> Self { other } @@ -330,19 +345,25 @@ impl<I: Clone> InputError<I> { pub fn new(input: I, kind: ErrorKind) -> Self { Self { input, kind } } + + /// Translate the input type + #[inline] + pub fn map_input<I2: Clone, O: Fn(I) -> I2>(self, op: O) -> InputError<I2> { + InputError { + input: op(self.input), + kind: self.kind, + } + } } #[cfg(feature = "alloc")] -impl<'i, I: Clone + ToOwned + ?Sized> InputError<&'i I> +impl<'i, I: ToOwned> InputError<&'i I> where <I as ToOwned>::Owned: Clone, { /// Obtaining ownership pub fn into_owned(self) -> InputError<<I as ToOwned>::Owned> { - InputError { - input: self.input.to_owned(), - kind: self.kind, - } + self.map_input(ToOwned::to_owned) } } @@ -397,7 +418,12 @@ impl<I: Clone> ErrorConvert<InputError<I>> for InputError<(I, usize)> { /// The Display implementation allows the `std::error::Error` implementation impl<I: Clone + fmt::Display> fmt::Display for InputError<I> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} error starting at: {}", self.kind, self.input) + write!( + f, + "{} error starting at: {}", + self.kind.description(), + self.input + ) } } @@ -464,6 +490,16 @@ impl<C> ContextError<C> { } } +impl<C: Clone> Clone for ContextError<C> { + fn clone(&self) -> Self { + Self { + context: self.context.clone(), + #[cfg(feature = "std")] + cause: self.cause.as_ref().map(|e| e.to_string().into()), + } + } +} + impl<C> Default for ContextError<C> { #[inline] fn default() -> Self { @@ -606,6 +642,15 @@ pub enum StrContext { Expected(StrContextValue), } +impl crate::lib::std::fmt::Display for StrContext { + fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { + match self { + Self::Label(name) => write!(f, "invalid {name}"), + Self::Expected(value) => write!(f, "expected {value}"), + } + } +} + /// See [`StrContext`] #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] @@ -619,12 +664,14 @@ pub enum StrContextValue { } impl From<char> for StrContextValue { + #[inline] fn from(inner: char) -> Self { Self::CharLiteral(inner) } } impl From<&'static str> for StrContextValue { + #[inline] fn from(inner: &'static str) -> Self { Self::StringLiteral(inner) } @@ -645,6 +692,395 @@ impl crate::lib::std::fmt::Display for StrContextValue { } } +/// Trace all error paths, particularly for tests +#[derive(Debug)] +#[cfg(feature = "std")] +pub enum TreeError<I, C = StrContext> { + /// Initial error that kicked things off + Base(TreeErrorBase<I>), + /// Traces added to the error while walking back up the stack + Stack { + /// Initial error that kicked things off + base: Box<Self>, + /// Traces added to the error while walking back up the stack + stack: Vec<TreeErrorFrame<I, C>>, + }, + /// All failed branches of an `alt` + Alt(Vec<Self>), +} + +/// See [`TreeError::Stack`] +#[derive(Debug)] +#[cfg(feature = "std")] +pub enum TreeErrorFrame<I, C = StrContext> { + /// See [`ParserError::append`] + Kind(TreeErrorBase<I>), + /// See [`AddContext::add_context`] + Context(TreeErrorContext<I, C>), +} + +/// See [`TreeErrorFrame::Kind`], [`ParserError::append`] +#[derive(Debug)] +#[cfg(feature = "std")] +pub struct TreeErrorBase<I> { + /// Parsed input, at the location where the error occurred + pub input: I, + /// Debug context + pub kind: ErrorKind, + /// See [`FromExternalError::from_external_error`] + pub cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>, +} + +/// See [`TreeErrorFrame::Context`], [`AddContext::add_context`] +#[derive(Debug)] +#[cfg(feature = "std")] +pub struct TreeErrorContext<I, C = StrContext> { + /// Parsed input, at the location where the error occurred + pub input: I, + /// See [`AddContext::add_context`] + pub context: C, +} + +#[cfg(feature = "std")] +impl<'i, I: ToOwned, C> TreeError<&'i I, C> +where + <I as ToOwned>::Owned: Clone, +{ + /// Obtaining ownership + pub fn into_owned(self) -> TreeError<<I as ToOwned>::Owned, C> { + self.map_input(ToOwned::to_owned) + } +} + +#[cfg(feature = "std")] +impl<I, C> TreeError<I, C> +where + I: Clone, +{ + /// Translate the input type + pub fn map_input<I2: Clone, O: Clone + Fn(I) -> I2>(self, op: O) -> TreeError<I2, C> { + match self { + TreeError::Base(base) => TreeError::Base(TreeErrorBase { + input: op(base.input), + kind: base.kind, + cause: base.cause, + }), + TreeError::Stack { base, stack } => { + let base = Box::new(base.map_input(op.clone())); + let stack = stack + .into_iter() + .map(|frame| match frame { + TreeErrorFrame::Kind(kind) => TreeErrorFrame::Kind(TreeErrorBase { + input: op(kind.input), + kind: kind.kind, + cause: kind.cause, + }), + TreeErrorFrame::Context(context) => { + TreeErrorFrame::Context(TreeErrorContext { + input: op(context.input), + context: context.context, + }) + } + }) + .collect(); + TreeError::Stack { base, stack } + } + TreeError::Alt(alt) => { + TreeError::Alt(alt.into_iter().map(|e| e.map_input(op.clone())).collect()) + } + } + } + + fn append_frame(self, frame: TreeErrorFrame<I, C>) -> Self { + match self { + TreeError::Stack { base, mut stack } => { + stack.push(frame); + TreeError::Stack { base, stack } + } + base => TreeError::Stack { + base: Box::new(base), + stack: vec![frame], + }, + } + } +} + +#[cfg(feature = "std")] +impl<I, C> ParserError<I> for TreeError<I, C> +where + I: Clone, +{ + fn from_error_kind(input: &I, kind: ErrorKind) -> Self { + TreeError::Base(TreeErrorBase { + input: input.clone(), + kind, + cause: None, + }) + } + + fn append(self, input: &I, kind: ErrorKind) -> Self { + let frame = TreeErrorFrame::Kind(TreeErrorBase { + input: input.clone(), + kind, + cause: None, + }); + self.append_frame(frame) + } + + fn or(self, other: Self) -> Self { + match (self, other) { + (TreeError::Alt(mut first), TreeError::Alt(second)) => { + // Just in case an implementation does a divide-and-conquer algorithm + // + // To prevent mixing `alt`s at different levels, parsers should + // `alt_err.append(input, ErrorKind::Alt)`. + first.extend(second); + TreeError::Alt(first) + } + (TreeError::Alt(mut alt), new) | (new, TreeError::Alt(mut alt)) => { + alt.push(new); + TreeError::Alt(alt) + } + (first, second) => TreeError::Alt(vec![first, second]), + } + } +} + +#[cfg(feature = "std")] +impl<I, C> AddContext<I, C> for TreeError<I, C> +where + I: Clone, +{ + fn add_context(self, input: &I, context: C) -> Self { + let frame = TreeErrorFrame::Context(TreeErrorContext { + input: input.clone(), + context, + }); + self.append_frame(frame) + } +} + +#[cfg(feature = "std")] +impl<I, C, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E> for TreeError<I, C> +where + I: Clone, +{ + fn from_external_error(input: &I, kind: ErrorKind, e: E) -> Self { + TreeError::Base(TreeErrorBase { + input: input.clone(), + kind, + cause: Some(Box::new(e)), + }) + } +} + +#[cfg(feature = "std")] +impl<I, C> TreeError<I, C> +where + I: Clone + std::fmt::Display, + C: fmt::Display, +{ + fn write(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result { + let child_indent = indent + 2; + match self { + TreeError::Base(base) => { + writeln!(f, "{:indent$}{base}", "")?; + } + TreeError::Stack { base, stack } => { + base.write(f, indent)?; + for (level, frame) in stack.iter().enumerate() { + match frame { + TreeErrorFrame::Kind(frame) => { + writeln!(f, "{:child_indent$}{level}: {frame}", "")?; + } + TreeErrorFrame::Context(frame) => { + writeln!(f, "{:child_indent$}{level}: {frame}", "")?; + } + } + } + } + TreeError::Alt(alt) => { + writeln!(f, "{:indent$}during one of:", "")?; + for child in alt { + child.write(f, child_indent)?; + } + } + } + + Ok(()) + } +} + +#[cfg(feature = "std")] +impl<I: Clone + fmt::Display> fmt::Display for TreeErrorBase<I> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(cause) = self.cause.as_ref() { + write!(f, "caused by {cause}")?; + } else { + let kind = self.kind.description(); + write!(f, "in {kind}")?; + } + let input = abbreviate(self.input.to_string()); + write!(f, " at '{input}'")?; + Ok(()) + } +} + +#[cfg(feature = "std")] +impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for TreeErrorContext<I, C> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let context = &self.context; + let input = abbreviate(self.input.to_string()); + write!(f, "{context} at '{input}'")?; + Ok(()) + } +} + +#[cfg(feature = "std")] +impl< + I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static, + C: fmt::Display + fmt::Debug, + > std::error::Error for TreeError<I, C> +{ +} + +#[cfg(feature = "std")] +fn abbreviate(input: String) -> String { + let mut abbrev = None; + + if let Some((line, _)) = input.split_once('\n') { + abbrev = Some(line); + } + + let max_len = 20; + let current = abbrev.unwrap_or(&input); + if max_len < current.len() { + if let Some((index, _)) = current.char_indices().nth(max_len) { + abbrev = Some(¤t[..index]); + } + } + + if let Some(abbrev) = abbrev { + format!("{abbrev}...") + } else { + input + } +} + +#[cfg(feature = "std")] +impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for TreeError<I, C> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.write(f, 0) + } +} + +/// Deprecated, replaced with [`ContextError`] +#[cfg(feature = "std")] +#[allow(deprecated)] +#[deprecated(since = "0.5.8", note = "Replaced with `ContextError`")] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct VerboseError<I: Clone, C = &'static str> { + /// Accumulated error information + pub errors: crate::lib::std::vec::Vec<(I, VerboseErrorKind<C>)>, +} + +#[cfg(feature = "std")] +#[allow(deprecated)] +impl<'i, I: ToOwned, C> VerboseError<&'i I, C> +where + <I as ToOwned>::Owned: Clone, +{ + /// Obtaining ownership + pub fn into_owned(self) -> VerboseError<<I as ToOwned>::Owned, C> { + self.map_input(ToOwned::to_owned) + } +} + +#[cfg(feature = "std")] +#[allow(deprecated)] +impl<I: Clone, C> VerboseError<I, C> { + /// Translate the input type + pub fn map_input<I2: Clone, O>(self, op: O) -> VerboseError<I2, C> + where + O: Fn(I) -> I2, + { + VerboseError { + errors: self.errors.into_iter().map(|(i, k)| (op(i), k)).collect(), + } + } +} + +/// Deprecated, replaced with [`ContextError`] +#[cfg(feature = "std")] +#[deprecated(since = "0.5.8", note = "Replaced with `ContextError`")] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum VerboseErrorKind<C = &'static str> { + /// Static string added by the `context` function + Context(C), + /// Error kind given by various parsers + Winnow(ErrorKind), +} + +#[cfg(feature = "std")] +#[allow(deprecated)] +impl<I: Clone, C> ParserError<I> for VerboseError<I, C> { + fn from_error_kind(input: &I, kind: ErrorKind) -> Self { + VerboseError { + errors: vec![(input.clone(), VerboseErrorKind::Winnow(kind))], + } + } + + fn append(mut self, input: &I, kind: ErrorKind) -> Self { + self.errors + .push((input.clone(), VerboseErrorKind::Winnow(kind))); + self + } +} + +#[cfg(feature = "std")] +#[allow(deprecated)] +impl<I: Clone, C> AddContext<I, C> for VerboseError<I, C> { + fn add_context(mut self, input: &I, ctx: C) -> Self { + self.errors + .push((input.clone(), VerboseErrorKind::Context(ctx))); + self + } +} + +#[cfg(feature = "std")] +#[allow(deprecated)] +impl<I: Clone, C, E> FromExternalError<I, E> for VerboseError<I, C> { + /// Create a new error from an input position and an external error + fn from_external_error(input: &I, kind: ErrorKind, _e: E) -> Self { + Self::from_error_kind(input, kind) + } +} + +#[cfg(feature = "std")] +#[allow(deprecated)] +impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for VerboseError<I, C> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Parse error:")?; + for (input, error) in &self.errors { + match error { + VerboseErrorKind::Winnow(e) => writeln!(f, "{} at: {}", e.description(), input)?, + VerboseErrorKind::Context(s) => writeln!(f, "in section '{}', at: {}", s, input)?, + } + } + + Ok(()) + } +} + +#[cfg(feature = "std")] +#[allow(deprecated)] +impl< + I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static, + C: fmt::Display + fmt::Debug, + > std::error::Error for VerboseError<I, C> +{ +} + /// Provide some minor debug context for errors #[rustfmt::skip] #[derive(Debug,PartialEq,Eq,Hash,Clone,Copy)] @@ -684,10 +1120,12 @@ impl ErrorKind { } impl<I> ParserError<I> for ErrorKind { + #[inline] fn from_error_kind(_input: &I, kind: ErrorKind) -> Self { kind } + #[inline] fn append(self, _: &I, _: ErrorKind) -> Self { self } @@ -697,6 +1135,7 @@ impl<I, C> AddContext<I, C> for ErrorKind {} impl<I, E> FromExternalError<I, E> for ErrorKind { /// Create a new error from an input position and an external error + #[inline] fn from_external_error(_input: &I, kind: ErrorKind, _e: E) -> Self { kind } @@ -750,6 +1189,12 @@ impl<I, E> ParseError<I, E> { pub fn inner(&self) -> &E { &self.inner } + + /// The original [`ParserError`] + #[inline] + pub fn into_inner(self) -> E { + self.inner + } } impl<I, E> core::fmt::Display for ParseError<I, E> |