#![allow(deprecated)]
#[cfg(feature = "alloc")]
use crate::lib::std::borrow::ToOwned;
use crate::lib::std::fmt;
use core::num::NonZeroUsize;
use crate::stream::Stream;
use crate::stream::StreamIsPartial;
use crate::Parser;
pub type IResult<I, O, E = Error<I>> = Result<(I, O), ErrMode<E>>;
pub trait FinishIResult<I, O, E> {
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
#[cfg_attr(feature = "std", doc = "```")]
fn finish(self) -> Result<O, E>;
fn finish_err(self) -> Result<(I, O), E>;
}
impl<I, O, E> FinishIResult<I, O, E> for IResult<I, O, E>
where
I: Stream,
I: StreamIsPartial,
I: Clone,
E: ParseError<I>,
{
fn finish(self) -> Result<O, E> {
debug_assert!(
!I::is_partial_supported(),
"partial streams need to handle `ErrMode::Incomplete`"
);
let (i, o) = self.finish_err()?;
crate::combinator::eof(i).finish_err()?;
Ok(o)
}
fn finish_err(self) -> Result<(I, O), E> {
debug_assert!(
!I::is_partial_supported(),
"partial streams need to handle `ErrMode::Incomplete`"
);
match self {
Ok(res) => Ok(res),
Err(ErrMode::Backtrack(e)) | Err(ErrMode::Cut(e)) => Err(e),
Err(ErrMode::Incomplete(_)) => {
panic!("complete parsers should not report `Err(ErrMode::Incomplete(_))`")
}
}
}
}
#[doc(hidden)]
#[deprecated(
since = "0.1.0",
note = "Replaced with `FinishIResult` which is available via `winnow::prelude`"
)]
#[cfg_attr(feature = "unstable-doc", doc(hidden))]
pub trait Finish<I, O, E> {
#[deprecated(
since = "0.1.0",
note = "Replaced with `FinishIResult::finish_err` which is available via `winnow::prelude`"
)]
#[cfg_attr(feature = "unstable-doc", doc(hidden))]
fn finish(self) -> Result<(I, O), E>;
}
#[allow(deprecated)]
impl<I, O, E> Finish<I, O, E> for IResult<I, O, E> {
fn finish(self) -> Result<(I, O), E> {
match self {
Ok(res) => Ok(res),
Err(ErrMode::Backtrack(e)) | Err(ErrMode::Cut(e)) => Err(e),
Err(ErrMode::Incomplete(_)) => {
panic!("Cannot call `finish()` on `Err(ErrMode::Incomplete(_))`: this result means that the parser does not have enough data to decide, you should gather more data and try to reapply the parser instead")
}
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
pub enum Needed {
Unknown,
Size(NonZeroUsize),
}
impl Needed {
pub fn new(s: usize) -> Self {
match NonZeroUsize::new(s) {
Some(sz) => Needed::Size(sz),
None => Needed::Unknown,
}
}
pub fn is_known(&self) -> bool {
*self != Needed::Unknown
}
#[inline]
pub fn map<F: Fn(NonZeroUsize) -> usize>(self, f: F) -> Needed {
match self {
Needed::Unknown => Needed::Unknown,
Needed::Size(n) => Needed::new(f(n)),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
pub enum ErrMode<E> {
Incomplete(Needed),
Backtrack(E),
Cut(E),
}
impl<E> ErrMode<E> {
#[inline]
pub fn is_incomplete(&self) -> bool {
matches!(self, ErrMode::Incomplete(_))
}
pub fn cut(self) -> Self {
match self {
ErrMode::Backtrack(e) => ErrMode::Cut(e),
rest => rest,
}
}
pub fn backtrack(self) -> Self {
match self {
ErrMode::Cut(e) => ErrMode::Backtrack(e),
rest => rest,
}
}
pub fn map<E2, F>(self, f: F) -> ErrMode<E2>
where
F: FnOnce(E) -> E2,
{
match self {
ErrMode::Incomplete(n) => ErrMode::Incomplete(n),
ErrMode::Cut(t) => ErrMode::Cut(f(t)),
ErrMode::Backtrack(t) => ErrMode::Backtrack(f(t)),
}
}
pub fn convert<F>(self) -> ErrMode<F>
where
E: ErrorConvert<F>,
{
self.map(ErrorConvert::convert)
}
}
impl<I, E: ParseError<I>> ParseError<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,
{
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)),
e => e,
}
}
fn or(self, other: Self) -> Self {
match (self, other) {
(ErrMode::Backtrack(e), ErrMode::Backtrack(o)) => ErrMode::Backtrack(e.or(o)),
(ErrMode::Incomplete(e), _) | (_, ErrMode::Incomplete(e)) => ErrMode::Incomplete(e),
(ErrMode::Cut(e), _) | (_, ErrMode::Cut(e)) => ErrMode::Cut(e),
}
}
}
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<T> ErrMode<Error<T>> {
pub fn map_input<U, F>(self, f: F) -> ErrMode<Error<U>>
where
F: FnOnce(T) -> U,
{
match self {
ErrMode::Incomplete(n) => ErrMode::Incomplete(n),
ErrMode::Cut(Error { input, kind }) => ErrMode::Cut(Error {
input: f(input),
kind,
}),
ErrMode::Backtrack(Error { input, kind }) => ErrMode::Backtrack(Error {
input: f(input),
kind,
}),
}
}
}
#[cfg(feature = "alloc")]
impl ErrMode<Error<&[u8]>> {
#[deprecated(since = "0.3.0", note = "Replaced with `Error::into_owned`")]
#[cfg_attr(feature = "unstable-doc", doc(hidden))]
pub fn to_owned(self) -> ErrMode<Error<crate::lib::std::vec::Vec<u8>>> {
self.map_input(ToOwned::to_owned)
}
}
#[cfg(feature = "alloc")]
impl ErrMode<Error<&str>> {
#[deprecated(since = "0.3.0", note = "Replaced with `Error::into_owned`")]
#[cfg_attr(feature = "unstable-doc", doc(hidden))]
pub fn to_owned(self) -> ErrMode<Error<crate::lib::std::string::String>> {
self.map_input(ToOwned::to_owned)
}
}
impl<E: Eq> Eq for ErrMode<E> {}
impl<E> fmt::Display for ErrMode<E>
where
E: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrMode::Incomplete(Needed::Size(u)) => write!(f, "Parsing requires {} bytes/chars", u),
ErrMode::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data"),
ErrMode::Cut(c) => write!(f, "Parsing Failure: {:?}", c),
ErrMode::Backtrack(c) => write!(f, "Parsing Error: {:?}", c),
}
}
}
pub trait ParseError<I>: Sized {
fn from_error_kind(input: I, kind: ErrorKind) -> Self;
#[cfg_attr(debug_assertions, track_caller)]
fn assert(input: I, _message: &'static str) -> Self
where
I: crate::lib::std::fmt::Debug,
{
#[cfg(debug_assertions)]
panic!("assert `{}` failed at {:#?}", _message, input);
#[cfg(not(debug_assertions))]
Self::from_error_kind(input, ErrorKind::Assert)
}
fn append(self, input: I, kind: ErrorKind) -> Self;
#[deprecated(since = "0.2.0", note = "Replaced with `ContextError`")]
#[cfg_attr(feature = "unstable-doc", doc(hidden))]
fn from_char(input: I, _: char) -> Self {
Self::from_error_kind(input, ErrorKind::Char)
}
#[inline]
fn or(self, other: Self) -> Self {
other
}
}
pub trait ContextError<I, C = &'static str>: Sized {
#[inline]
fn add_context(self, _input: I, _ctx: C) -> Self {
self
}
}
pub trait FromExternalError<I, E> {
fn from_external_error(input: I, kind: ErrorKind, e: E) -> Self;
}
pub trait ErrorConvert<E> {
fn convert(self) -> E;
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Error<I> {
pub input: I,
pub kind: ErrorKind,
}
impl<I> Error<I> {
#[inline]
pub fn new(input: I, kind: ErrorKind) -> Error<I> {
Error { input, kind }
}
}
#[cfg(feature = "alloc")]
impl<'i, I: ToOwned + ?Sized> Error<&'i I> {
pub fn into_owned(self) -> Error<<I as ToOwned>::Owned> {
Error {
input: self.input.to_owned(),
kind: self.kind,
}
}
}
impl<I> ParseError<I> for Error<I> {
#[inline]
fn from_error_kind(input: I, kind: ErrorKind) -> Self {
Error { input, kind }
}
#[inline]
fn append(self, _: I, _: ErrorKind) -> Self {
self
}
}
impl<I, C> ContextError<I, C> for Error<I> {}
impl<I, E> FromExternalError<I, E> for Error<I> {
#[inline]
fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self {
Error { input, kind }
}
}
impl<I> ErrorConvert<Error<(I, usize)>> for Error<I> {
#[inline]
fn convert(self) -> Error<(I, usize)> {
Error {
input: (self.input, 0),
kind: self.kind,
}
}
}
impl<I> ErrorConvert<Error<I>> for Error<(I, usize)> {
#[inline]
fn convert(self) -> Error<I> {
Error {
input: self.input.0,
kind: self.kind,
}
}
}
impl<I: fmt::Display> fmt::Display for Error<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "error {:?} at: {}", self.kind, self.input)
}
}
#[cfg(feature = "std")]
impl<I: fmt::Debug + fmt::Display + Sync + Send + 'static> std::error::Error for Error<I> {}
impl<I> ParseError<I> for () {
#[inline]
fn from_error_kind(_: I, _: ErrorKind) -> Self {}
#[inline]
fn append(self, _: I, _: ErrorKind) -> Self {}
}
impl<I, C> ContextError<I, C> for () {}
impl<I, E> FromExternalError<I, E> for () {
#[inline]
fn from_external_error(_input: I, _kind: ErrorKind, _e: E) -> Self {}
}
impl ErrorConvert<()> for () {
#[inline]
fn convert(self) {}
}
#[deprecated(since = "0.2.0", note = "Replaced with `ParseError::from_error_kind`")]
#[cfg_attr(feature = "unstable-doc", doc(hidden))]
pub fn make_error<I, E: ParseError<I>>(input: I, kind: ErrorKind) -> E {
E::from_error_kind(input, kind)
}
#[deprecated(since = "0.2.0", note = "Replaced with `ParseError::append`")]
#[cfg_attr(feature = "unstable-doc", doc(hidden))]
pub fn append_error<I, E: ParseError<I>>(input: I, kind: ErrorKind, other: E) -> E {
other.append(input, kind)
}
#[cfg(feature = "alloc")]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VerboseError<I> {
pub errors: crate::lib::std::vec::Vec<(I, VerboseErrorKind)>,
}
#[cfg(feature = "alloc")]
impl<'i, I: ToOwned + ?Sized> VerboseError<&'i I> {
pub fn into_owned(self) -> VerboseError<<I as ToOwned>::Owned> {
#[allow(clippy::redundant_clone)] VerboseError {
errors: self
.errors
.into_iter()
.map(|(i, k)| (i.to_owned(), k))
.collect(),
}
}
}
#[cfg(feature = "alloc")]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum VerboseErrorKind {
Context(&'static str),
Winnow(ErrorKind),
}
#[cfg(feature = "alloc")]
impl<I> ParseError<I> for VerboseError<I> {
fn from_error_kind(input: I, kind: ErrorKind) -> Self {
VerboseError {
errors: vec![(input, VerboseErrorKind::Winnow(kind))],
}
}
fn append(mut self, input: I, kind: ErrorKind) -> Self {
self.errors.push((input, VerboseErrorKind::Winnow(kind)));
self
}
}
#[cfg(feature = "alloc")]
impl<I> ContextError<I, &'static str> for VerboseError<I> {
fn add_context(mut self, input: I, ctx: &'static str) -> Self {
self.errors.push((input, VerboseErrorKind::Context(ctx)));
self
}
}
#[cfg(feature = "alloc")]
impl<I, E> FromExternalError<I, E> for VerboseError<I> {
fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self {
Self::from_error_kind(input, kind)
}
}
#[cfg(feature = "alloc")]
impl<I> ErrorConvert<VerboseError<I>> for VerboseError<(I, usize)> {
fn convert(self) -> VerboseError<I> {
VerboseError {
errors: self.errors.into_iter().map(|(i, e)| (i.0, e)).collect(),
}
}
}
#[cfg(feature = "alloc")]
impl<I> ErrorConvert<VerboseError<(I, usize)>> for VerboseError<I> {
fn convert(self) -> VerboseError<(I, usize)> {
VerboseError {
errors: self.errors.into_iter().map(|(i, e)| ((i, 0), e)).collect(),
}
}
}
#[cfg(feature = "alloc")]
impl<I: fmt::Display> fmt::Display for VerboseError<I> {
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, input)?,
VerboseErrorKind::Context(s) => writeln!(f, "in section '{}', at: {}", s, input)?,
}
}
Ok(())
}
}
#[cfg(feature = "std")]
impl<I: fmt::Debug + fmt::Display + Sync + Send + 'static> std::error::Error for VerboseError<I> {}
#[deprecated(since = "0.1.0", note = "Replaced with `Parser::context")]
#[cfg_attr(feature = "unstable-doc", doc(hidden))]
pub fn context<I: Clone, E: ContextError<I, &'static str>, F, O>(
context: &'static str,
mut f: F,
) -> impl FnMut(I) -> IResult<I, O, E>
where
F: Parser<I, O, E>,
{
move |i: I| match f.parse_next(i.clone()) {
Ok(o) => Ok(o),
Err(ErrMode::Incomplete(i)) => Err(ErrMode::Incomplete(i)),
Err(ErrMode::Backtrack(e)) => Err(ErrMode::Backtrack(e.add_context(i, context))),
Err(ErrMode::Cut(e)) => Err(ErrMode::Cut(e.add_context(i, context))),
}
}
#[cfg(feature = "alloc")]
pub fn convert_error<I: core::ops::Deref<Target = str>>(
input: I,
e: VerboseError<I>,
) -> crate::lib::std::string::String {
use crate::lib::std::fmt::Write;
use crate::stream::Offset;
let mut result = crate::lib::std::string::String::new();
for (i, (substring, kind)) in e.errors.iter().enumerate() {
let offset = input.offset_to(substring);
if input.is_empty() {
match kind {
VerboseErrorKind::Context(s) => {
write!(&mut result, "{}: in {}, got empty input\n\n", i, s)
}
VerboseErrorKind::Winnow(e) => {
write!(&mut result, "{}: in {:?}, got empty input\n\n", i, e)
}
}
} else {
let prefix = &input.as_bytes()[..offset];
let line_number = prefix.iter().filter(|&&b| b == b'\n').count() + 1;
let line_begin = prefix
.iter()
.rev()
.position(|&b| b == b'\n')
.map(|pos| offset - pos)
.unwrap_or(0);
let line = input[line_begin..]
.lines()
.next()
.unwrap_or(&input[line_begin..])
.trim_end();
let column_number = line.offset_to(substring) + 1;
match kind {
VerboseErrorKind::Context(s) => write!(
&mut result,
"{i}: at line {line_number}, in {context}:\n\
{line}\n\
{caret:>column$}\n\n",
i = i,
line_number = line_number,
context = s,
line = line,
caret = '^',
column = column_number,
),
VerboseErrorKind::Winnow(e) => write!(
&mut result,
"{i}: at line {line_number}, in {kind:?}:\n\
{line}\n\
{caret:>column$}\n\n",
i = i,
line_number = line_number,
kind = e,
line = line,
caret = '^',
column = column_number,
),
}
}
.unwrap();
}
result
}
#[rustfmt::skip]
#[derive(Debug,PartialEq,Eq,Hash,Clone,Copy)]
#[allow(deprecated,missing_docs)]
pub enum ErrorKind {
Assert,
Tag,
MapRes,
Alt,
IsNot,
IsA,
SeparatedList,
SeparatedNonEmptyList,
Many0,
Many1,
ManyTill,
Count,
TakeUntil,
LengthValue,
TagClosure,
Alpha,
Digit,
HexDigit,
OctDigit,
AlphaNumeric,
Space,
MultiSpace,
LengthValueFn,
Eof,
Switch,
TagBits,
OneOf,
NoneOf,
Char,
CrLf,
RegexpMatch,
RegexpMatches,
RegexpFind,
RegexpCapture,
RegexpCaptures,
TakeWhile1,
Complete,
Fix,
Escaped,
EscapedTransform,
NonEmpty,
ManyMN,
Not,
Permutation,
Verify,
TakeTill1,
TakeWhileMN,
TooLarge,
Many0Count,
Many1Count,
Float,
Satisfy,
Fail,
}
impl ErrorKind {
#[rustfmt::skip]
#[allow(deprecated)]
pub fn description(&self) -> &str {
match *self {
ErrorKind::Assert => "Assert",
ErrorKind::Tag => "Tag",
ErrorKind::MapRes => "Map on Result",
ErrorKind::Alt => "Alternative",
ErrorKind::IsNot => "IsNot",
ErrorKind::IsA => "IsA",
ErrorKind::SeparatedList => "Separated list",
ErrorKind::SeparatedNonEmptyList => "Separated non empty list",
ErrorKind::Many0 => "Many0",
ErrorKind::Many1 => "Many1",
ErrorKind::Count => "Count",
ErrorKind::TakeUntil => "Take until",
ErrorKind::LengthValue => "Length followed by value",
ErrorKind::TagClosure => "Tag closure",
ErrorKind::Alpha => "Alphabetic",
ErrorKind::Digit => "Digit",
ErrorKind::AlphaNumeric => "AlphaNumeric",
ErrorKind::Space => "Space",
ErrorKind::MultiSpace => "Multiple spaces",
ErrorKind::LengthValueFn => "LengthValueFn",
ErrorKind::Eof => "End of file",
ErrorKind::Switch => "Switch",
ErrorKind::TagBits => "Tag on bitstream",
ErrorKind::OneOf => "OneOf",
ErrorKind::NoneOf => "NoneOf",
ErrorKind::Char => "Char",
ErrorKind::CrLf => "CrLf",
ErrorKind::RegexpMatch => "RegexpMatch",
ErrorKind::RegexpMatches => "RegexpMatches",
ErrorKind::RegexpFind => "RegexpFind",
ErrorKind::RegexpCapture => "RegexpCapture",
ErrorKind::RegexpCaptures => "RegexpCaptures",
ErrorKind::TakeWhile1 => "TakeWhile1",
ErrorKind::Complete => "Complete",
ErrorKind::Fix => "Fix",
ErrorKind::Escaped => "Escaped",
ErrorKind::EscapedTransform => "EscapedTransform",
ErrorKind::NonEmpty => "NonEmpty",
ErrorKind::ManyMN => "Many(m, n)",
ErrorKind::HexDigit => "Hexadecimal Digit",
ErrorKind::OctDigit => "Octal digit",
ErrorKind::Not => "Negation",
ErrorKind::Permutation => "Permutation",
ErrorKind::ManyTill => "ManyTill",
ErrorKind::Verify => "predicate verification",
ErrorKind::TakeTill1 => "TakeTill1",
ErrorKind::TakeWhileMN => "TakeWhileMN",
ErrorKind::TooLarge => "Needed data size is too large",
ErrorKind::Many0Count => "Count occurrence of >=0 patterns",
ErrorKind::Many1Count => "Count occurrence of >=1 patterns",
ErrorKind::Float => "Float",
ErrorKind::Satisfy => "Satisfy",
ErrorKind::Fail => "Fail",
}
}
}
#[allow(unused_variables)]
#[macro_export(local_inner_macros)]
#[cfg_attr(
not(test),
deprecated(since = "0.3.0", note = "Replaced with `E::from_error_kind`")
)]
#[cfg_attr(feature = "unstable-doc", doc(hidden))]
macro_rules! error_position(
($input:expr, $code:expr) => ({
$crate::error::ParseError::from_error_kind($input, $code)
});
);
#[allow(unused_variables)]
#[macro_export(local_inner_macros)]
#[cfg_attr(
not(test),
deprecated(since = "0.3.0", note = "Replaced with `E::append`")
)]
#[cfg_attr(feature = "unstable-doc", doc(hidden))]
macro_rules! error_node_position(
($input:expr, $code:expr, $next:expr) => ({
$crate::error::ParseError::append($next, $input, $code)
});
);
#[deprecated(
since = "0.1.0",
note = "Replaced with `trace` and the `debug` feature flag"
)]
#[cfg_attr(feature = "unstable-doc", doc(hidden))]
#[cfg(feature = "std")]
pub fn dbg_dmp<'a, F, O, E: std::fmt::Debug>(
mut f: F,
context: &'static str,
) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], O, E>
where
F: Parser<&'a [u8], O, E>,
{
use crate::stream::HexDisplay;
move |i: &'a [u8]| match f.parse_next(i) {
Err(e) => {
println!("{}: Error({:?}) at:\n{}", context, e, i.to_hex(8));
Err(e)
}
a => a,
}
}
#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
use super::*;
use crate::bytes::one_of;
#[test]
fn convert_error_panic() {
let input = "";
let _result: IResult<_, _, VerboseError<&str>> = one_of('x')(input);
}
}