From 08bc972b2d70470fe956fd84c173fc5b76de817f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 31 Jan 2025 14:20:20 -0600 Subject: [PATCH] chore: Upgrade to Winnow 0.7 (#330) * chore: Upgrade to Winnow 0.6.26 * refactor: Resolve deprecations * refactor: Switch from Parser to ModalParser * chore: Upgrade to Winnow 0.7 I am not thrilled with the fact that annotations of some kind (I used no-op `map_err`s here) are needed for the errors and will be digging into this to better understand why. The code in `toml_edit` is very similar and yet it doesn't need them. * refactor: Remove From impl for ErrMode * refactor: Remove map_err's added to work around 'impl From for ErrMode' --- rinja_parser/Cargo.toml | 2 +- rinja_parser/src/expr.rs | 8 ++----- rinja_parser/src/lib.rs | 48 +++++++++++++++++++------------------- rinja_parser/src/node.rs | 16 ++++++------- rinja_parser/src/target.rs | 4 ++-- 5 files changed, 37 insertions(+), 41 deletions(-) diff --git a/rinja_parser/Cargo.toml b/rinja_parser/Cargo.toml index 35ab8185d..14bef8fd7 100644 --- a/rinja_parser/Cargo.toml +++ b/rinja_parser/Cargo.toml @@ -23,7 +23,7 @@ harness = false [dependencies] memchr = "2" serde = { version = "1.0", optional = true, features = ["derive"] } -winnow = "0.6.23" +winnow = "0.7.0" [dev-dependencies] criterion = "0.5" diff --git a/rinja_parser/src/expr.rs b/rinja_parser/src/expr.rs index a6f0264d5..c2e4f9cf3 100644 --- a/rinja_parser/src/expr.rs +++ b/rinja_parser/src/expr.rs @@ -6,7 +6,7 @@ use winnow::ascii::digit1; use winnow::combinator::{ alt, cut_err, fail, not, opt, peek, preceded, repeat, separated, terminated, }; -use winnow::error::{ErrorKind, ParserError as _}; +use winnow::error::ParserError as _; use winnow::stream::Stream as _; use crate::node::CondTest; @@ -625,11 +625,7 @@ impl<'a> Suffix<'a> { expr = WithSpan::new(Expr::RustMacro(vec![name], args), before_suffix) } _ => { - return Err(winnow::error::ErrMode::from_error_kind( - &before_suffix, - ErrorKind::Tag, - ) - .cut()); + return Err(winnow::error::ErrMode::from_input(&before_suffix).cut()); } }, } diff --git a/rinja_parser/src/lib.rs b/rinja_parser/src/lib.rs index 83188e6f7..23e0eb143 100644 --- a/rinja_parser/src/lib.rs +++ b/rinja_parser/src/lib.rs @@ -10,12 +10,12 @@ use std::path::Path; use std::sync::Arc; use std::{fmt, str}; -use winnow::Parser; use winnow::ascii::take_escaped; use winnow::combinator::{alt, cut_err, delimited, fail, not, opt, peek, preceded, repeat}; -use winnow::error::{ErrorKind, FromExternalError}; +use winnow::error::FromExternalError; use winnow::stream::{AsChar, Stream as _}; use winnow::token::{any, one_of, take_till, take_while}; +use winnow::{ModalParser, Parser}; pub mod expr; pub use expr::{Attr, Expr, Filter, TyGenerics}; @@ -308,28 +308,34 @@ impl<'a> ErrorContext<'a> { message: Some(message.into()), } } + + fn backtrack(self) -> winnow::error::ErrMode { + winnow::error::ErrMode::Backtrack(self) + } + + fn cut(self) -> winnow::error::ErrMode { + winnow::error::ErrMode::Cut(self) + } } impl<'a> winnow::error::ParserError<&'a str> for ErrorContext<'a> { - fn from_error_kind(input: &&'a str, _code: ErrorKind) -> Self { + type Inner = Self; + + fn from_input(input: &&'a str) -> Self { Self { span: (*input).into(), message: None, } } - fn append( - self, - _: &&'a str, - _: &<&str as winnow::stream::Stream>::Checkpoint, - _: ErrorKind, - ) -> Self { - self + #[inline(always)] + fn into_inner(self) -> Result { + Ok(self) } } impl<'a, E: std::fmt::Display> FromExternalError<&'a str, E> for ErrorContext<'a> { - fn from_external_error(input: &&'a str, _kind: ErrorKind, e: E) -> Self { + fn from_external_error(input: &&'a str, e: E) -> Self { Self { span: (*input).into(), message: Some(Cow::Owned(e.to_string())), @@ -337,12 +343,6 @@ impl<'a, E: std::fmt::Display> FromExternalError<&'a str, E> for ErrorContext<'a } } -impl<'a> From> for winnow::error::ErrMode> { - fn from(cx: ErrorContext<'a>) -> Self { - Self::Cut(cx) - } -} - #[inline] fn skip_ws0<'a>(i: &mut &'a str) -> ParseResult<'a, ()> { *i = i.trim_ascii_start(); @@ -361,8 +361,8 @@ fn skip_ws1<'a>(i: &mut &'a str) -> ParseResult<'a, ()> { } fn ws<'a, O>( - inner: impl Parser<&'a str, O, ErrorContext<'a>>, -) -> impl Parser<&'a str, O, ErrorContext<'a>> { + inner: impl ModalParser<&'a str, O, ErrorContext<'a>>, +) -> impl ModalParser<&'a str, O, ErrorContext<'a>> { delimited(skip_ws0, inner, skip_ws0) } @@ -370,8 +370,8 @@ fn ws<'a, O>( /// Returns tuple that would be returned when parsing `end`. fn skip_till<'a, 'b, O>( candidate_finder: impl crate::memchr_splitter::Splitter, - end: impl Parser<&'a str, O, ErrorContext<'a>>, -) -> impl Parser<&'a str, (&'a str, O), ErrorContext<'a>> { + end: impl ModalParser<&'a str, O, ErrorContext<'a>>, +) -> impl ModalParser<&'a str, (&'a str, O), ErrorContext<'a>> { let mut next = alt((end.map(Some), any.map(|_| None))); move |i: &mut &'a str| loop { *i = match candidate_finder.split(i) { @@ -392,7 +392,7 @@ fn skip_till<'a, 'b, O>( } } -fn keyword(k: &str) -> impl Parser<&str, &str, ErrorContext<'_>> { +fn keyword(k: &str) -> impl ModalParser<&str, &str, ErrorContext<'_>> { identifier.verify(move |v: &str| v == k) } @@ -508,7 +508,7 @@ fn num_lit<'a>(i: &mut &'a str) -> ParseResult<'a, Num<'a>> { fn separated_digits<'a>( radix: u32, start: bool, -) -> impl Parser<&'a str, &'a str, ErrorContext<'a>> { +) -> impl ModalParser<&'a str, &'a str, ErrorContext<'a>> { ( move |i: &mut &'a _| match start { true => Ok(()), @@ -758,7 +758,7 @@ impl State<'_, '_> { control.escape_default(), self.syntax.block_end.escape_default(), ); - Err(ParseErr::backtrack(ErrorContext::new(message, *i).into())) + Err(ErrorContext::new(message, *i).backtrack()) } else { Ok(()) } diff --git a/rinja_parser/src/node.rs b/rinja_parser/src/node.rs index 136886322..5b35ad72e 100644 --- a/rinja_parser/src/node.rs +++ b/rinja_parser/src/node.rs @@ -1,13 +1,13 @@ use std::collections::HashSet; use std::str::{self, FromStr}; -use winnow::Parser; use winnow::combinator::{ alt, cut_err, delimited, empty, eof, fail, not, opt, peek, preceded, repeat, separated, terminated, }; use winnow::stream::Stream as _; use winnow::token::{any, literal, rest}; +use winnow::{ModalParser, Parser}; use crate::memchr_splitter::{Splitter1, Splitter2, Splitter3}; use crate::{ @@ -120,7 +120,7 @@ impl<'a> Node<'a> { .parse_next(i)?; match closed { true => Ok(node), - false => Err(ErrorContext::unclosed("block", s.syntax.block_end, start).into()), + false => Err(ErrorContext::unclosed("block", s.syntax.block_end, start).cut()), } } @@ -188,7 +188,7 @@ impl<'a> Node<'a> { .parse_next(i)?; match closed { true => Ok(Self::Expr(Ws(pws, nws), expr)), - false => Err(ErrorContext::unclosed("expression", s.syntax.expr_end, start).into()), + false => Err(ErrorContext::unclosed("expression", s.syntax.expr_end, start).cut()), } } @@ -218,8 +218,8 @@ impl<'a> Node<'a> { fn cut_node<'a, O>( kind: Option<&'static str>, - inner: impl Parser<&'a str, O, ErrorContext<'a>>, -) -> impl Parser<&'a str, O, ErrorContext<'a>> { + inner: impl ModalParser<&'a str, O, ErrorContext<'a>>, +) -> impl ModalParser<&'a str, O, ErrorContext<'a>> { let mut inner = cut_err(inner); move |i: &mut &'a str| { let start = *i; @@ -1096,7 +1096,7 @@ impl<'a> Lit<'a> { return fail.parse_next(i); } Some(content) => content, - None => rest.parse_next(i)?, // there is no {block,comment,expr}_start: take everything + None => rest.parse_next(i)?, /* there is no {block,comment,expr}_start: take everything */ }; Ok(WithSpan::new(Self::split_ws_parts(content), start)) } @@ -1352,7 +1352,7 @@ impl<'a> Comment<'a> { let tag = opt(skip_till(splitter, |i: &mut _| tag(i, s))).parse_next(i)?; let Some((inclusive, tag)) = tag else { return Err( - ErrorContext::unclosed("comment", s.syntax.comment_end, start).into(), + ErrorContext::unclosed("comment", s.syntax.comment_end, start).cut(), ); }; match tag { @@ -1414,7 +1414,7 @@ pub struct Ws(pub Option, pub Option); fn end_node<'a, 'g: 'a>( node: &'g str, expected: &'g str, -) -> impl Parser<&'a str, &'a str, ErrorContext<'a>> + 'g { +) -> impl ModalParser<&'a str, &'a str, ErrorContext<'a>> + 'g { move |i: &mut &'a str| { let start = i.checkpoint(); let actual = ws(identifier).parse_next(i)?; diff --git a/rinja_parser/src/target.rs b/rinja_parser/src/target.rs index 5a2a39bb9..56d722fe1 100644 --- a/rinja_parser/src/target.rs +++ b/rinja_parser/src/target.rs @@ -1,6 +1,6 @@ -use winnow::Parser; use winnow::combinator::{alt, opt, peek, preceded, separated}; use winnow::token::one_of; +use winnow::{ModalParser, Parser}; use crate::{ CharLit, ErrorContext, Num, ParseErr, ParseResult, PathOrIdentifier, State, StrLit, WithSpan, @@ -217,7 +217,7 @@ fn verify_name<'a>( fn collect_targets<'a, T>( i: &mut &'a str, delim: char, - one: impl Parser<&'a str, T, ErrorContext<'a>>, + one: impl ModalParser<&'a str, T, ErrorContext<'a>>, ) -> ParseResult<'a, (bool, Vec)> { let opt_comma = ws(opt(',')).map(|o| o.is_some()); let mut opt_end = ws(opt(one_of(delim))).map(|o| o.is_some());