From a12a667871b6213185652fce8519b1bb1adb1c2a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 16 Jul 2024 11:53:06 -0500 Subject: [PATCH] refactor(parser): Migrate to Winnow 0.3 --- rinja_parser/Cargo.toml | 2 +- rinja_parser/src/expr.rs | 42 +++++++++++----------- rinja_parser/src/lib.rs | 72 +++++++++++++++++++------------------- rinja_parser/src/node.rs | 56 +++++++++++++++-------------- rinja_parser/src/target.rs | 36 ++++++++++--------- 5 files changed, 108 insertions(+), 100 deletions(-) diff --git a/rinja_parser/Cargo.toml b/rinja_parser/Cargo.toml index 4e099592..f699b1d4 100644 --- a/rinja_parser/Cargo.toml +++ b/rinja_parser/Cargo.toml @@ -22,8 +22,8 @@ config = ["dep:serde"] [dependencies] memchr = "2" -nom = { version = "7", default-features = false, features = ["alloc"] } serde = { version = "1.0", optional = true, features = ["derive"] } +winnow = "0.3" [dev-dependencies] criterion = "0.5" diff --git a/rinja_parser/src/expr.rs b/rinja_parser/src/expr.rs index aa287fa7..e163bdd1 100644 --- a/rinja_parser/src/expr.rs +++ b/rinja_parser/src/expr.rs @@ -1,14 +1,14 @@ use std::collections::HashSet; use std::str; -use nom::branch::alt; -use nom::bytes::complete::{tag, take_till}; -use nom::character::complete::{char, digit1}; -use nom::combinator::{consumed, cut, fail, map, not, opt, peek, recognize, value}; -use nom::error::ErrorKind; -use nom::error_position; -use nom::multi::{fold_many0, many0, separated_list0, separated_list1}; -use nom::sequence::{pair, preceded, terminated, tuple}; +use winnow::branch::alt; +use winnow::bytes::complete::{tag, take_till}; +use winnow::character::complete::{char, digit1}; +use winnow::combinator::{consumed, cut, fail, map, not, opt, peek, recognize, value}; +use winnow::error::ErrorKind; +use winnow::multi::{fold_many0, many0, separated_list0, separated_list1}; +use winnow::sequence::{pair, preceded, terminated, tuple}; +use winnow::{Parser, error_position}; use crate::{ CharLit, ErrorContext, Level, Num, ParseResult, PathOrIdentifier, StrLit, WithSpan, char_lit, @@ -21,7 +21,9 @@ macro_rules! expr_prec_layer { let (_, level) = level.nest(i)?; let start = i; let (i, left) = Self::$inner(i, level)?; - let (i, right) = many0(pair(ws($op), |i| Self::$inner(i, level)))(i)?; + let (i, right) = many0(pair(ws($op), |i| Self::$inner(i, level))) + .map(|v: Vec<_>| v) + .parse_next(i)?; Ok(( i, right.into_iter().fold(left, |left, (op, right)| { @@ -102,7 +104,7 @@ impl<'a> Expr<'a> { move |i| Self::parse(i, level), ))(i)?; if has_named_arguments && !matches!(*expr, Self::NamedArgument(_, _)) { - Err(nom::Err::Failure(ErrorContext::new( + Err(winnow::Err::Cut(ErrorContext::new( "named arguments must always be passed last", start, ))) @@ -138,7 +140,7 @@ impl<'a> Expr<'a> { WithSpan::new(Self::NamedArgument(argument, Box::new(value)), start), )) } else { - Err(nom::Err::Failure(ErrorContext::new( + Err(winnow::Err::Cut(ErrorContext::new( format!("named argument `{argument}` was passed more than once"), start, ))) @@ -204,12 +206,12 @@ impl<'a> Expr<'a> { if crate::PRIMITIVE_TYPES.contains(&target) { return Ok((i, WithSpan::new(Self::As(Box::new(lhs), target), start))); } else if target.is_empty() { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "`as` operator expects the name of a primitive type on its right-hand side", before_keyword.trim_start(), ))); } else { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( format!( "`as` operator expects the name of a primitive type on its right-hand \ side, found `{target}`" @@ -224,7 +226,7 @@ impl<'a> Expr<'a> { let (i, rhs) = opt(terminated(opt(keyword("not")), ws(keyword("defined"))))(i)?; let ctor = match rhs { None => { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "expected `defined` or `not defined` after `is`", // We use `start` to show the whole `var is` thing instead of the current token. start, @@ -236,13 +238,13 @@ impl<'a> Expr<'a> { let var_name = match *lhs { Self::Var(var_name) => var_name, Self::Attr(_, _) => { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "`is defined` operator can only be used on variables, not on their fields", start, ))); } _ => { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "`is defined` operator can only be used on variables", start, ))); @@ -269,7 +271,7 @@ impl<'a> Expr<'a> { let (_, nested) = level.nest(i)?; let start = i; let (i, (ops, mut expr)) = pair( - many0(ws(alt((tag("!"), tag("-"), tag("*"), tag("&"))))), + many0(ws(alt((tag("!"), tag("-"), tag("*"), tag("&"))))).map(|v: Vec<_>| v), |i| Suffix::parse(i, nested), )(i)?; @@ -404,7 +406,7 @@ fn token_xor(i: &str) -> ParseResult<'_> { if good { Ok((i, "^")) } else { - Err(nom::Err::Failure(ErrorContext::new( + Err(winnow::Err::Cut(ErrorContext::new( "the binary XOR operator is called `xor` in rinja", i, ))) @@ -419,7 +421,7 @@ fn token_bitand(i: &str) -> ParseResult<'_> { if good { Ok((i, "&")) } else { - Err(nom::Err::Failure(ErrorContext::new( + Err(winnow::Err::Cut(ErrorContext::new( "the binary AND operator is called `bitand` in rinja", i, ))) @@ -464,7 +466,7 @@ impl<'a> Suffix<'a> { Some(Self::MacroCall(args)) => match expr.inner { Expr::Path(path) => expr = WithSpan::new(Expr::RustMacro(path, args), i), Expr::Var(name) => expr = WithSpan::new(Expr::RustMacro(vec![name], args), i), - _ => return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))), + _ => return Err(winnow::Err::Cut(error_position!(i, ErrorKind::Tag))), }, None => break, } diff --git a/rinja_parser/src/lib.rs b/rinja_parser/src/lib.rs index a827e361..9f1a70cc 100644 --- a/rinja_parser/src/lib.rs +++ b/rinja_parser/src/lib.rs @@ -10,14 +10,15 @@ use std::path::Path; use std::sync::Arc; use std::{fmt, str}; -use nom::branch::alt; -use nom::bytes::complete::{escaped, is_not, tag, take_till, take_while_m_n}; -use nom::character::complete::{anychar, char, one_of, satisfy}; -use nom::combinator::{consumed, cut, fail, map, not, opt, recognize, value}; -use nom::error::{ErrorKind, FromExternalError}; -use nom::multi::{many0_count, many1}; -use nom::sequence::{delimited, pair, preceded, tuple}; -use nom::{AsChar, InputTakeAtPosition}; +use winnow::Parser; +use winnow::branch::alt; +use winnow::bytes::complete::{escaped, is_not, tag, take_till, take_while_m_n, take_while1}; +use winnow::character::complete::{anychar, char, one_of, satisfy}; +use winnow::combinator::{consumed, cut, fail, map, not, opt, recognize, value}; +use winnow::error::{ErrorKind, FromExternalError}; +use winnow::multi::{many0_count, many1}; +use winnow::sequence::{delimited, pair, preceded, tuple}; +use winnow::stream::AsChar; pub mod expr; pub use expr::{Expr, Filter}; @@ -114,10 +115,10 @@ impl<'a> Ast<'a> { ) -> Result { match Node::parse_template(src, &State::new(syntax)) { Ok(("", nodes)) => Ok(Self { nodes }), - Ok(_) | Err(nom::Err::Incomplete(_)) => unreachable!(), + Ok(_) | Err(winnow::Err::Incomplete(_)) => unreachable!(), Err( - nom::Err::Error(ErrorContext { input, message, .. }) - | nom::Err::Failure(ErrorContext { input, message, .. }), + winnow::Err::Backtrack(ErrorContext { input, message, .. }) + | winnow::Err::Cut(ErrorContext { input, message, .. }), ) => Err(ParseError { message, offset: src.len() - input.len(), @@ -221,7 +222,7 @@ impl fmt::Display for ParseError { } } -pub(crate) type ParseErr<'a> = nom::Err>; +pub(crate) type ParseErr<'a> = winnow::Err>; pub(crate) type ParseResult<'a, T = &'a str> = Result<(&'a str, T), ParseErr<'a>>; /// This type is used to handle `nom` errors and in particular to add custom error messages. @@ -248,7 +249,7 @@ impl<'a> ErrorContext<'a> { } } -impl<'a> nom::error::ParseError<&'a str> for ErrorContext<'a> { +impl<'a> winnow::error::ParseError<&'a str> for ErrorContext<'a> { fn from_error_kind(input: &'a str, _code: ErrorKind) -> Self { Self { input, @@ -256,8 +257,8 @@ impl<'a> nom::error::ParseError<&'a str> for ErrorContext<'a> { } } - fn append(_: &'a str, _: ErrorKind, other: Self) -> Self { - other + fn append(self, _: &'a str, _: ErrorKind) -> Self { + self } } @@ -270,9 +271,9 @@ impl<'a, E: std::fmt::Display> FromExternalError<&'a str, E> for ErrorContext<'a } } -impl<'a> From> for nom::Err> { +impl<'a> From> for winnow::Err> { fn from(cx: ErrorContext<'a>) -> Self { - Self::Failure(cx) + Self::Cut(cx) } } @@ -302,7 +303,12 @@ fn skip_till<'a, 'b, O>( loop { i = match candidate_finder.split(i) { Some((_, j)) => j, - None => return Err(nom::Err::Error(ErrorContext::new("`end` not found`", i))), + None => { + return Err(winnow::Err::Backtrack(ErrorContext::new( + "`end` not found`", + i, + ))); + } }; i = match next(i)? { (j, Some(lookahead)) => return Ok((i, (j, lookahead))), @@ -321,17 +327,11 @@ fn keyword<'a>(k: &'a str) -> impl FnMut(&'a str) -> ParseResult<'a> { fn identifier(input: &str) -> ParseResult<'_> { fn start(s: &str) -> ParseResult<'_> { - s.split_at_position1_complete( - |c| !(c.is_alpha() || c == '_' || c >= '\u{0080}'), - nom::error::ErrorKind::Alpha, - ) + take_while1(|c: char| c.is_alpha() || c == '_' || c >= '\u{0080}').parse_next(s) } fn tail(s: &str) -> ParseResult<'_> { - s.split_at_position1_complete( - |c| !(c.is_alphanum() || c == '_' || c >= '\u{0080}'), - nom::error::ErrorKind::Alpha, - ) + take_while1(|c: char| c.is_alphanum() || c == '_' || c >= '\u{0080}').parse_next(s) } recognize(pair(start, opt(tail)))(input) @@ -362,7 +362,7 @@ fn num_lit<'a>(start: &'a str) -> ParseResult<'a, Num<'a>> { { Ok((i, value)) } else { - Err(nom::Err::Failure(ErrorContext::new( + Err(winnow::Err::Cut(ErrorContext::new( format!("unknown {kind} suffix `{suffix}`"), start, ))) @@ -381,7 +381,7 @@ fn num_lit<'a>(start: &'a str) -> ParseResult<'a, Num<'a>> { ))(i)?; match opt(separated_digits(base, false))(i)? { (i, Some(_)) => Ok((i, ())), - (_, None) => Err(nom::Err::Failure(ErrorContext::new( + (_, None) => Err(winnow::Err::Cut(ErrorContext::new( format!("expected digits after `{kind}`"), start, ))), @@ -396,7 +396,7 @@ fn num_lit<'a>(start: &'a str) -> ParseResult<'a, Num<'a>> { let (i, (kind, op)) = pair(one_of("eE"), opt(one_of("+-")))(i)?; match opt(separated_digits(10, op.is_none()))(i)? { (i, Some(_)) => Ok((i, ())), - (_, None) => Err(nom::Err::Failure(ErrorContext::new( + (_, None) => Err(winnow::Err::Cut(ErrorContext::new( format!("expected decimal digits, `+` or `-` after exponent `{kind}`"), start, ))), @@ -521,13 +521,13 @@ fn char_lit(i: &str) -> Result<(&str, CharLit<'_>), ParseErr<'_>> { ))(i)?; let Some(s) = s else { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "empty character literal", start, ))); }; let Ok(("", c)) = Char::parse(s) else { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "invalid character", start, ))); @@ -557,10 +557,10 @@ fn char_lit(i: &str) -> Result<(&str, CharLit<'_>), ParseErr<'_>> { }; let Ok(nb) = u32::from_str_radix(nb, 16) else { - return Err(nom::Err::Failure(ErrorContext::new(err1, start))); + return Err(winnow::Err::Cut(ErrorContext::new(err1, start))); }; if nb > max_value { - return Err(nom::Err::Failure(ErrorContext::new(err2, start))); + return Err(winnow::Err::Cut(ErrorContext::new(err2, start))); } Ok((i, CharLit { @@ -627,7 +627,7 @@ enum PathOrIdentifier<'a> { fn path_or_identifier(i: &str) -> ParseResult<'_, PathOrIdentifier<'_>> { let root = ws(opt(tag("::"))); - let tail = opt(many1(preceded(ws(tag("::")), identifier))); + let tail = opt(many1(preceded(ws(tag("::")), identifier)).map(|v: Vec<_>| v)); let (i, (root, start, rest)) = tuple((root, identifier, tail))(i)?; let rest = rest.as_deref().unwrap_or_default(); @@ -872,7 +872,7 @@ pub(crate) struct Level(u8); impl Level { fn nest(self, i: &str) -> ParseResult<'_, Level> { if self.0 >= Self::MAX_DEPTH { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "your template code is too deeply nested, or last expression is too complex", i, ))); @@ -899,7 +899,7 @@ fn filter<'a>( opt(|i| Expr::arguments(i, *level, false)), ))(j) } else { - Err(nom::Err::Failure(ErrorContext::new( + Err(winnow::Err::Cut(ErrorContext::new( "the filter operator `|` must not be preceded by any whitespace characters\n\ the binary OR operator is called `bitor` in rinja", i, diff --git a/rinja_parser/src/node.rs b/rinja_parser/src/node.rs index b4b652b1..15b031e7 100644 --- a/rinja_parser/src/node.rs +++ b/rinja_parser/src/node.rs @@ -1,13 +1,14 @@ use std::str; -use nom::branch::alt; -use nom::bytes::complete::{tag, take_till}; -use nom::character::complete::{anychar, char}; -use nom::combinator::{ +use winnow::Parser; +use winnow::branch::alt; +use winnow::bytes::complete::{tag, take_till}; +use winnow::character::complete::{anychar, char}; +use winnow::combinator::{ complete, consumed, cut, eof, fail, map, map_opt, not, opt, peek, recognize, value, }; -use nom::multi::{many0, separated_list0, separated_list1}; -use nom::sequence::{delimited, pair, preceded, tuple}; +use winnow::multi::{many0, separated_list0, separated_list1}; +use winnow::sequence::{delimited, pair, preceded, tuple}; use crate::memchr_splitter::{Splitter1, Splitter2, Splitter3}; use crate::{ @@ -41,7 +42,7 @@ impl<'a> Node<'a> { let (i, result) = match complete(|i| Self::many(i, s))(i) { Ok((i, result)) => (i, result), Err(err) => { - if let nom::Err::Error(err) | nom::Err::Failure(err) = &err { + if let winnow::Err::Backtrack(err) | winnow::Err::Cut(err) = &err { if err.message.is_none() { opt(|i| unexpected_tag(i, s))(err.input)?; } @@ -52,7 +53,7 @@ impl<'a> Node<'a> { let (i, _) = opt(|i| unexpected_tag(i, s))(i)?; let (i, is_eof) = opt(eof)(i)?; if is_eof.is_none() { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "cannot parse entire template\n\ you should never encounter this error\n\ please report this error to ", @@ -68,7 +69,9 @@ impl<'a> Node<'a> { map(|i| Comment::parse(i, s), Self::Comment), |i| Self::expr(i, s), |i| Self::parse(i, s), - )))(i) + ))) + .map(|v: Vec<_>| v) + .parse_next(i) } fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { @@ -127,7 +130,7 @@ impl<'a> Node<'a> { )); let (j, (pws, _, nws)) = p(i)?; if !s.is_in_loop() { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "you can only `break` inside a `for` loop", i, ))); @@ -143,7 +146,7 @@ impl<'a> Node<'a> { )); let (j, (pws, _, nws)) = p(i)?; if !s.is_in_loop() { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "you can only `continue` inside a `for` loop", i, ))); @@ -208,7 +211,7 @@ fn cut_node<'a, O>( let mut inner = cut(inner); move |i: &'a str| { let result = inner(i); - if let Err(nom::Err::Failure(err) | nom::Err::Error(err)) = &result { + if let Err(winnow::Err::Cut(err) | winnow::Err::Backtrack(err)) = &result { if err.message.is_none() { opt(|i| unexpected_raw_tag(kind, i))(err.input)?; } @@ -240,7 +243,7 @@ fn unexpected_raw_tag<'a>(kind: Option<&'static str>, i: &'a str) -> ParseResult tag if tag.starts_with("end") => format!("unexpected closing tag `{tag}`"), tag => format!("unknown node `{tag}`"), }; - Err(nom::Err::Failure(ErrorContext::new(msg, i))) + Err(winnow::Err::Cut(ErrorContext::new(msg, i))) } #[derive(Debug, PartialEq)] @@ -295,7 +298,7 @@ impl<'a> When<'a> { tuple(( opt(Whitespace::parse), |i| s.tag_block_end(i), - many0(value((), ws(|i| Comment::parse(i, s)))), + many0(value((), ws(|i| Comment::parse(i, s)))).map(|()| ()), )), ), ))), @@ -447,7 +450,7 @@ fn check_block_start<'a>( expected: &str, ) -> ParseResult<'a> { if i.is_empty() { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( format!("expected `{expected}` to terminate `{node}` node, found nothing"), start, ))); @@ -599,7 +602,7 @@ impl<'a> Macro<'a> { )); let (j, (pws1, _, (name, params, nws1, _))) = start(i)?; if is_rust_keyword(name) { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( format!("'{name}' is not a valid name for a macro"), i, ))); @@ -669,7 +672,8 @@ impl<'a> FilterBlock<'a> { opt(|i| Expr::arguments(i, s.level.get(), false)), many0(|i| { filter(i, &mut level).map(|(j, (name, params))| (j, (name, params, i))) - }), + }) + .map(|v: Vec<_>| v), ws(|i| Ok((i, ()))), opt(Whitespace::parse), |i| s.tag_block_end(i), @@ -829,8 +833,8 @@ impl<'a> Match<'a> { cut_node( Some("match"), tuple(( - ws(many0(ws(value((), |i| Comment::parse(i, s))))), - many0(|i| When::when(i, s)), + ws(many0(ws(value((), |i| Comment::parse(i, s))))).map(|()| ()), + many0(|i| When::when(i, s)).map(|v: Vec<_>| v), cut_node( Some("match"), tuple(( @@ -859,7 +863,7 @@ impl<'a> Match<'a> { arms.push(arm); } if arms.is_empty() { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "`match` nodes must contain at least one `when` node and/or an `else` case", start, ))); @@ -955,7 +959,7 @@ fn check_end_name<'a>( return Ok((after, end_name)); } - Err(nom::Err::Failure(ErrorContext::new( + Err(winnow::Err::Cut(ErrorContext::new( match name.is_empty() && !end_name.is_empty() { true => format!("unexpected name `{end_name}` in `end{kind}` tag for unnamed `{kind}`"), false => format!("expected name `{name}` in `end{kind}` tag, found `{end_name}`"), @@ -1112,7 +1116,7 @@ impl<'a> If<'a> { Some("if"), tuple(( |i| Node::many(i, s), - many0(|i| Cond::parse(i, s)), + many0(|i| Cond::parse(i, s)).map(|v: Vec<_>| v), cut_node( Some("if"), tuple(( @@ -1202,7 +1206,7 @@ impl<'a> Extends<'a> { ))(i)?; match (pws, nws) { (None, None) => Ok((i, WithSpan::new(Self { path }, start))), - (_, _) => Err(nom::Err::Failure(ErrorContext::new( + (_, _) => Err(winnow::Err::Cut(ErrorContext::new( "whitespace control is not allowed on `extends`", start, ))), @@ -1246,7 +1250,7 @@ impl<'a> Comment<'a> { Tag::Open => match depth.checked_add(1) { Some(new_depth) => depth = new_depth, None => { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "too deeply nested comments", start, ))); @@ -1269,7 +1273,7 @@ impl<'a> Comment<'a> { let mut ws = Ws(None, None); if content.len() == 1 && matches!(content, "-" | "+" | "~") { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( format!( "ambiguous whitespace stripping\n\ use `{}{content} {content}{}` to apply the same whitespace stripping on both \ @@ -1302,7 +1306,7 @@ fn end_node<'a, 'g: 'a>( if actual == expected { Ok((i, actual)) } else if actual.starts_with("end") { - Err(nom::Err::Failure(ErrorContext::new( + Err(winnow::Err::Cut(ErrorContext::new( format!("expected `{expected}` to terminate `{node}` node, found `{actual}`"), start, ))) diff --git a/rinja_parser/src/target.rs b/rinja_parser/src/target.rs index cf6298b1..dbdf4459 100644 --- a/rinja_parser/src/target.rs +++ b/rinja_parser/src/target.rs @@ -1,9 +1,10 @@ -use nom::branch::alt; -use nom::bytes::complete::tag; -use nom::character::complete::{char, one_of}; -use nom::combinator::{consumed, map, map_res, opt}; -use nom::multi::separated_list1; -use nom::sequence::{pair, preceded, tuple}; +use winnow::Parser; +use winnow::branch::alt; +use winnow::bytes::complete::tag; +use winnow::character::complete::{char, one_of}; +use winnow::combinator::{consumed, map, map_res, opt}; +use winnow::multi::separated_list1; +use winnow::sequence::{pair, preceded, tuple}; use crate::{ CharLit, ErrorContext, Num, ParseErr, ParseResult, PathOrIdentifier, State, StrLit, WithSpan, @@ -31,7 +32,8 @@ impl<'a> Target<'a> { /// Parses multiple targets with `or` separating them pub(super) fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { map( - separated_list1(ws(tag("or")), |i| s.nest(i, |i| Self::parse_one(i, s))), + separated_list1(ws(tag("or")), |i| s.nest(i, |i| Self::parse_one(i, s))) + .map(|v: Vec<_>| v), |mut opts| match opts.len() { 1 => opts.pop().unwrap(), _ => Self::OrChain(opts), @@ -132,7 +134,7 @@ impl<'a> Target<'a> { if let Some(rest) = rest { let (_, chr) = ws(opt(one_of(",:")))(i)?; if let Some(chr) = chr { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( format!( "unexpected `{chr}` character after `..`\n\ note that in a named struct, `..` must come last to ignore other members" @@ -142,7 +144,7 @@ impl<'a> Target<'a> { } if let Target::Rest(ref s) = rest.1 { if s.inner.is_some() { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "`@ ..` cannot be used in struct", s.span, ))); @@ -157,7 +159,7 @@ impl<'a> Target<'a> { )(init_i)?; if src == "_" { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "cannot use placeholder `_` as source in named struct", init_i, ))); @@ -182,9 +184,9 @@ impl<'a> Target<'a> { fn verify_name<'a>( input: &'a str, name: &'a str, -) -> Result, nom::Err>> { +) -> Result, winnow::Err>> { match name { - "self" | "writer" => Err(nom::Err::Failure(ErrorContext::new( + "self" | "writer" => Err(winnow::Err::Cut(ErrorContext::new( format!("cannot use `{name}` as a name"), input, ))), @@ -206,9 +208,9 @@ fn collect_targets<'a, T>( return Ok((i, (false, Vec::new()))); } - let (i, targets) = opt(separated_list1(ws(char(',')), |i| one(i, s)))(i)?; + let (i, targets) = opt(separated_list1(ws(char(',')), |i| one(i, s)).map(|v: Vec<_>| v))(i)?; let Some(targets) = targets else { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "expected comma separated list of members", i, ))); @@ -220,7 +222,7 @@ fn collect_targets<'a, T>( true => format!("expected member, or `{delim}` as terminator"), false => format!("expected `,` for more members, or `{delim}` as terminator"), }; - return Err(nom::Err::Failure(ErrorContext::new(msg, i))); + return Err(winnow::Err::Cut(ErrorContext::new(msg, i))); } let singleton = !has_comma && targets.len() == 1; @@ -237,13 +239,13 @@ fn only_one_rest_pattern<'a>( for target in &targets { if let Target::Rest(s) = target { if !allow_named_rest && s.inner.is_some() { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( "`@ ..` is only allowed in slice patterns", s.span, ))); } if found_rest { - return Err(nom::Err::Failure(ErrorContext::new( + return Err(winnow::Err::Cut(ErrorContext::new( format!("`..` can only be used once per {type_kind} pattern"), s.span, )));