From 8a7b6394986a6fe697b4f4f780022ccc5e1f2223 Mon Sep 17 00:00:00 2001 From: Santiago Fraire Willemoes Date: Thu, 30 Jan 2025 17:41:29 -0300 Subject: [PATCH] fix: bump to winnow 0.7 --- Cargo.lock | 75 +++---------------------- crates/recipe-parser/Cargo.toml | 4 +- crates/recipe-parser/src/parser.rs | 88 +++++++++++++++--------------- 3 files changed, 54 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b71500a..0ed2694 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,54 +166,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - [[package]] name = "futures-macro" version = "0.3.31" @@ -225,12 +183,6 @@ dependencies = [ "syn", ] -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - [[package]] name = "futures-task" version = "0.3.31" @@ -249,13 +201,9 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "futures-channel", "futures-core", - "futures-io", "futures-macro", - "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", "slab", @@ -403,7 +351,7 @@ dependencies = [ "serde_json", "tsify", "wasm-bindgen", - "winnow 0.6.26", + "winnow", ] [[package]] @@ -454,21 +402,21 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rstest" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035" +checksum = "03e905296805ab93e13c1ec3a03f4b6c4f35e9498a3d5fa96dc626d22c03cd89" dependencies = [ - "futures", "futures-timer", + "futures-util", "rstest_macros", "rustc_version", ] [[package]] name = "rstest_macros" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a" +checksum = "ef0053bbffce09062bee4bcc499b0fbe7a57b879f1efe088d6d8d4c7adcdef9b" dependencies = [ "cfg-if", "glob", @@ -636,7 +584,7 @@ checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.7.0", + "winnow", ] [[package]] @@ -823,15 +771,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.6.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.7.0" diff --git a/crates/recipe-parser/Cargo.toml b/crates/recipe-parser/Cargo.toml index 4f3e12f..09ad1e6 100644 --- a/crates/recipe-parser/Cargo.toml +++ b/crates/recipe-parser/Cargo.toml @@ -12,14 +12,14 @@ homepage = { workspace = true } categories = ["command-line-interface", "parser-implementations"] [dependencies] -winnow = "0.6" +winnow = "0.7" schemars = { version = "0.8.16", optional = true } serde = { version = "1", features = ["derive"], optional = true } wasm-bindgen = {version = "0.2.92", optional = true} tsify = { version = "0.4.5", optional = true} [dev-dependencies] -rstest = "0.23.0" +rstest = "0.24.0" serde_json = "1.0" [lib] diff --git a/crates/recipe-parser/src/parser.rs b/crates/recipe-parser/src/parser.rs index 1ddf319..a3db5ca 100644 --- a/crates/recipe-parser/src/parser.rs +++ b/crates/recipe-parser/src/parser.rs @@ -1,19 +1,19 @@ use std::fmt::Display; use winnow::ascii::{line_ending, multispace0, multispace1, space0, space1}; -use winnow::combinator::{alt, cut_err, delimited, opt, preceded, repeat, rest}; +use winnow::combinator::{alt, cut_err, delimited, opt, preceded, repeat}; use winnow::error::{ContextError, ParseError, StrContext, StrContextValue}; -use winnow::token::{take_till, take_until, take_while}; -use winnow::{Located, PResult, Parser}; +use winnow::token::{rest, take_till, take_until, take_while}; +use winnow::{LocatingSlice, ModalResult, Parser}; -type Input<'a> = Located<&'a str>; +type Input<'a> = LocatingSlice<&'a str>; /// Parses a valid string from the input. /// /// This function takes a mutable reference to a string slice and parses a valid string from it. /// A valid string can contain alphanumeric characters as well as certain symbols and spaces. /// The function returns a `PResult` containing the parsed valid string. -fn parse_valid_string<'a>(input: &mut Input<'a>) -> PResult<&'a str> { +fn parse_valid_string<'a>(input: &mut Input<'a>) -> ModalResult<&'a str> { let spaces_and_symbols = "\t /-_@.,%#'"; take_while(1.., move |c: char| { c.is_alphanumeric() || spaces_and_symbols.contains(c) @@ -26,7 +26,7 @@ fn parse_valid_string<'a>(input: &mut Input<'a>) -> PResult<&'a str> { /// ```recp /// /* */ /// ``` -fn parse_comment<'a>(input: &mut Input<'a>) -> PResult<&'a str> { +fn parse_comment<'a>(input: &mut Input<'a>) -> ModalResult<&'a str> { delimited( "/*", cut_err(take_until(0.., "*/")) @@ -43,7 +43,7 @@ fn parse_comment<'a>(input: &mut Input<'a>) -> PResult<&'a str> { /// {salt} /// {tomatoes} /// ``` -fn parse_curly<'a>(input: &mut Input<'a>) -> PResult<&'a str> { +fn parse_curly<'a>(input: &mut Input<'a>) -> ModalResult<&'a str> { delimited( "{", parse_valid_string.map(|v| v.trim()), @@ -63,7 +63,7 @@ fn parse_curly<'a>(input: &mut Input<'a>) -> PResult<&'a str> { /// 3_000_000 /// 2/3 /// ``` -fn parse_quantity<'a>(input: &mut Input<'a>) -> PResult<&'a str> { +fn parse_quantity<'a>(input: &mut Input<'a>) -> ModalResult<&'a str> { let spaces_and_symbols = ".,/_"; cut_err( @@ -87,14 +87,14 @@ fn parse_quantity<'a>(input: &mut Input<'a>) -> PResult<&'a str> { } /// Parse units like kg, kilograms, pinch, etc. -fn parse_unit<'a>(input: &mut Input<'a>) -> PResult<&'a str> { +fn parse_unit<'a>(input: &mut Input<'a>) -> ModalResult<&'a str> { parse_valid_string.parse_next(input) } /// Ingredient amounts are surrounded by parenthesis fn parse_ingredient_amount<'a>( input: &mut Input<'a>, -) -> PResult<(Option<&'a str>, Option<&'a str>)> { +) -> ModalResult<(Option<&'a str>, Option<&'a str>)> { delimited( ("(", space0), ( @@ -116,7 +116,7 @@ fn parse_ingredient_amount<'a>( /// ``` fn parse_ingredient<'a>( input: &mut Input<'a>, -) -> PResult<(&'a str, Option<(Option<&'a str>, Option<&'a str>)>)> { +) -> ModalResult<(&'a str, Option<(Option<&'a str>, Option<&'a str>)>)> { (parse_curly, opt(parse_ingredient_amount)).parse_next(input) } @@ -127,7 +127,7 @@ fn parse_ingredient<'a>( /// &{small jar} /// &{stick} /// ``` -fn parse_material<'a>(input: &mut Input<'a>) -> PResult<&'a str> { +fn parse_material<'a>(input: &mut Input<'a>) -> ModalResult<&'a str> { preceded("&", parse_curly).parse_next(input) } @@ -137,7 +137,7 @@ fn parse_material<'a>(input: &mut Input<'a>) -> PResult<&'a str> { /// t{25 minutes} /// t{10 sec} /// ``` -fn parse_timer<'a>(input: &mut Input<'a>) -> PResult<&'a str> { +fn parse_timer<'a>(input: &mut Input<'a>) -> ModalResult<&'a str> { preceded("t", parse_curly).parse_next(input) } @@ -149,16 +149,16 @@ fn parse_timer<'a>(input: &mut Input<'a>) -> PResult<&'a str> { /// ``` fn parse_recipe_ref<'a>( input: &mut Input<'a>, -) -> PResult<(&'a str, Option<(Option<&'a str>, Option<&'a str>)>)> { +) -> ModalResult<(&'a str, Option<(Option<&'a str>, Option<&'a str>)>)> { preceded("@", (parse_curly, opt(parse_ingredient_amount))).parse_next(input) } /// Tokens are separated into words -fn parse_word<'a>(input: &mut Input<'a>) -> PResult<&'a str> { +fn parse_word<'a>(input: &mut Input<'a>) -> ModalResult<&'a str> { take_till(1.., (' ', '\t', '\r', '\n', '\'', '`')).parse_next(input) } -fn parse_metadata<'a>(input: &mut Input<'a>) -> PResult<(&'a str, &'a str)> { +fn parse_metadata<'a>(input: &mut Input<'a>) -> ModalResult<(&'a str, &'a str)> { preceded( (">>", space0), ( @@ -175,7 +175,7 @@ fn parse_metadata<'a>(input: &mut Input<'a>) -> PResult<(&'a str, &'a str)> { /// --- /// This recipe was given by my grandma /// ``` -fn parse_backstory<'a>(input: &mut Input<'a>) -> PResult<&'a str> { +fn parse_backstory<'a>(input: &mut Input<'a>) -> ModalResult<&'a str> { preceded( delimited( preceded(line_ending, multispace0), @@ -198,7 +198,7 @@ fn parse_backstory<'a>(input: &mut Input<'a>) -> PResult<&'a str> { /// There's a word connected to the ingredient by a symbol, in order to be prevent /// the `parse_word` from ingesting the chunk `l'{ingredient}` as a word, we need /// to tell `parse_word` to stop at this character and also we need to catch it here. -fn parse_special_symbols<'a>(input: &mut Input<'a>) -> PResult<&'a str> { +fn parse_special_symbols<'a>(input: &mut Input<'a>) -> ModalResult<&'a str> { alt(("(", "'", "`")).parse_next(input) } /* **************** @@ -311,7 +311,7 @@ impl Display for Token<'_> { } } -pub fn recipe_value<'a>(input: &mut Input<'a>) -> PResult> { +pub fn recipe_value<'a>(input: &mut Input<'a>) -> ModalResult> { alt(( parse_metadata.map(|(key, value)| Token::Metadata { key, value }), parse_material.map(|m| Token::Material(m)), @@ -356,7 +356,7 @@ pub fn recipe_value<'a>(input: &mut Input<'a>) -> PResult> { .parse_next(input) } -pub fn recipe<'a>(input: &mut Input<'a>) -> PResult>> { +pub fn recipe<'a>(input: &mut Input<'a>) -> ModalResult>> { repeat(0.., recipe_value).parse_next(input) } @@ -372,8 +372,8 @@ pub fn recipe<'a>(input: &mut Input<'a>) -> PResult>> { /// /// println!("{result:?}"); /// ``` -pub fn parse(input: &str) -> Result>, ParseError, ContextError>> { - let input = Located::new(input); +pub fn parse(input: &str) -> Result>, ParseError, ContextError>> { + let input = LocatingSlice::new(input); recipe.parse(input) } @@ -396,7 +396,7 @@ mod test { #[case("#vegan", "#vegan")] #[case("mango's", "mango's")] fn test_parse_valid_string(#[case] input: String, #[case] expected: &str) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let valid_str = parse_valid_string(&mut input).unwrap(); assert_eq!(valid_str, expected) } @@ -406,14 +406,14 @@ mod test { #[case("/* hello */", "hello")] #[case("/* multi\nline\ncomment */", "multi\nline\ncomment")] fn test_parse_comment_ok(#[case] input: String, #[case] expected: &str) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let comment = parse_comment(&mut input).expect("failed to parse comment"); assert_eq!(comment, expected) } #[test] fn test_parse_comment_wrong() { - let mut input = Located::new("/* unclosed"); + let mut input = LocatingSlice::new("/* unclosed"); let res = parse_comment(&mut input); assert!(res.is_err()); @@ -429,18 +429,18 @@ mod test { #[case("{15 minutes}", "15 minutes")] #[case("{ 15 minutes }", "15 minutes")] fn test_parse_curly_ok(#[case] input: String, #[case] expected: &str) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let content = parse_curly(&mut input).expect("to work"); assert_eq!(expected, content); } #[test] fn test_parse_curly_wrong() { - let mut input = Located::new("{}"); + let mut input = LocatingSlice::new("{}"); let res = parse_curly(&mut input); assert!(res.is_err()); - let mut input = Located::new("{unclosed"); + let mut input = LocatingSlice::new("{unclosed"); let res = parse_curly(&mut input); assert!(res.is_err()); @@ -457,7 +457,7 @@ mod test { #[case("1/2", "1/2")] #[case(".2", ".2")] fn test_parse_quantity_ok(#[case] input: String, #[case] expected: &str) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let content = parse_quantity(&mut input).expect("to work"); assert_eq!(expected, content); } @@ -469,7 +469,7 @@ mod test { #[case("2//0")] fn test_parse_quantity_invalid(#[case] input: String) { // TODO: Add verify function to validate the last char - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let res = parse_quantity(&mut input); let err = res.unwrap_err(); assert!(matches!(err, winnow::error::ErrMode::Cut(_))); @@ -486,7 +486,7 @@ mod test { #[case] input: String, #[case] expected: (Option<&str>, Option<&str>), ) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let content = parse_ingredient_amount(&mut input).expect("to work"); assert_eq!(expected, content); } @@ -495,7 +495,7 @@ mod test { #[case("()")] #[case("(unclosed")] fn test_parse_ingredient_amount_invalid_quantity(#[case] input: String) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let res = parse_ingredient_amount(&mut input); match res { Ok(_) => { @@ -521,7 +521,7 @@ mod test { #[rstest] #[case("(1.5")] fn test_parse_ingredient_amount_invalid_amount(#[case] input: String) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let res = parse_ingredient_amount(&mut input); match res { Ok(_) => { @@ -549,7 +549,7 @@ mod test { #[case] expected_ingredient: &str, #[case] expected_amount: Option<(Option<&str>, Option<&str>)>, ) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let (ingredient, amount) = parse_ingredient(&mut input).unwrap(); assert_eq!(expected_ingredient, ingredient); assert_eq!(expected_amount, amount); @@ -561,7 +561,7 @@ mod test { #[case("&{stick}", "stick")] #[case("&{bricks}", "bricks")] fn test_parse_material_ok(#[case] input: String, #[case] expected: &str) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let material = parse_material(&mut input).expect("Failed to parse material"); assert_eq!(material, expected) } @@ -569,7 +569,7 @@ mod test { #[rstest] #[case("t{1 minute}", "1 minute")] fn test_parse_timer_ok(#[case] input: String, #[case] expected: &str) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let timer = parse_timer(&mut input).expect("Failed to parse timer"); assert_eq!(timer, expected) } @@ -583,7 +583,7 @@ mod test { #[case] expected_recipe: &str, #[case] expected_amount: Option<(Option<&str>, Option<&str>)>, ) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let (recipe, amount) = parse_recipe_ref(&mut input).unwrap(); assert_eq!(expected_recipe, recipe); assert_eq!(expected_amount, amount); @@ -598,7 +598,7 @@ mod test { #[case(">> key:\t\tpepe\n", ("key", "pepe"))] #[case(">> key:pepe\n", ("key", "pepe"))] fn test_parse_metadata_ok(#[case] input: String, #[case] expected: (&str, &str)) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let metadata = parse_metadata(&mut input).expect("Failed to parse metadata"); assert_eq!(metadata, expected) } @@ -610,7 +610,7 @@ mod test { #[case("\n ---\n\nthis is **markdown**", "this is **markdown**")] #[case("\n ---\n\nthis is [markdown](url)", "this is [markdown](url)")] fn test_parse_backstory_ok(#[case] input: String, #[case] expected: &str) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let backsotry = parse_backstory(&mut input).expect("failed to parse backstory"); assert_eq!(backsotry, expected) } @@ -618,7 +618,7 @@ mod test { #[rstest] #[case("\n--- \nwhat a backstory")] fn test_parse_backstory_fail(#[case] input: String) { - let mut input = Located::new(input.as_str()); + let mut input = LocatingSlice::new(input.as_str()); let out = parse_backstory(&mut input); assert!(out.is_err()) } @@ -627,7 +627,7 @@ mod test { #[case(" ", Token::Space(" "))] #[case("{holis}(100 gr)", Token::Ingredient { name: "holis", quantity: Some("100"), unit: Some("gr") })] fn test_recipe_value_ok(#[case] input: &str, #[case] expected: Token) { - let mut input = Located::new(input); + let mut input = LocatingSlice::new(input); let token = recipe_value(&mut input).expect("failed to parse token"); assert_eq!(token, expected) } @@ -636,7 +636,9 @@ mod test { fn test_recipe_ok() { let input = "Boil the quinoa for t{5 minutes} in a &{pot}.\nPut the boiled {quinoa}(200gr) in the base of the bowl."; let expected = "Boil the quinoa for 5 minutes in a pot.\nPut the boiled quinoa in the base of the bowl."; - let recipe = recipe.parse(Located::new(input)).expect("parse failed"); + let recipe = recipe + .parse(LocatingSlice::new(input)) + .expect("parse failed"); let fmt_recipe = recipe .iter() .fold(String::new(), |acc, val| format!("{acc}{val}")); @@ -654,7 +656,7 @@ mod test { #[case(">> source: https://hello.com\n>> tags: hello\n", vec![Token::Metadata {key: "source", value: "https://hello.com"}, Token::Space("\n"), Token::Metadata {key: "tags", value: "hello"}, Token::Space("\n")])] #[case("{holis}(100 gr)", vec![Token::Ingredient { name: "holis", quantity: Some("100"), unit: Some("gr") }])] fn test_recipe_cases_ok(#[case] input: &str, #[case] expected: Vec) { - let mut input = Located::new(input); + let mut input = LocatingSlice::new(input); let token = recipe(&mut input).expect("failed to parse token"); assert_eq!(token, expected) }