From 1fabfc5d446134d8764ef228ad0ec4ad8eccc047 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 17 Dec 2024 14:53:50 -0600 Subject: [PATCH 1/4] fix(error)!: Remove nom from ConfigError Fixes #516 --- src/error.rs | 4 ++-- src/path/mod.rs | 21 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index 8f3363b8..9d6de82c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -46,7 +46,7 @@ pub enum ConfigError { NotFound(String), /// Configuration path could not be parsed. - PathParse(nom::error::ErrorKind), + PathParse { cause: Box }, /// Configuration could not be parsed from file. FileParse { @@ -187,7 +187,7 @@ impl fmt::Display for ConfigError { match *self { ConfigError::Frozen => write!(f, "configuration is frozen"), - ConfigError::PathParse(ref kind) => write!(f, "{}", kind.description()), + ConfigError::PathParse { ref cause } => write!(f, "{cause}"), ConfigError::Message(ref s) => write!(f, "{s}"), diff --git a/src/path/mod.rs b/src/path/mod.rs index 52089de1..dc42dd57 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -17,10 +17,29 @@ impl FromStr for Expression { type Err = ConfigError; fn from_str(s: &str) -> Result { - parser::from_str(s).map_err(ConfigError::PathParse) + parser::from_str(s).map_err(|e| ConfigError::PathParse { + cause: Box::new(ParseError::new(e)), + }) } } +#[derive(Debug)] +struct ParseError(String); + +impl ParseError { + fn new(inner: nom::error::ErrorKind) -> Self { + Self(inner.description().to_owned()) + } +} + +impl std::fmt::Display for ParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl std::error::Error for ParseError {} + fn sindex_to_uindex(index: isize, len: usize) -> usize { if index >= 0 { index as usize From 2153159231f31072b605576be6912ee422d8b26c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 17 Dec 2024 14:54:33 -0600 Subject: [PATCH 2/4] fix(error)!: Allow adding new errors --- src/error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/error.rs b/src/error.rs index 9d6de82c..13e13b82 100644 --- a/src/error.rs +++ b/src/error.rs @@ -38,6 +38,7 @@ impl fmt::Display for Unexpected { /// Represents all possible errors that can occur when working with /// configuration. +#[non_exhaustive] pub enum ConfigError { /// Configuration is frozen and no further mutations can be made. Frozen, From 8db5d3e5bd7c490a03752a3b3e79109c3eb2ea48 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 17 Dec 2024 14:57:31 -0600 Subject: [PATCH 3/4] refactor(path): Top-down organize the parser --- src/path/parser.rs | 74 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/path/parser.rs b/src/path/parser.rs index fbb8ba24..8fd02db3 100644 --- a/src/path/parser.rs +++ b/src/path/parser.rs @@ -12,42 +12,6 @@ use nom::{ use crate::path::Expression; -fn raw_ident(i: &str) -> IResult<&str, String> { - map( - is_a( - "abcdefghijklmnopqrstuvwxyz \ - ABCDEFGHIJKLMNOPQRSTUVWXYZ \ - 0123456789 \ - _-", - ), - ToString::to_string, - )(i) -} - -fn integer(i: &str) -> IResult<&str, isize> { - map_res( - delimited(space0, recognize(pair(opt(tag("-")), digit1)), space0), - FromStr::from_str, - )(i) -} - -fn ident(i: &str) -> IResult<&str, Expression> { - map(raw_ident, Expression::Identifier)(i) -} - -fn postfix<'a>(expr: Expression) -> impl FnMut(&'a str) -> IResult<&'a str, Expression> { - let e2 = expr.clone(); - let child = map(preceded(tag("."), raw_ident), move |id| { - Expression::Child(Box::new(expr.clone()), id) - }); - - let subscript = map(delimited(char('['), integer, char(']')), move |num| { - Expression::Subscript(Box::new(e2.clone()), num) - }); - - alt((child, subscript)) -} - pub(crate) fn from_str(input: &str) -> Result { match ident(input) { Ok((mut rem, mut expr)) => { @@ -73,7 +37,43 @@ pub(crate) fn from_str(input: &str) -> Result { } } -pub(crate) fn to_error_kind(e: Err>) -> ErrorKind { +fn ident(i: &str) -> IResult<&str, Expression> { + map(raw_ident, Expression::Identifier)(i) +} + +fn postfix<'a>(expr: Expression) -> impl FnMut(&'a str) -> IResult<&'a str, Expression> { + let e2 = expr.clone(); + let child = map(preceded(tag("."), raw_ident), move |id| { + Expression::Child(Box::new(expr.clone()), id) + }); + + let subscript = map(delimited(char('['), integer, char(']')), move |num| { + Expression::Subscript(Box::new(e2.clone()), num) + }); + + alt((child, subscript)) +} + +fn raw_ident(i: &str) -> IResult<&str, String> { + map( + is_a( + "abcdefghijklmnopqrstuvwxyz \ + ABCDEFGHIJKLMNOPQRSTUVWXYZ \ + 0123456789 \ + _-", + ), + ToString::to_string, + )(i) +} + +fn integer(i: &str) -> IResult<&str, isize> { + map_res( + delimited(space0, recognize(pair(opt(tag("-")), digit1)), space0), + FromStr::from_str, + )(i) +} + +fn to_error_kind(e: Err>) -> ErrorKind { match e { Err::Incomplete(_) => ErrorKind::Complete, Err::Failure(e) | Err::Error(e) => e.code, From f25e6a29c0c7bf685161db0d6319482b1e9aeb02 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 17 Dec 2024 15:21:51 -0600 Subject: [PATCH 4/4] refactor(path): Port to winnow --- Cargo.lock | 22 ++------- Cargo.toml | 2 +- src/path/mod.rs | 4 +- src/path/parser.rs | 117 ++++++++++++++++++++------------------------- 4 files changed, 59 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ada170a1..686a3d7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -246,7 +246,6 @@ dependencies = [ "indexmap 2.2.2", "json5", "log", - "nom", "notify", "pathdiff", "reqwest", @@ -260,6 +259,7 @@ dependencies = [ "tokio", "toml", "warp", + "winnow", "yaml-rust2", ] @@ -1030,12 +1030,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1104,16 +1098,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -2517,9 +2501,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 50cae5f4..cba1717b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,7 +122,6 @@ async = ["async-trait"] [dependencies] serde = "1.0" -nom = "7" async-trait = { version = "0.1", optional = true } toml = { version = "0.8", optional = true } @@ -134,6 +133,7 @@ json5_rs = { version = "0.4", optional = true, package = "json5" } indexmap = { version = "2.2", features = ["serde"], optional = true } convert_case = { version = "0.6", optional = true } pathdiff = "0.2" +winnow = "0.6.20" [dev-dependencies] serde_derive = "1.0" diff --git a/src/path/mod.rs b/src/path/mod.rs index dc42dd57..3c0b4213 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -27,8 +27,8 @@ impl FromStr for Expression { struct ParseError(String); impl ParseError { - fn new(inner: nom::error::ErrorKind) -> Self { - Self(inner.description().to_owned()) + fn new(inner: winnow::error::ContextError) -> Self { + Self(inner.to_string()) } } diff --git a/src/path/parser.rs b/src/path/parser.rs index 8fd02db3..6666e21f 100644 --- a/src/path/parser.rs +++ b/src/path/parser.rs @@ -1,83 +1,72 @@ use std::str::FromStr; -use nom::{ - branch::alt, - bytes::complete::{is_a, tag}, - character::complete::{char, digit1, space0}, - combinator::{map, map_res, opt, recognize}, - error::ErrorKind, - sequence::{delimited, pair, preceded}, - Err, IResult, -}; +use winnow::ascii::digit1; +use winnow::ascii::space0; +use winnow::combinator::dispatch; +use winnow::combinator::eof; +use winnow::combinator::fail; +use winnow::combinator::opt; +use winnow::combinator::repeat; +use winnow::combinator::seq; +use winnow::error::ContextError; +use winnow::prelude::*; +use winnow::token::any; +use winnow::token::take_while; use crate::path::Expression; -pub(crate) fn from_str(input: &str) -> Result { - match ident(input) { - Ok((mut rem, mut expr)) => { - while !rem.is_empty() { - match postfix(expr)(rem) { - Ok((rem_, expr_)) => { - rem = rem_; - expr = expr_; - } - - // Forward Incomplete and Error - result => { - return result.map(|(_, o)| o).map_err(to_error_kind); - } - } - } - - Ok(expr) - } - - // Forward Incomplete and Error - result => result.map(|(_, o)| o).map_err(to_error_kind), - } +pub(crate) fn from_str(mut input: &str) -> Result { + let input = &mut input; + path(input).map_err(|e| e.into_inner().unwrap()) } -fn ident(i: &str) -> IResult<&str, Expression> { - map(raw_ident, Expression::Identifier)(i) +fn path(i: &mut &str) -> PResult { + let root = ident.parse_next(i)?; + let expr = repeat(0.., postfix) + .fold( + || root.clone(), + |prev, cur| match cur { + Child::Key(k) => Expression::Child(Box::new(prev), k), + Child::Index(k) => Expression::Subscript(Box::new(prev), k), + }, + ) + .parse_next(i)?; + eof.parse_next(i)?; + Ok(expr) } -fn postfix<'a>(expr: Expression) -> impl FnMut(&'a str) -> IResult<&'a str, Expression> { - let e2 = expr.clone(); - let child = map(preceded(tag("."), raw_ident), move |id| { - Expression::Child(Box::new(expr.clone()), id) - }); - - let subscript = map(delimited(char('['), integer, char(']')), move |num| { - Expression::Subscript(Box::new(e2.clone()), num) - }); +fn ident(i: &mut &str) -> PResult { + raw_ident.map(Expression::Identifier).parse_next(i) +} - alt((child, subscript)) +fn postfix(i: &mut &str) -> PResult { + dispatch! {any; + '[' => seq!(integer.map(Child::Index), _: ']').map(|(i,)| i), + '.' => raw_ident.map(Child::Key), + _ => fail, + } + .parse_next(i) } -fn raw_ident(i: &str) -> IResult<&str, String> { - map( - is_a( - "abcdefghijklmnopqrstuvwxyz \ - ABCDEFGHIJKLMNOPQRSTUVWXYZ \ - 0123456789 \ - _-", - ), - ToString::to_string, - )(i) +enum Child { + Key(String), + Index(isize), } -fn integer(i: &str) -> IResult<&str, isize> { - map_res( - delimited(space0, recognize(pair(opt(tag("-")), digit1)), space0), - FromStr::from_str, - )(i) +fn raw_ident(i: &mut &str) -> PResult { + take_while(1.., ('a'..='z', 'A'..='Z', '0'..='9', '_', '-')) + .map(ToString::to_string) + .parse_next(i) } -fn to_error_kind(e: Err>) -> ErrorKind { - match e { - Err::Incomplete(_) => ErrorKind::Complete, - Err::Failure(e) | Err::Error(e) => e.code, - } +fn integer(i: &mut &str) -> PResult { + seq!( + _: space0, + (opt('-'), digit1).take().try_map(FromStr::from_str), + _: space0 + ) + .map(|(i,)| i) + .parse_next(i) } #[cfg(test)]