diff --git a/crates/winnow-datetime-assert/CHANGELOG.md b/crates/winnow-datetime-assert/CHANGELOG.md new file mode 100644 index 0000000..76c5f01 --- /dev/null +++ b/crates/winnow-datetime-assert/CHANGELOG.md @@ -0,0 +1,10 @@ +## 0.2.0 - 2024-05-04 +* Changed parser signatures to meet winnow 0.7 standards +* Removed dependency on galvanic-test which was accidentally left over from some initial design experiments. +* Removed paste dependency which is no longer used. + +## 0.1.0 - 2024-12-29 + +Initial release + +* Best efforts to fully cover common specs diff --git a/crates/winnow-datetime-assert/Cargo.toml b/crates/winnow-datetime-assert/Cargo.toml index c464f09..3421d49 100644 --- a/crates/winnow-datetime-assert/Cargo.toml +++ b/crates/winnow-datetime-assert/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "winnow_datetime_assert" -version = "0.1.0" +version = "0.2.0" description = "Testing/Benchmarking winnow-datetime parsers" keywords = [ "iso8601", "date-time", "parser", "winnow" ] categories = [ "parser-implementations", "date-and-time" ] @@ -13,11 +13,9 @@ readme = "README.md" edition = "2021" [dependencies] -winnow_datetime = { path = "../winnow-datetime", version = "0.1.0", features = ["serde"] } +winnow_datetime = { path = "../winnow-datetime", version = "0.2.0", features = ["serde"] } libtest-mimic = "0.8.1" -galvanic-test = "0.2.0" -winnow = "0.6.20" -paste = "1.0.15" +winnow = "0.7" serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9.34-deprecated" diff --git a/crates/winnow-datetime-assert/src/lib.rs b/crates/winnow-datetime-assert/src/lib.rs index dc503a4..62dc65e 100644 --- a/crates/winnow-datetime-assert/src/lib.rs +++ b/crates/winnow-datetime-assert/src/lib.rs @@ -132,11 +132,33 @@ macro_rules! define_format_tests { (covered, uncovered) }); - fn parse_input(input: &str) -> Result<$piece_type, String> { - match terminated($parser, eof).parse_next(&mut input.as_bytes()) { - Ok(p) => Ok(p), - Err(e) => Err(format!("Failed to parse {}: {}", input, e)), - } + fn parse_input<'i, Input>(input: &mut Input) -> Result<$piece_type, String> + where + Input: StreamIsPartial + + Stream + + Compare<&'i str> + + AsBStr + + Clone + + std::fmt::Display, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + { + let o = $parser::>(input).map_err(|e| { + format!( + "Failed to parse datetime: {}: {}", + String::from_utf8_lossy(input.as_bstr()), + e.to_string() + ) + })?; + let _ = eof::>(input).map_err(|e| { + format!( + "Remaining input parsing datetime: {}: {}", + String::from_utf8_lossy(input.as_bstr()), + e.to_string() + ) + })?; + + Ok(o) } // Generate a trial for each assertion @@ -144,7 +166,7 @@ macro_rules! define_format_tests { let name = format!("parses - {}", assertion.format); trials.push(Trial::test(name, move || { // Parse the input - let result = parse_input(&assertion.input); + let result = parse_input(&mut assertion.input.as_str()); // If covered, the result must match the expected value if result != Ok(assertion.expected) { @@ -162,7 +184,7 @@ macro_rules! define_format_tests { let name = format!("rejects - {}", assertion.format); trials.push(Trial::test(name, move || { // Parse the input - let result = parse_input(&assertion.input); + let result = parse_input(&mut assertion.input.as_str()); // If not covered, the result must be an error if result.is_ok() { diff --git a/crates/winnow-datetime/CHANGELOG.md b/crates/winnow-datetime/CHANGELOG.md new file mode 100644 index 0000000..4288f47 --- /dev/null +++ b/crates/winnow-datetime/CHANGELOG.md @@ -0,0 +1,5 @@ +## 0.2.0 - 2015-05-04 +* Changed `Stream` helper type to `PartialInput` since this name conflicts with the winnow naming scheme and causes confusion. +* Bumped winnow version to 0.7 and changed parser singatures to match standard winnow parsers + +## 0.1.0 - 2014-12-29 - Initial Release diff --git a/crates/winnow-datetime/Cargo.toml b/crates/winnow-datetime/Cargo.toml index 041153d..c70adfb 100644 --- a/crates/winnow-datetime/Cargo.toml +++ b/crates/winnow-datetime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "winnow_datetime" -version = "0.1.0" +version = "0.2.0" description = "Parsing dates using winnow" keywords = [ "iso8601", "date-time", "parser", "winnow" ] categories = [ "parser-implementations", "date-and-time" ] @@ -13,7 +13,7 @@ readme = "README.md" edition = "2021" [dependencies] -winnow = "0.6.20" +winnow = "0.7" chrono = { version = "0.4", default-features = false, optional = true } time = { version = "0.3.37", default-features = false, optional = true } num-traits = { version = "0.2", optional = true } diff --git a/crates/winnow-datetime/src/lib.rs b/crates/winnow-datetime/src/lib.rs index 66285cc..fc119b8 100644 --- a/crates/winnow-datetime/src/lib.rs +++ b/crates/winnow-datetime/src/lib.rs @@ -18,4 +18,4 @@ pub use types::Time; use winnow::Partial; /// Type for holding partial data for parsers -pub type Stream<'i> = Partial<&'i [u8]>; +pub type PartialInput<'i> = Partial<&'i [u8]>; diff --git a/crates/winnow-datetime/src/parser.rs b/crates/winnow-datetime/src/parser.rs index 0a00974..9ce9249 100644 --- a/crates/winnow-datetime/src/parser.rs +++ b/crates/winnow-datetime/src/parser.rs @@ -5,252 +5,258 @@ use core::str; use std::ops::RangeBounds; -use winnow::ascii::digit1; +use winnow::ascii::{digit1, Int, Uint}; use winnow::combinator::{alt, trace}; -use winnow::error::{ContextError, ErrMode}; -use winnow::stream::{AsBStr, AsChar, Compare, Stream as InputStream, StreamIsPartial}; +use winnow::error::ParserError; +use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial}; use winnow::token::{literal, take_while}; -use winnow::{PResult, Parser}; - +use winnow::Parser; // UTILITY /// Exactly 1 digit -pub fn digit_1<'i, Input>(i: &mut Input) -> PResult +pub fn digit_1(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("digit_1", move |input: &mut Input| { take_exact_digits(input, 1) }) - .parse_next(i) + .parse_next(input) } /// Exactly 2 digits -pub fn digit_2<'i, Input>(i: &mut Input) -> PResult +pub fn digit_2(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("digit_2", move |input: &mut Input| { take_exact_digits(input, 2) }) - .parse_next(i) + .parse_next(input) } /// Exactly 3 digits -pub fn digit_3<'i, Input>(i: &mut Input) -> PResult +pub fn digit_3(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("digit_3", move |input: &mut Input| { take_exact_digits(input, 3) }) - .parse_next(i) + .parse_next(input) } /// Exactly 4 digits -pub fn digit_4<'i, Input>(i: &mut Input) -> PResult +pub fn digit_4(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("digit_4", move |input: &mut Input| { take_exact_digits(input, 4) }) - .parse_next(i) + .parse_next(input) } /// Exactly 6 digits -pub fn digit_6<'i, Input>(i: &mut Input) -> PResult +pub fn digit_6(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("digit_6", move |input: &mut Input| { take_exact_digits(input, 2) }) - .parse_next(i) + .parse_next(input) } -fn take_exact_digits<'i, Input>(i: &mut Input, places: usize) -> PResult +pub(crate) fn take_exact_digits( + input: &mut Input, + places: usize, +) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { - let n = take_while(places, |c: ::Token| { - AsChar::is_dec_digit(&c.as_char()) - }) - .parse_next(i)?; + let n = take_while(places, AsChar::is_dec_digit).parse_next(input)?; - let s = str::from_utf8(n.as_bstr()).expect("Invalid data, expected UTF-8 string"); + let n = String::from_utf8_lossy(n.as_bstr()); - let number: u32 = s - .parse() - .expect("Invalid string, expected ASCII representation of a number"); + let n = u32::try_from_dec_uint(n.as_ref()).unwrap(); - Ok(number) + Ok(n) } -pub fn take_digits<'i, Input>(i: &mut Input) -> PResult +pub fn take_digits<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("take_digits", move |input: &mut Input| { - let digits = take_while(1.., |c: ::Token| c.is_dec_digit()) - .parse_next(input)?; + let out = take_while(1.., AsChar::is_dec_digit).parse_next(input)?; + let out = String::from_utf8_lossy(out.as_bstr()); + let out = u32::try_from_dec_uint(out.as_ref()).unwrap(); - if digits.as_bstr().is_empty() { - return Err(ErrMode::Backtrack(ContextError::new())); - } - - let s = str::from_utf8(digits.as_bstr()).expect("Invalid data, expected UTF-8 string"); - let res = s - .parse() - .expect("Invalid string, expected ASCII representation of a number"); - - Ok(res) + Ok(out) }) - .parse_next(i) + .parse_next(input) } -pub fn take_digits_in_range<'i, Input>( - i: &mut Input, +pub fn take_digits_in_range( + input: &mut Input, places: usize, range: impl RangeBounds, -) -> PResult +) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { - let n = take_while(places, |c: ::Token| { - c.as_char().is_digit(10) - }) - .parse_next(i)?; - - let s = str::from_utf8(n.as_bstr()).expect("Invalid data, expected UTF-8 string"); + let out = take_while(places, AsChar::is_dec_digit).parse_next(input)?; + let out = String::from_utf8_lossy(out.as_bstr()); + let out = u32::try_from_dec_uint(out.as_ref()).unwrap(); - let number: u32 = s - .parse() - .expect("Invalid string, expected ASCII representation of a number"); - - if range.contains(&number) { - Ok(number) + if range.contains(&out) { + Ok(out) } else { - return Err(ErrMode::Backtrack(ContextError::new())); + Err(ParserError::from_input(input)) } } -pub fn sign<'i, Input>(i: &mut Input) -> PResult +pub fn sign<'a, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'a str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { - alt((literal("-"), literal("+"))) - .map(|s: ::Slice| match s.as_bstr() { - b"-" => -1, - _ => 1, - }) - .parse_next(i) + trace("sign", move |input: &mut Input| { + let i = alt((literal("-"), literal("+"))) + .map(|s: ::Slice| match s.as_bstr() { + b"-" => "-1", + _ => "1", + }) + .parse_next(input)?; + + Ok(i32::try_from_dec_int(i).unwrap()) + }) + .parse_next(input) } // DATE // MM -pub fn date_month<'i, Input>(i: &mut Input) -> PResult +pub fn date_month(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("date_month", move |input: &mut Input| { take_digits_in_range(input, 2, 1..=12) }) - .parse_next(i) + .parse_next(input) } // DD -pub fn date_day<'i, Input>(i: &mut Input) -> PResult +pub fn date_day(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("date_day", move |input: &mut Input| { take_digits_in_range(input, 2, 1..=31) }) - .parse_next(i) + .parse_next(input) } // TIME // HH -pub fn time_hour<'i, Input>(i: &mut Input) -> PResult +pub fn time_hour(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("time_hour", move |input: &mut Input| { take_digits_in_range(input, 2, 0..=23) }) - .parse_next(i) + .parse_next(input) } // MM -pub fn time_minute<'i, Input>(i: &mut Input) -> PResult +pub fn time_minute(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("time_minute", move |input: &mut Input| { take_digits_in_range(input, 2, 0..=59) }) - .parse_next(i) + .parse_next(input) } // SS -pub fn time_second<'i, Input>(i: &mut Input) -> PResult +pub fn time_second(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("time_second", move |input: &mut Input| { take_digits_in_range(input, 2, 0..=60) }) - .parse_next(i) + .parse_next(input) } // Converts the fractional part if-any of a number of seconds to milliseconds // truncating towards zero if there are more than three digits. // e.g. "" -> 0, "1" -> 100, "12" -> 120, "123" -> 123, "1234" -> 123 -pub fn fraction_millisecond<'i, Input>(i: &mut Input) -> PResult +pub fn fraction_millisecond<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream, + ::Slice: AsBStr, + ::Token: AsChar, + + Error: ParserError, { trace("fraction_millisecond", move |input: &mut Input| { let d = digit1(input)?; let mut digits = d.as_bstr(); let mut l = digits.len(); + if l > 3 { digits = digits.get(0..3).unwrap(); } + let mut result = 0; + if l > 0 { let digits = str::from_utf8(digits).unwrap(); // This can't panic, `digits` will only include digits. result = digits.parse().unwrap(); @@ -259,7 +265,8 @@ where result *= 10; l += 1; } + Ok(result) }) - .parse_next(i) + .parse_next(input) } diff --git a/crates/winnow-datetime/src/types.rs b/crates/winnow-datetime/src/types.rs index 47fa4cb..56a98af 100644 --- a/crates/winnow-datetime/src/types.rs +++ b/crates/winnow-datetime/src/types.rs @@ -104,7 +104,7 @@ pub struct Time { } impl Time { - /// Change this time's offset offset. + /// Change this time's offset. /// /// # Arguments /// diff --git a/crates/winnow-datetime/tests/lib.rs b/crates/winnow-datetime/tests/lib.rs deleted file mode 100644 index 8b13789..0000000 --- a/crates/winnow-datetime/tests/lib.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/winnow-iso8601/CHANGELOG.md b/crates/winnow-iso8601/CHANGELOG.md index 92029a5..c9afe4e 100644 --- a/crates/winnow-iso8601/CHANGELOG.md +++ b/crates/winnow-iso8601/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 0.5.0 - 2024-05-04 +* Changed parser signatures to meet winnow 0.7 standards + ## 0.4.0 - 2014-12-29 * Moved Date, Time, and DateTime, Timezone to the `winnow-datetime` so that upcoming format crates can re-use them. This crate will no longer export these directly, since this caused several type diff --git a/crates/winnow-iso8601/Cargo.toml b/crates/winnow-iso8601/Cargo.toml index 80947d0..73c2dfc 100644 --- a/crates/winnow-iso8601/Cargo.toml +++ b/crates/winnow-iso8601/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "winnow_iso8601" -version = "0.4.0" +version = "0.5.0" description = "Parsing ISO8601 dates using winnow" keywords = [ "iso8601", "date-time", "parser", "winnow" ] categories = [ "parser-implementations", "date-and-time" ] @@ -13,15 +13,15 @@ readme = "README.md" edition = "2021" [dependencies] -winnow = { version = "0.6.20"} -winnow_datetime = { path = "../winnow-datetime", features = ["serde"], version = "0.1.0" } +winnow = { version = "0.7" } +winnow_datetime = { path = "../winnow-datetime", features = ["serde"], version = "0.2.0" } chrono = { version = "0.4", default-features = false, optional = true } time = { version = "0.3.37", default-features = false, optional = true } num-traits = { version = "0.2", optional = true } serde = { version = "1.0.217", features = ["derive"], optional = true } [dev-dependencies ] -winnow_datetime_assert = { path = "../winnow-datetime-assert", version = "0.1.0" } +winnow_datetime_assert = { path = "../winnow-datetime-assert", version = "0.2.0" } libtest-mimic = "0.8.1" [[test]] diff --git a/crates/winnow-iso8601/src/date.rs b/crates/winnow-iso8601/src/date.rs index 1efa658..41d75b5 100644 --- a/crates/winnow-iso8601/src/date.rs +++ b/crates/winnow-iso8601/src/date.rs @@ -1,13 +1,10 @@ -use alloc::string::String; -use std::str; use winnow::combinator::separated_pair; use winnow::combinator::terminated; use winnow::combinator::{alt, eof, opt, preceded, trace}; -use winnow::error::ContextError; -use winnow::error::ErrMode; -use winnow::stream::{AsBStr, AsChar, Compare, Stream as InputStream, StreamIsPartial}; +use winnow::error::{InputError, ParserError}; +use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial}; use winnow::token::literal; -use winnow::{seq, PResult, Parser}; +use winnow::{seq, Parser, Result}; use winnow_datetime::parser::date_day; use winnow_datetime::parser::digit_1; use winnow_datetime::parser::{date_month, digit_4}; @@ -22,12 +19,8 @@ use winnow_datetime::{date_ymd_seq, date_ywd_seq}; /// ```rust /// let date = winnow_iso8601::parse_date("2015-11-02").unwrap(); /// ``` -pub fn parse_date(mut i: &str) -> Result { - if let Ok(parsed) = terminated(date, eof).parse_next(&mut i) { - Ok(parsed) - } else { - Err(format!("Failed to parse date: {}", i)) - } +pub fn parse_date(mut i: &str) -> Result> { + terminated(date, eof).parse_next(&mut i) } /// Parses a date @@ -38,60 +31,64 @@ pub fn parse_date(mut i: &str) -> Result { /// * `2015-W45-01` or `2015W451` /// * `2015-306` or `2015306` /// -/// See [`date()`][`crate::date()`] for the supported formats. -pub fn date<'i, Input>(i: &mut Input) -> PResult +/// See [`date()`][`mod@crate::date`] for the supported formats. +pub fn date<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("date", move |input: &mut Input| { alt((date_ywd, date_ymd_numeric, date_yddd, date_ymd)).parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// Date separator - -pub fn date_sep<'i, Input>(i: &mut Input) -> PResult +pub fn date_sep<'a, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'a str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("date_sep", move |input: &mut Input| { literal("-").parse_next(input).map(|_| '-') }) - .parse_next(i) + .parse_next(input) } /// Parses 2 digit week of the year within range 01-52 // WW -fn date_week<'i, Input>(i: &mut Input) -> PResult +fn date_week<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("date_week", move |input: &mut Input| { let _ = preceded(opt(literal("-")), literal("W")).parse_next(input)?; // [-]Ww week_of_year(input) }) - .parse_next(i) + .parse_next(input) } /// Parses 2 digit week of the year within range 01-52 // WW -pub(crate) fn week_of_year<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn week_of_year<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { - trace("date_week", move |input: &mut Input| { + trace("week_of_year", move |input: &mut Input| { take_digits_in_range(input, 2, 1..=52) }) - .parse_next(i) + .parse_next(input) } /// Verifies an opt value and calls a verification function on Some @@ -110,26 +107,28 @@ fn verify_day_of_week(day: u32) -> bool { } /// Parses 2 digit week of the year within range 01-7 -pub(crate) fn day_of_week<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn day_of_week<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("day_of_week", move |input: &mut Input| { take_digits_in_range(input, 1, 1..=7) }) - .parse_next(i) + .parse_next(input) } /// Parses 2 digit week of the year within range 01-52 /// Parses a date string as ISO 8601 week date. // YYYY-"W"WW-D -pub(crate) fn date_ywd<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn date_ywd<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("", move |input: &mut Input| { date_ywd_seq!(Date::Week { @@ -141,39 +140,41 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } // [-]D - unverified /// Parses a year with +/- and will eventually support 6 digit year // [+/-]YYYY -pub(crate) fn date_year<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn date_year<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("date_year", move |input: &mut Input| { // The sign is optional, but defaults to `+` - let sign = opt(sign).parse_next(input)?.unwrap_or(1); + let sign = opt(sign).parse_next(input)?.unwrap_or(1i32); - let year = digit_4(input)? as i32; + let year = digit_4(input)?; if year >= 100 && year < 10000 { - Ok(sign * year) + Ok(sign * year as i32) } else { - Err(ErrMode::Backtrack(ContextError::new())) + Err(ParserError::from_input(input)) } }) - .parse_next(i) + .parse_next(input) } -// YYYY-MM-DD -pub(crate) fn date_ymd<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn date_ymd<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, + // YYYY-MM-DD { trace("date_ymd", move |input: &mut Input| { date_ymd_seq!(Date::YMD { @@ -183,43 +184,46 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } // ordinal DDD -pub(crate) fn date_day_of_year<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn date_day_of_year<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("date_day_of_year", move |input: &mut Input| { take_digits_in_range(input, 3, 1..=366) }) - .parse_next(i) + .parse_next(input) } // YYYY-DDD -pub(crate) fn date_yddd<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn date_yddd<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("date_yddd", move |input: &mut Input| { separated_pair(date_year, opt(literal("-")), date_day_of_year) .map(|(year, day)| Date::Ordinal { year, day }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// Parses a date string specificed as YYYYMMDD -pub(crate) fn date_ymd_numeric<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn date_ymd_numeric<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("date_ymd_numeric", move |input: &mut Input| { seq!(Date::YMD { @@ -229,93 +233,98 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } #[cfg(test)] mod parsers { use crate::date::{date, date_yddd, date_year, date_ywd, day_of_week}; - use winnow::stream::AsBStr; + use winnow::error::InputError; + use winnow::Parser; use winnow_datetime::parser::{date_day, date_month}; - use winnow_datetime::Stream; + use winnow_datetime::PartialInput; #[test] fn test_date_year() { - assert_eq!(date_year(&mut "2015".as_bstr()).unwrap(), 2015); - assert_eq!(date_year(&mut "+2015".as_bstr()).unwrap(), 2015); - assert!(date_year(&mut "-333".as_bstr()).is_err()); - assert_eq!(date_year(&mut "-0333".as_bstr()).unwrap(), -333); - assert_eq!(date_year(&mut "2015-".as_bstr()).unwrap(), 2015); - assert!(date_year(&mut Stream::new(b"abcd")).is_err()); - assert!(date_year(&mut Stream::new(b"2a03")).is_err()); + assert_eq!(date_year::<_, InputError<_>>(&mut "2015").unwrap(), 2015); + assert_eq!(date_year::<_, InputError<_>>(&mut "+2015").unwrap(), 2015); + assert!(date_year::<_, InputError<_>>(&mut "-333").is_err()); + assert_eq!(date_year::<_, InputError<_>>(&mut "-0333").unwrap(), -333); + assert_eq!(date_year::<_, InputError<_>>(&mut "2015-").unwrap(), 2015); + assert!(date_year::<_, InputError<_>>(&mut PartialInput::new(b"abcd")).is_err()); + assert!(date_year::<_, InputError<_>>(&mut PartialInput::new(b"2a03")).is_err()); } #[test] fn test_date_month() { - assert_eq!(date_month(&mut "01".as_bstr()).unwrap(), 1); - assert_eq!(date_month(&mut "06".as_bstr()).unwrap(), 6); - assert_eq!(date_month(&mut "12".as_bstr()).unwrap(), 12); - assert_eq!(date_month(&mut "12-".as_bstr()).unwrap(), 12); + assert_eq!(date_month::<_, InputError<_>>(&mut "01").unwrap(), 1); + assert_eq!(date_month::<_, InputError<_>>(&mut "06").unwrap(), 6); + assert_eq!(date_month::<_, InputError<_>>(&mut "12").unwrap(), 12); + assert_eq!(date_month::<_, InputError<_>>(&mut "12-").unwrap(), 12); - assert!(date_month(&mut Stream::new(b"13\n")).is_err()); - assert!(date_month(&mut Stream::new(b"00\n")).is_err()); + assert!(date_month::<_, InputError<_>>(&mut PartialInput::new(b"13\n")).is_err()); + assert!(date_month::<_, InputError<_>>(&mut PartialInput::new(b"00\n")).is_err()); } #[test] fn test_date_day() { - assert_eq!(date_day(&mut "01".as_bstr()).unwrap(), 1); - assert_eq!(date_day(&mut "12".as_bstr()).unwrap(), 12); - assert_eq!(date_day(&mut "20".as_bstr()).unwrap(), 20); - assert_eq!(date_day(&mut "28".as_bstr()).unwrap(), 28); - assert_eq!(date_day(&mut "30".as_bstr()).unwrap(), 30); - assert_eq!(date_day(&mut "31".as_bstr()).unwrap(), 31); - assert_eq!(date_day(&mut "31-".as_bstr()).unwrap(), 31); + assert_eq!(date_day::<_, InputError<_>>(&mut "01").unwrap(), 1); + assert_eq!(date_day::<_, InputError<_>>(&mut "12").unwrap(), 12); + assert_eq!(date_day::<_, InputError<_>>(&mut "20").unwrap(), 20); + assert_eq!(date_day::<_, InputError<_>>(&mut "28").unwrap(), 28); + assert_eq!(date_day::<_, InputError<_>>(&mut "30").unwrap(), 30); + assert_eq!(date_day::<_, InputError<_>>(&mut "31").unwrap(), 31); + assert_eq!(date_day::<_, InputError<_>>(&mut "31-").unwrap(), 31); - assert!(date_day(&mut Stream::new(b"00")).is_err()); - assert!(date_day(&mut Stream::new(b"32")).is_err()); + assert!(date_day::<_, InputError<_>>(&mut PartialInput::new(b"00")).is_err()); + assert!(date_day::<_, InputError<_>>(&mut PartialInput::new(b"32")).is_err()); } #[test] fn test_date() { - assert!(date(&mut Stream::new(b"201")).is_err()); - assert!(date(&mut Stream::new(b"2015p00p00")).is_err()); - assert!(date(&mut Stream::new(b"pppp")).is_err()); + assert!(date::<_, InputError<_>>(&mut PartialInput::new(b"201")).is_err()); + assert!(date::<_, InputError<_>>(&mut PartialInput::new(b"2015p00p00")).is_err()); + assert!(date::<_, InputError<_>>(&mut PartialInput::new(b"pppp")).is_err()); } #[test] fn test_date_iso_week_date() { - assert!(date_ywd - .parse_next(&mut Stream::new(b"2015-W06-8")) + assert!(date_ywd::<_, InputError<_>> + .parse_next(&mut PartialInput::new(b"2015-W06-8")) + .is_err()); + assert!(date_ywd::<_, InputError<_>> + .parse_next(&mut PartialInput::new(b"2015-W068")) + .is_err()); + assert!(date_ywd::<_, InputError<_>> + .parse_next(&mut PartialInput::new(b"2015-W06-0")) .is_err()); - assert!(date_ywd.parse_next(&mut Stream::new(b"2015-W068")).is_err()); - assert!(date_ywd - .parse_next(&mut Stream::new(b"2015-W06-0")) + assert!(date_ywd::<_, InputError<_>> + .parse_next(&mut PartialInput::new(b"2015-W00-2")) .is_err()); - assert!(date_ywd - .parse_next(&mut Stream::new(b"2015-W00-2")) + assert!(date_ywd::<_, InputError<_>> + .parse_next(&mut PartialInput::new(b"2015-W54-2")) .is_err()); - assert!(date_ywd - .parse_next(&mut Stream::new(b"2015-W54-2")) + assert!(date_ywd::<_, InputError<_>> + .parse_next(&mut PartialInput::new(b"2015-W542")) .is_err()); - assert!(date_ywd.parse_next(&mut Stream::new(b"2015-W542")).is_err()); } #[test] fn test_date_ordinal_date() { // not valid here either - assert!(date_yddd(&mut Stream::new(b"2015-400")).is_err()); + assert!(date_yddd::<_, InputError<_>>(&mut PartialInput::new(b"2015-400")).is_err()); } #[test] fn test_day_of_week() { - assert_eq!(day_of_week(&mut "1".as_bstr()).unwrap(), 1); - assert_eq!(day_of_week(&mut "7".as_bstr()).unwrap(), 7); - assert!(day_of_week(&mut "8".as_bstr()).is_err()); // Invalid day + assert_eq!(day_of_week::<_, InputError<_>>(&mut "1").unwrap(), 1); + assert_eq!(day_of_week::<_, InputError<_>>(&mut "7").unwrap(), 7); + assert!(day_of_week::<_, InputError<_>>(&mut "8").is_err()); // Invalid day } #[test] fn disallows_notallowed() { - assert!(date(&mut Stream::new(b"0000-20-40")).is_err()); + assert!(date::<_, InputError<_>>(&mut PartialInput::new(b"0000-20-40")).is_err()); } } diff --git a/crates/winnow-iso8601/src/datetime.rs b/crates/winnow-iso8601/src/datetime.rs index 860cdd6..3a29b37 100644 --- a/crates/winnow-iso8601/src/datetime.rs +++ b/crates/winnow-iso8601/src/datetime.rs @@ -1,11 +1,11 @@ use crate::date::date; use crate::time::base_time; use ::winnow::Parser; -use alloc::string::String; -use winnow::combinator::{separated_pair, trace}; -use winnow::stream::{AsBStr, AsChar, Compare, Stream as InputStream, StreamIsPartial}; +use winnow::combinator::{eof, separated_pair, terminated, trace}; +use winnow::error::{InputError, ParserError}; +use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial}; use winnow::token::literal; -use winnow::PResult; +use winnow::Result; use winnow_datetime::DateTime; /// Parses a datetime string. @@ -15,12 +15,8 @@ use winnow_datetime::DateTime; /// ```rust /// let dt = winnow_iso8601::parse_datetime("2015-11-03T21:56").unwrap(); /// ``` -pub fn parse_datetime(mut i: &str) -> Result { - if let Ok(parsed) = datetime(&mut i) { - Ok(parsed) - } else { - Err(format!("Failed to parse datetime: {}", i)) - } +pub fn parse_datetime(mut i: &str) -> Result> { + terminated(datetime, eof).parse_next(&mut i) } /// Parses a datetime string. @@ -28,54 +24,56 @@ pub fn parse_datetime(mut i: &str) -> Result { /// A datetime string is a combination of the valid formats for the date and time, /// separated by a literal `T`. // Full ISO8601 datetime -pub fn datetime<'i, Input>(i: &mut Input) -> PResult +pub fn datetime<'i, Input, Error>(input: &mut Input) -> std::result::Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("datetime", move |input: &mut Input| { separated_pair(date, literal("T"), base_time) .map(|(d, t)| DateTime { date: d, time: t }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } #[cfg(test)] mod parsers { use crate::datetime::datetime; - use winnow_datetime::Stream; + use winnow::error::InputError; + use winnow_datetime::PartialInput; #[test] fn format_equivalence() { assert_eq!( - datetime(&mut Stream::new(b"2001-02-03T04:05:06+07:00")), - datetime(&mut Stream::new(b"20010203T040506+0700")) + datetime::<_, InputError<_>>(&mut "2001-02-03T04:05:06+07:00"), + datetime::<_, InputError<_>>(&mut "20010203T040506+0700") ); assert_eq!( - datetime(&mut Stream::new(b"2001-02-03T04:05:06+07:00")), - datetime(&mut Stream::new(b"20010203T04:05:06+0700")) + datetime::<_, InputError<_>>(&mut PartialInput::new(b"2001-02-03T04:05:06+07:00")), + datetime::<_, InputError<_>>(&mut PartialInput::new(b"20010203T04:05:06+0700")) ); assert_eq!( - datetime(&mut Stream::new(b"2001-02-03T04:05:00+07:00")), - datetime(&mut Stream::new(b"20010203T0405+0700")) + datetime::<_, InputError<_>>(&mut PartialInput::new(b"2001-02-03T04:05:00+07:00")), + datetime::<_, InputError<_>>(&mut PartialInput::new(b"20010203T0405+0700")) ); assert_eq!( - datetime(&mut Stream::new(b"20010203T0405+0700")), - datetime(&mut Stream::new(b"2001-02-03T0405+0700")) + datetime::<_, InputError<_>>(&mut PartialInput::new(b"20010203T0405+0700")), + datetime::<_, InputError<_>>(&mut PartialInput::new(b"2001-02-03T0405+0700")) ); assert_eq!( - datetime(&mut Stream::new(b"20010203T040506+0700")), - datetime(&mut Stream::new(b"2001-02-03T040506+0700")) + datetime::<_, InputError<_>>(&mut PartialInput::new(b"20010203T040506+0700")), + datetime::<_, InputError<_>>(&mut PartialInput::new(b"2001-02-03T040506+0700")) ); assert_eq!( - datetime(&mut Stream::new(b"20010203T040506+0000")), - datetime(&mut Stream::new(b"20010203T040506Z")) + datetime::<_, InputError<_>>(&mut PartialInput::new(b"20010203T040506+0000")), + datetime::<_, InputError<_>>(&mut PartialInput::new(b"20010203T040506Z")) ); assert_eq!( - datetime(&mut Stream::new(b"2015W056T04:05:06+07:00")), - datetime(&mut Stream::new(b"2015-W05-6T04:05:06+07:00")) + datetime::<_, InputError<_>>(&mut PartialInput::new(b"2015W056T04:05:06+07:00")), + datetime::<_, InputError<_>>(&mut PartialInput::new(b"2015-W05-6T04:05:06+07:00")) ); } @@ -84,13 +82,16 @@ mod parsers { let test_datetimes = vec!["ppp", "dumd-di-duTmd:iu:m"]; for iso_string in test_datetimes { - let res = datetime(&mut Stream::new(iso_string.as_bytes())); + let res = datetime::<_, InputError<_>>(&mut PartialInput::new(iso_string.as_bytes())); assert!(res.is_err()); } } #[test] fn disallows_notallowed() { - assert!(datetime(&mut Stream::new(b"2001-w05-6t04:05:06.123z")).is_err()); + assert!( + datetime::<_, InputError<_>>(&mut PartialInput::new(b"2001-w05-6t04:05:06.123z")) + .is_err() + ); } } diff --git a/crates/winnow-iso8601/src/duration.rs b/crates/winnow-iso8601/src/duration.rs index f80a0f1..6a1e2fd 100644 --- a/crates/winnow-iso8601/src/duration.rs +++ b/crates/winnow-iso8601/src/duration.rs @@ -1,9 +1,9 @@ -use alloc::string::String; use core::str; -use winnow::combinator::{opt, preceded, trace}; -use winnow::stream::{AsBStr, AsChar, Compare, Stream as InputStream, StreamIsPartial}; +use winnow::combinator::{eof, opt, preceded, terminated, trace}; +use winnow::error::{InputError, ParserError}; +use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial}; use winnow::token::{literal, one_of}; -use winnow::{seq, PResult, Parser}; +use winnow::{seq, Parser, Result}; use winnow_datetime::duration_part_seq; use winnow_datetime::parser::take_digits; use winnow_datetime::types::{Duration, DurationPart}; @@ -17,11 +17,8 @@ use winnow_datetime::types::{Duration, DurationPart}; /// let duration = winnow_iso8601::parse_duration("P1Y2M3DT4H5M6S").unwrap(); /// let duration = winnow_iso8601::parse_duration("P1W").unwrap(); /// ``` -pub fn parse_duration(mut i: &str) -> Result { - match duration(&mut i) { - Ok(p) => Ok(p), - Err(e) => Err(format!("Failed to parse duration {}: {}", i, e)), - } +pub fn parse_duration(mut i: &str) -> Result> { + terminated(duration, eof).parse_next(&mut i) } /// Parses a duration string with the format P%dY%dM%dDT%dH%dM%dS @@ -30,7 +27,7 @@ pub fn parse_duration(mut i: &str) -> Result { /// /// * Fully-specified duration: `P1Y2M3DT4H5M6S` /// * Duration in weekly intervals: `P1W` -/// * Fully-specified duration in [`DateTime`](`crate::DateTime`) format: `P` +/// * Fully-specified duration in [`DateTime`](`winnow_datetime::DateTime`) format: `P` /// /// Both fully-specified formats get parsed into the YMDHMS Duration variant. /// The weekly interval format gets parsed into the Weeks Duration variant. @@ -47,11 +44,12 @@ pub fn parse_duration(mut i: &str) -> Result { /// * Hour 0 - 24 /// * Minute 0 - 60 /// * Second 0 - 60 -pub fn duration<'i, Input>(i: &mut Input) -> PResult +pub fn duration<'i, Input, Error>(input: &mut Input) -> std::result::Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("duration", move |input: &mut Input| { seq!(( @@ -86,15 +84,18 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// dur-year = 1*DIGIT "Y" [dur-month] -pub(crate) fn duration_part_year<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn duration_part_year<'i, Input, Error>( + input: &mut Input, +) -> std::result::Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("duration_part_year", move |input: &mut Input| { duration_part_seq!({ @@ -105,15 +106,18 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// dur-month = 1*DIGIT "M" [dur-day] -pub(crate) fn duration_part_month<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn duration_part_month<'i, Input, Error>( + input: &mut Input, +) -> std::result::Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("duration_part_month", move |input: &mut Input| { duration_part_seq!({ @@ -124,15 +128,18 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// dur-week = 1*DIGIT "W" -pub(crate) fn duration_part_week<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn duration_part_week<'i, Input, Error>( + input: &mut Input, +) -> std::result::Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("duration_part_week", move |input: &mut Input| { duration_part_seq!({ @@ -143,15 +150,18 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } // dur-day = 1*DIGIT "D" -pub(crate) fn duration_part_day<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn duration_part_day<'i, Input, Error>( + input: &mut Input, +) -> std::result::Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("duration_part_day", move |input: &mut Input| { duration_part_seq!({ @@ -162,16 +172,19 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// dur-hour = 1*DIGIT "H" [dur-minute] /// dur-time = "T" (dur-hour / dur-minute / dur-second) -pub(crate) fn duration_part_hour<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn duration_part_hour<'i, Input, Error>( + input: &mut Input, +) -> std::result::Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("duration_part_hour", move |input: &mut Input| { duration_part_seq!({ @@ -182,15 +195,18 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// dur-minute = 1*DIGIT "M" [dur-second] -pub(crate) fn duration_part_minute<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn duration_part_minute<'i, Input, Error>( + input: &mut Input, +) -> std::result::Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("duration_part_minute", move |input: &mut Input| { duration_part_seq!({ @@ -201,15 +217,18 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// dur-second = 1*DIGIT "S" -pub(crate) fn duration_part_second<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn duration_part_second<'i, Input, Error>( + input: &mut Input, +) -> std::result::Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("duration_part_second", move |input: &mut Input| { duration_part_seq!({ @@ -220,39 +239,47 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// Parses time portion of a duration -pub fn duration_time<'i, Input>( - i: &mut Input, -) -> PResult<( - Option, - Option, - Option, -)> +pub fn duration_time<'i, Input, Error>( + input: &mut Input, +) -> std::result::Result< + ( + Option, + Option, + Option, + ), + Error, +> where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("duration_time", move |input: &mut Input| { preceded(opt(literal("T")), duration_base_time).parse_next(input) }) - .parse_next(i) + .parse_next(input) } -pub(crate) fn duration_base_time<'i, Input>( - i: &mut Input, -) -> PResult<( - Option, - Option, - Option, -)> +pub fn duration_base_time<'i, Input, Error>( + input: &mut Input, +) -> std::result::Result< + ( + Option, + Option, + Option, + ), + Error, +> where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("duration_base_time", move |input: &mut Input| { seq!(( @@ -264,228 +291,229 @@ where .map(|(h, m, s)| (h, m, s)) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } #[cfg(test)] mod parsers { use crate::duration::*; + use winnow::error::InputError; use winnow_datetime::types::DurationPart; - use winnow_datetime::Stream; + use winnow_datetime::PartialInput; #[test] fn test_duration_year() { assert_eq!( - duration_part_year(&mut "2019Y".as_bstr()).unwrap(), + duration_part_year::<_, InputError<_>>(&mut "2019Y").unwrap(), (DurationPart { whole: 2019, frac: None }) ); assert_eq!( - duration_part_year(&mut "0Y".as_bstr()).unwrap(), + duration_part_year::<_, InputError<_>>(&mut "0Y").unwrap(), (DurationPart { whole: 0, frac: None }) ); assert_eq!( - duration_part_year(&mut "10000Y".as_bstr()).unwrap(), + duration_part_year::<_, InputError<_>>(&mut "10000Y").unwrap(), (DurationPart { whole: 10000, frac: None }) ); - assert!(duration_part_year(&mut Stream::new(b"abcd")).is_err()); - assert!(duration_part_year(&mut Stream::new(b"-1")).is_err()); + assert!(duration_part_year::<_, InputError<_>>(&mut PartialInput::new(b"abcd")).is_err()); + assert!(duration_part_year::<_, InputError<_>>(&mut PartialInput::new(b"-1")).is_err()); } #[test] fn test_duration_month() { assert_eq!( - duration_part_month(&mut "6M".as_bstr()).unwrap(), + duration_part_month::<_, InputError<_>>(&mut "6M").unwrap(), (DurationPart { whole: 6, frac: None }) ); assert_eq!( - duration_part_month(&mut "0M".as_bstr()).unwrap(), + duration_part_month::<_, InputError<_>>(&mut "0M").unwrap(), (DurationPart { whole: 0, frac: None }) ); assert_eq!( - duration_part_month(&mut "12M".as_bstr()).unwrap(), + duration_part_month::<_, InputError<_>>(&mut "12M").unwrap(), (DurationPart { whole: 12, frac: None }) ); - assert!(duration_part_month(&mut Stream::new(b"ab")).is_err()); - assert!(duration_part_month(&mut Stream::new(b"-1")).is_err()); - assert!(duration_part_month(&mut Stream::new(b"13")).is_err()); + assert!(duration_part_month::<_, InputError<_>>(&mut PartialInput::new(b"ab")).is_err()); + assert!(duration_part_month::<_, InputError<_>>(&mut PartialInput::new(b"-1")).is_err()); + assert!(duration_part_month::<_, InputError<_>>(&mut PartialInput::new(b"13")).is_err()); } #[test] fn test_duration_week() { assert_eq!( - duration_part_week(&mut "26W".as_bstr()).unwrap(), + duration_part_week::<_, InputError<_>>(&mut "26W").unwrap(), DurationPart { whole: 26, frac: None } ); assert_eq!( - duration_part_week(&mut "0W".as_bstr()).unwrap(), + duration_part_week::<_, InputError<_>>(&mut "0W").unwrap(), DurationPart { whole: 0, frac: None } ); assert_eq!( - duration_part_week(&mut "52W".as_bstr()).unwrap(), + duration_part_week::<_, InputError<_>>(&mut "52W").unwrap(), DurationPart { whole: 52, frac: None } ); - assert!(duration_part_week(&mut Stream::new(b"ab")).is_err()); - assert!(duration_part_week(&mut Stream::new(b"-1")).is_err()); - assert!(duration_part_week(&mut Stream::new(b"53")).is_err()); + assert!(duration_part_week::<_, InputError<_>>(&mut PartialInput::new(b"ab")).is_err()); + assert!(duration_part_week::<_, InputError<_>>(&mut PartialInput::new(b"-1")).is_err()); + assert!(duration_part_week::<_, InputError<_>>(&mut PartialInput::new(b"53")).is_err()); } #[test] fn test_duration_day() { assert_eq!( - duration_part_day(&mut "16D".as_bstr()).unwrap(), + duration_part_day::<_, InputError<_>>(&mut "16D").unwrap(), DurationPart { whole: 16, frac: None } ); assert_eq!( - duration_part_day(&mut "0D".as_bstr()).unwrap(), + duration_part_day::<_, InputError<_>>(&mut "0D").unwrap(), DurationPart { whole: 0, frac: None } ); assert_eq!( - duration_part_day(&mut "31D".as_bstr()).unwrap(), + duration_part_day::<_, InputError<_>>(&mut "31D").unwrap(), DurationPart { whole: 31, frac: None } ); - assert!(duration_part_day(&mut Stream::new(b"ab")).is_err()); - assert!(duration_part_day(&mut Stream::new(b"-1")).is_err()); - assert!(duration_part_day(&mut Stream::new(b"32")).is_err()); + assert!(duration_part_day::<_, InputError<_>>(&mut PartialInput::new(b"ab")).is_err()); + assert!(duration_part_day::<_, InputError<_>>(&mut PartialInput::new(b"-1")).is_err()); + assert!(duration_part_day::<_, InputError<_>>(&mut PartialInput::new(b"32")).is_err()); } #[test] fn test_duration_hour() { assert_eq!( - duration_part_hour(&mut "12H".as_bstr()).unwrap(), + duration_part_hour::<_, InputError<_>>(&mut "12H").unwrap(), DurationPart { whole: 12, frac: None } ); assert_eq!( - duration_part_hour(&mut "0H".as_bstr()).unwrap(), + duration_part_hour::<_, InputError<_>>(&mut "0H").unwrap(), DurationPart { whole: 0, frac: None } ); assert_eq!( - duration_part_hour(&mut "24H".as_bstr()).unwrap(), + duration_part_hour::<_, InputError<_>>(&mut "24H").unwrap(), DurationPart { whole: 24, frac: None } ); - assert!(duration_part_hour(&mut Stream::new(b"ab")).is_err()); - assert!(duration_part_hour(&mut Stream::new(b"-1")).is_err()); - assert!(duration_part_hour(&mut Stream::new(b"25")).is_err()); + assert!(duration_part_hour::<_, InputError<_>>(&mut PartialInput::new(b"ab")).is_err()); + assert!(duration_part_hour::<_, InputError<_>>(&mut PartialInput::new(b"-1")).is_err()); + assert!(duration_part_hour::<_, InputError<_>>(&mut PartialInput::new(b"25")).is_err()); } #[test] fn test_duration_minute() { assert_eq!( - duration_part_minute(&mut "30M".as_bstr()).unwrap(), + duration_part_minute::<_, InputError<_>>(&mut "30M").unwrap(), DurationPart { whole: 30, frac: None } ); assert_eq!( - duration_part_minute(&mut "0M".as_bstr()).unwrap(), + duration_part_minute::<_, InputError<_>>(&mut "0M").unwrap(), DurationPart { whole: 0, frac: None } ); assert_eq!( - duration_part_minute(&mut "60M".as_bstr()).unwrap(), + duration_part_minute::<_, InputError<_>>(&mut "60M").unwrap(), DurationPart { whole: 60, frac: None } ); - assert!(duration_part_minute(&mut Stream::new(b"ab")).is_err()); - assert!(duration_part_minute(&mut Stream::new(b"-1")).is_err()); - assert!(duration_part_minute(&mut Stream::new(b"61")).is_err()); + assert!(duration_part_minute::<_, InputError<_>>(&mut PartialInput::new(b"ab")).is_err()); + assert!(duration_part_minute::<_, InputError<_>>(&mut PartialInput::new(b"-1")).is_err()); + assert!(duration_part_minute::<_, InputError<_>>(&mut PartialInput::new(b"61")).is_err()); } #[test] fn test_duration_second_and_millisecond1() { assert_eq!( - duration_part_second(&mut "30S".as_bstr()).unwrap(), + duration_part_second::<_, InputError<_>>(&mut "30S").unwrap(), DurationPart { whole: 30, frac: None } ); assert_eq!( - duration_part_second(&mut "0S".as_bstr()).unwrap(), + duration_part_second::<_, InputError<_>>(&mut "0S").unwrap(), DurationPart { whole: 0, frac: None } ); assert_eq!( - duration_part_second(&mut "60S".as_bstr()).unwrap(), + duration_part_second::<_, InputError<_>>(&mut "60S").unwrap(), DurationPart { whole: 60, frac: None } ); assert_eq!( - duration_part_second(&mut "1,23S".as_bstr()).unwrap(), + duration_part_second::<_, InputError<_>>(&mut "1,23S").unwrap(), DurationPart { whole: 1, frac: Some(0.23) } ); assert_eq!( - duration_part_second(&mut "2.34S".as_bstr()).unwrap(), + duration_part_second::<_, InputError<_>>(&mut "2.34S").unwrap(), DurationPart { whole: 2, frac: Some(0.34) } ); - assert!(duration_part_second(&mut Stream::new(b"abS")).is_err()); - assert!(duration_part_second(&mut Stream::new(b"-1S")).is_err()); + assert!(duration_part_second::<_, InputError<_>>(&mut PartialInput::new(b"abS")).is_err()); + assert!(duration_part_second::<_, InputError<_>>(&mut PartialInput::new(b"-1S")).is_err()); } #[test] fn test_duration_time() { assert_eq!( - duration_time(&mut "T1H2M3S".as_bstr()).unwrap(), + duration_time::<_, InputError<_>>(&mut "T1H2M3S").unwrap(), ( Some(DurationPart { whole: 1, @@ -502,7 +530,7 @@ mod parsers { ) ); assert_eq!( - duration_time(&mut "T10H12M30S".as_bstr()).unwrap(), + duration_time::<_, InputError<_>>(&mut "T10H12M30S").unwrap(), ( Some(DurationPart { whole: 10, @@ -519,7 +547,7 @@ mod parsers { ) ); assert_eq!( - duration_time(&mut "T1H3S".as_bstr()).unwrap(), + duration_time::<_, InputError<_>>(&mut "T1H3S").unwrap(), ( Some(DurationPart { whole: 1, @@ -534,7 +562,7 @@ mod parsers { ); assert_eq!( - duration_time(&mut "T2M".as_bstr()).unwrap(), + duration_time::<_, InputError<_>>(&mut "T2M").unwrap(), ( None, Some(DurationPart { @@ -545,7 +573,7 @@ mod parsers { ) ); assert_eq!( - duration_time(&mut "T1H2M3,4S".as_bstr()).unwrap(), + duration_time::<_, InputError<_>>(&mut "T1H2M3,4S").unwrap(), ( Some(DurationPart { whole: 1, @@ -562,7 +590,7 @@ mod parsers { ) ); assert_eq!( - duration_time(&mut "T1H23.4S".as_bstr()).unwrap(), + duration_time::<_, InputError<_>>(&mut "T1H23.4S").unwrap(), ( Some(DurationPart { whole: 1, @@ -576,7 +604,7 @@ mod parsers { ) ); assert_eq!( - duration_time(&mut "T0,123S".as_bstr()).unwrap(), + duration_time::<_, InputError<_>>(&mut "T0,123S").unwrap(), ( None, None, @@ -587,7 +615,7 @@ mod parsers { ) ); assert_eq!( - duration_time(&mut "T0123S".as_bstr()).unwrap(), + duration_time::<_, InputError<_>>(&mut "T0123S").unwrap(), ( None, None, @@ -601,24 +629,26 @@ mod parsers { #[test] fn test_duration_ymdhms_error() { - assert!(duration(&mut Stream::new(b"")).is_err()); - assert!(duration(&mut Stream::new(b"P")).is_err()); // empty duration is not 0 seconds - assert!(duration(&mut Stream::new(b"1Y2M3DT4H5M6S")).is_err()); // missing P at start - assert!(duration(&mut Stream::new(b"T4H5M6S")).is_err()); // missing P, + assert!(duration::<_, InputError<_>>(&mut PartialInput::new(b"")).is_err()); + assert!(duration::<_, InputError<_>>(&mut PartialInput::new(b"P")).is_err()); // empty duration is not 0 seconds + assert!(duration::<_, InputError<_>>(&mut PartialInput::new(b"1Y2M3DT4H5M6S")).is_err()); // missing P at start + assert!(duration::<_, InputError<_>>(&mut PartialInput::new(b"T4H5M6S")).is_err()); + // missing P, } #[test] fn test_duration_weeks_error() { - assert!(duration(&mut Stream::new(b"")).is_err()); - assert!(duration(&mut Stream::new(b"P")).is_err()); // empty duration is not 0 seconds - assert!(duration(&mut Stream::new(b"P1")).is_err()); // missing W after number - assert!(duration(&mut Stream::new(b"PW")).is_err()); // missing number + assert!(duration::<_, InputError<_>>(&mut PartialInput::new(b"")).is_err()); + assert!(duration::<_, InputError<_>>(&mut PartialInput::new(b"P")).is_err()); // empty duration is not 0 seconds + assert!(duration::<_, InputError<_>>(&mut PartialInput::new(b"P1")).is_err()); // missing W after number + assert!(duration::<_, InputError<_>>(&mut PartialInput::new(b"PW")).is_err()); + // missing number } #[test] fn test_duration_second() { assert_eq!( - duration(&mut "PT30S".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "PT30S").unwrap(), Duration { years: 0, months: 0, @@ -631,7 +661,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "PT30.123S".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "PT30.123S").unwrap(), Duration { years: 0, months: 0, @@ -644,7 +674,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P2021Y11M16DT23H26M59.123S".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P2021Y11M16DT23H26M59.123S").unwrap(), Duration { years: 2021, months: 11, @@ -661,7 +691,7 @@ mod parsers { #[test] fn duration_roundtrip() { assert_eq!( - duration(&mut "P0W".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P0W").unwrap(), Duration { years: 0, months: 0, @@ -674,7 +704,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P2021Y11M16DT23H26M59S".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P2021Y11M16DT23H26M59S").unwrap(), Duration { years: 2021, months: 11, @@ -687,7 +717,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P2021Y11M16DT23H26M".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P2021Y11M16DT23H26M").unwrap(), Duration { years: 2021, months: 11, @@ -700,7 +730,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P2021Y11M16DT23H".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P2021Y11M16DT23H").unwrap(), Duration { years: 2021, months: 11, @@ -713,7 +743,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P2021Y11M16D".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P2021Y11M16D").unwrap(), Duration { years: 2021, months: 11, @@ -726,7 +756,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P2021Y11M16DT1S".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P2021Y11M16DT1S").unwrap(), Duration { years: 2021, months: 11, @@ -739,7 +769,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P2021Y11M16DT0.471S".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P2021Y11M16DT0.471S").unwrap(), Duration { years: 2021, months: 11, @@ -752,7 +782,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P2021Y11M".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P2021Y11M").unwrap(), Duration { years: 2021, months: 11, @@ -765,7 +795,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P11M".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P11M").unwrap(), Duration { years: 0, months: 11, @@ -778,7 +808,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P16D".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P16D").unwrap(), Duration { years: 0, months: 0, @@ -791,7 +821,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P0D".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P0D").unwrap(), Duration { years: 0, months: 0, @@ -808,7 +838,7 @@ mod parsers { #[test] fn duration_multi_digit_hour() { assert_eq!( - duration(&mut "PT12H".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "PT12H").unwrap(), Duration { years: 0, months: 0, @@ -821,7 +851,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "PT8760H".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "PT8760H").unwrap(), Duration { years: 0, months: 0, @@ -838,7 +868,7 @@ mod parsers { #[test] fn duration_multi_digit_minute() { assert_eq!( - duration(&mut "PT15M".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "PT15M").unwrap(), Duration { years: 0, months: 0, @@ -851,7 +881,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "PT600M".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "PT600M").unwrap(), Duration { years: 0, months: 0, @@ -868,7 +898,7 @@ mod parsers { #[test] fn duration_multi_digit_second() { assert_eq!( - duration(&mut "PT16S".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "PT16S").unwrap(), Duration { years: 0, months: 0, @@ -881,7 +911,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "PT900S".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "PT900S").unwrap(), Duration { years: 0, months: 0, @@ -898,7 +928,7 @@ mod parsers { #[test] fn duration_multi_digit_day() { assert_eq!( - duration(&mut "P365D".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P365D").unwrap(), Duration { years: 0, months: 0, @@ -911,7 +941,7 @@ mod parsers { } ); assert_eq!( - duration(&mut "P36500D".as_bstr()).unwrap(), + duration::<_, InputError<_>>(&mut "P36500D").unwrap(), Duration { years: 0, months: 0, diff --git a/crates/winnow-iso8601/src/fractional_duration.rs b/crates/winnow-iso8601/src/fractional_duration.rs index 2ed5dcc..e5acc20 100644 --- a/crates/winnow-iso8601/src/fractional_duration.rs +++ b/crates/winnow-iso8601/src/fractional_duration.rs @@ -1,38 +1,30 @@ use crate::duration::{ - duration, duration_base_time, duration_part_day, duration_part_month, duration_part_week, + duration_base_time, duration_part_day, duration_part_month, duration_part_week, duration_part_year, }; -use alloc::string::String; -use winnow::combinator::{opt, preceded, trace}; -use winnow::stream::{AsBStr, AsChar, Compare, Stream as InputStream, StreamIsPartial}; +use winnow::combinator::{eof, opt, preceded, terminated, trace}; +use winnow::error::{InputError, ParserError}; +use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial}; use winnow::token::literal; -use winnow::{seq, PResult, Parser}; -use winnow_datetime::types::Duration; +use winnow::{seq, Parser, Result}; use winnow_datetime::FractionalDuration; /// Parses a duration with the same formating rules but allows for decimal places. -pub fn parse_duration(mut i: &str) -> Result { - match duration(&mut i) { - Ok(p) => Ok(p), - Err(e) => Err(format!("Failed to parse duration {}: {}", i, e)), - } -} - /// let duration = winnow_iso8601::parse_fractional_duration("P1,5Y2M3DT4,5H5M6S").unwrap(); /// let duration = winnow_iso8601::parse_fractional_duration("P1,5W").unwrap(); -pub fn parse_fractional_duration(mut i: &str) -> Result { - match fractional_duration(&mut i) { - Ok(p) => Ok(p), - Err(e) => Err(format!("Failed to parse duration {}: {}", i, e)), - } +pub fn parse_fractional_duration(mut i: &str) -> Result> { + terminated(fractional_duration, eof).parse_next(&mut i) } /// Parses a duration string with the format P%dY%dM%dDT%dH%dM%dS -pub fn fractional_duration<'i, Input>(i: &mut Input) -> PResult +pub fn fractional_duration<'i, Input, Error>( + input: &mut Input, +) -> std::result::Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("fractional_duration", move |input: &mut Input| { seq!(( @@ -66,5 +58,5 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } diff --git a/crates/winnow-iso8601/src/interval.rs b/crates/winnow-iso8601/src/interval.rs index 5ea7cdc..55fc60d 100644 --- a/crates/winnow-iso8601/src/interval.rs +++ b/crates/winnow-iso8601/src/interval.rs @@ -1,13 +1,12 @@ use crate::duration::duration; use crate::partial_datetime::{partial_datetime, partial_end_datetime}; -use alloc::string::String; -use core::str; -use winnow::combinator::alt; use winnow::combinator::opt; use winnow::combinator::trace; -use winnow::stream::{AsBStr, AsChar, Compare, Stream as InputStream, StreamIsPartial}; +use winnow::combinator::{alt, eof, terminated}; +use winnow::error::{InputError, ParserError}; +use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial}; use winnow::token::literal; -use winnow::{seq, PResult, Parser}; +use winnow::{seq, Parser, Result}; use winnow_datetime::parser::take_digits; use winnow_datetime::types::{Interval, IntervalRange}; @@ -16,19 +15,17 @@ use winnow_datetime::types::{Interval, IntervalRange}; /// A string that optionally starts with `R` and contains a combination of partial date-times in the /// following permissible formats: /// -pub fn parse_interval(mut i: &str) -> Result { - match interval(&mut i) { - Ok(p) => Ok(p), - Err(e) => Err(format!("Failed to parse interval {}: {}", i, e)), - } +pub fn parse_interval(mut i: &str) -> Result> { + terminated(interval, eof).parse_next(&mut i) } /// Parses a interval string containing combinations of partial date-times and duration. -pub fn interval<'i, Input>(i: &mut Input) -> PResult +pub fn interval<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("interval", move |input: &mut Input| { seq!(Interval { @@ -42,40 +39,43 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } -fn interval_repetitions<'i, Input>(i: &mut Input) -> PResult> +pub fn interval_repetitions<'i, Input, Error>(input: &mut Input) -> Result, Error> where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("interval_repetitions", move |input: &mut Input| { seq!((literal("R"), opt(take_digits), literal("/"))) .map(|(_, r, _)| r) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } -fn interval_open<'i, Input>(i: &mut Input) -> PResult +fn interval_open<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("interval_open", move |input: &mut Input| { duration(input).map(|duration| IntervalRange::Open { duration }) }) - .parse_next(i) + .parse_next(input) } -fn interval_closed<'i, Input>(i: &mut Input) -> PResult +fn interval_closed<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("interval_closed", move |input: &mut Input| { let start = partial_datetime(input)?; @@ -84,14 +84,15 @@ where Ok(IntervalRange::Closed { start, end }) }) - .parse_next(i) + .parse_next(input) } -fn interval_closed_end<'i, Input>(i: &mut Input) -> PResult +fn interval_closed_end<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("interval_closed_end", move |input: &mut Input| { seq!(IntervalRange::ClosedEnd { @@ -101,14 +102,15 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } -fn interval_closed_start<'i, Input>(i: &mut Input) -> PResult +fn interval_closed_start<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("interval_closed_start", move |input: &mut Input| { seq!( IntervalRange::ClosedStart { @@ -118,21 +120,22 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } #[cfg(test)] mod parsers { use crate::interval::interval; use crate::partial_date::partial_end_date; - use winnow::stream::AsBStr; + use winnow::error::InputError; + use winnow_datetime::types::{IntervalRange, PartialDate, PartialDateTime, PartialTime}; use winnow_datetime::{Duration, Interval, Offset}; #[test] fn interval_closed() { assert_eq!( - interval(&mut "2015-06-25/2015-06-26".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "2015-06-25/2015-06-26").unwrap(), Interval { repetitions: None, range: IntervalRange::Closed { @@ -157,7 +160,7 @@ mod parsers { ); assert_eq!( - interval(&mut "2015-06-25 12:00:00Z/2015-06-26 12:00:00Z".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "2015-06-25 12:00:00Z/2015-06-26 12:00:00Z").unwrap(), Interval { repetitions: None, range: IntervalRange::Closed { @@ -204,7 +207,7 @@ mod parsers { fn interval_closed_partial_ymd_end_date() { // Partial end: 2024-12-22/12-23 assert_eq!( - interval(&mut "2024-12-22/12-23".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "2024-12-22/12-23").unwrap(), Interval { repetitions: None, range: IntervalRange::Closed { @@ -230,7 +233,7 @@ mod parsers { // Partial start: 2024-12-22/12-23 assert_eq!( - interval(&mut "2024-12-22/23".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "2024-12-22/23").unwrap(), Interval { repetitions: None, range: IntervalRange::Closed { @@ -259,7 +262,7 @@ mod parsers { fn interval_closed_partial_ywd_end_date() { // Partial end: 2024-12-22/12-23 assert_eq!( - interval(&mut "2024-W51-7/2024-W52-1".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "2024-W51-7/2024-W52-1").unwrap(), Interval { repetitions: None, range: IntervalRange::Closed { @@ -283,7 +286,7 @@ mod parsers { } ); assert_eq!( - interval(&mut "2024-W51-7/W52-1".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "2024-W51-7/W52-1").unwrap(), Interval { repetitions: None, range: IntervalRange::Closed { @@ -307,7 +310,7 @@ mod parsers { } ); assert_eq!( - interval(&mut "2024-W51-7/52-1".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "2024-W51-7/52-1").unwrap(), Interval { repetitions: None, range: IntervalRange::Closed { @@ -331,7 +334,7 @@ mod parsers { } ); assert_eq!( - interval(&mut "2024-W51-1/2".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "2024-W51-1/2").unwrap(), Interval { repetitions: None, range: IntervalRange::Closed { @@ -355,7 +358,7 @@ mod parsers { } ); assert_eq!( - interval(&mut "2024-W51-7/1".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "2024-W51-7/1").unwrap(), Interval { repetitions: None, range: IntervalRange::Closed { @@ -385,8 +388,8 @@ mod parsers { #[test] fn test_partial_end_date_ywd() { assert_eq!( - partial_end_date( - &mut "1".as_bstr(), + partial_end_date::<_, InputError<_>>( + &mut "1", &PartialDate::YWD { year: Some(2024), week: Some(51), @@ -405,7 +408,7 @@ mod parsers { #[test] fn interval_open() { assert_eq!( - interval(&mut "P1Y2M".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "P1Y2M").unwrap(), Interval { repetitions: None, range: IntervalRange::Open { @@ -427,7 +430,7 @@ mod parsers { #[test] fn interval_closed_start() { assert_eq!( - interval(&mut "2015-06-25/P1M".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "2015-06-25/P1M").unwrap(), Interval { repetitions: None, range: IntervalRange::ClosedStart { @@ -457,7 +460,7 @@ mod parsers { #[test] fn interval_closed_end() { assert_eq!( - interval(&mut "P1M/2015-06-25".as_bstr()).unwrap(), + interval::<_, InputError<_>>(&mut "P1M/2015-06-25").unwrap(), Interval { repetitions: None, range: IntervalRange::ClosedEnd { diff --git a/crates/winnow-iso8601/src/lib.rs b/crates/winnow-iso8601/src/lib.rs index 3b45adb..6901f9b 100644 --- a/crates/winnow-iso8601/src/lib.rs +++ b/crates/winnow-iso8601/src/lib.rs @@ -19,9 +19,6 @@ #[macro_use] extern crate std; -#[macro_use] -extern crate alloc; - mod clippy; /// date mod diff --git a/crates/winnow-iso8601/src/offset.rs b/crates/winnow-iso8601/src/offset.rs index ccf3b57..27f4afc 100644 --- a/crates/winnow-iso8601/src/offset.rs +++ b/crates/winnow-iso8601/src/offset.rs @@ -1,8 +1,8 @@ -use alloc::string::String; -use winnow::combinator::{alt, opt, preceded, trace}; -use winnow::stream::{AsBStr, AsChar, Compare, Stream as InputStream, StreamIsPartial}; +use winnow::combinator::{alt, eof, opt, preceded, terminated, trace}; +use winnow::error::{InputError, ParserError}; +use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial}; use winnow::token::literal; -use winnow::{seq, PResult, Parser}; +use winnow::{seq, Parser, Result}; use winnow_datetime::parser::{sign, time_hour, time_minute}; use winnow_datetime::Offset; @@ -13,12 +13,8 @@ use winnow_datetime::Offset; /// ```rust /// let dt = winnow_iso8601::parse_offset("Z").unwrap(); /// ``` -pub fn parse_offset(mut i: &str) -> Result, String> { - if let Ok(parsed) = offset(&mut i) { - Ok(parsed) - } else { - Err(format!("Failed to parse datetime: {}", i)) - } +pub fn parse_offset(mut i: &str) -> Result, InputError<&str>> { + terminated(offset, eof).parse_next(&mut i) } // (+...|-...) @@ -29,38 +25,41 @@ pub fn parse_offset(mut i: &str) -> Result, String> { /// This will accept (Z|+...|-...) as offsets /// // (Z|+...|-...) -pub fn offset<'i, Input>(i: &mut Input) -> PResult> +pub fn offset<'i, Input, Error>(input: &mut Input) -> Result, Error> where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("offset", move |input: &mut Input| { alt((offset_hour, offset_zulu)).parse_next(input) }) - .parse_next(i) + .parse_next(input) } // Z -fn offset_zulu<'i, Input>(i: &mut Input) -> PResult> +pub fn offset_zulu<'i, Input, Error>(input: &mut Input) -> Result, Error> where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("offset_zulu", move |input: &mut Input| { literal("Z") .map(|_| Some(Offset::default())) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } -fn offset_hour<'i, Input>(i: &mut Input) -> PResult> +pub fn offset_hour<'i, Input, Error>(input: &mut Input) -> Result, Error> where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("offset_hour", move |input: &mut Input| { seq!(( @@ -77,5 +76,5 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } diff --git a/crates/winnow-iso8601/src/partial_date.rs b/crates/winnow-iso8601/src/partial_date.rs index b1fb909..b6ae638 100644 --- a/crates/winnow-iso8601/src/partial_date.rs +++ b/crates/winnow-iso8601/src/partial_date.rs @@ -1,11 +1,10 @@ use crate::date::{date_day_of_year, date_year, day_of_week, week_of_year}; use core::str; use winnow::combinator::{alt, empty, fail, opt, preceded, trace}; -use winnow::error::ContextError; -use winnow::error::ErrMode; -use winnow::stream::{AsBStr, AsChar, Compare, Stream as InputStream, StreamIsPartial}; +use winnow::error::ParserError; +use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial}; use winnow::token::literal; -use winnow::{seq, PResult, Parser}; +use winnow::{seq, Parser, Result}; use winnow_datetime::parser::{date_day, date_month}; use winnow_datetime::types::PartialDate; use winnow_datetime::{date_yddd_seq, date_ymd_seq, date_ywd_seq}; @@ -13,11 +12,12 @@ use winnow_datetime::{date_yddd_seq, date_ymd_seq, date_ywd_seq}; /// Parses 2 digit week of the year within range 01-52 /// Parses a date string as ISO 8601 week date. // YYYY-"W"WW-D -fn partial_date_ywd<'i, Input>(i: &mut Input) -> PResult +fn partial_date_ywd<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_date_ywd", move |input: &mut Input| { seq!( @@ -32,15 +32,16 @@ where .map(|(year, week, day)| PartialDate::YWD { year, week, day }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } // YYYY-MM-DD -fn partial_date_ymd<'i, Input>(i: &mut Input) -> PResult +fn partial_date_ymd<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_date_ymd", move |input: &mut Input| { seq!(( @@ -56,15 +57,16 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } // YYYY-DDD -fn partial_date_yddd<'i, Input>(i: &mut Input) -> PResult +fn partial_date_yddd<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_date_yddd", move |input: &mut Input| { seq!(( @@ -75,15 +77,16 @@ where .map(|(year, day)| PartialDate::YDDD { year, day }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// Parses a date string specificed as YYYYMMDD -fn partial_date_ymd_numeric<'i, Input>(i: &mut Input) -> PResult +fn partial_date_ymd_numeric<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_date_ymd_numeric", move |input: &mut Input| { seq!(( @@ -99,32 +102,34 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// Parses a date string specificed as YYYYMMDD -pub fn partial_date_y<'i, Input>(i: &mut Input) -> PResult +fn partial_date_y<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_date_year_only", move |input: &mut Input| { date_year(input).map(|d| PartialDate::Year { year: Some(d), // YYYY }) }) - .parse_next(i) + .parse_next(input) } /// Parses a date string. /// /// See [`date()`][`crate::date()`] for the supported formats. -pub fn partial_date<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn partial_date<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_date", move |input: &mut Input| { alt(( @@ -136,27 +141,33 @@ where )) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// Parses a possibly trunctated partial datetime string based on a partial start date -pub(crate) fn partial_end_date<'i, Input>( - i: &mut Input, +pub(crate) fn partial_end_date<'i, Input, Error>( + input: &mut Input, start_date: &PartialDate, -) -> PResult +) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_end_date", move |input: &mut Input| { match start_date { PartialDate::Year { year: start_year } => { // Parse the year or use fallback only when parsing fails - let year = match date_year.parse_next(input) { + let year = match date_year::.parse_next(input) { Ok(parsed) => Some(parsed), - Err(ErrMode::Backtrack(_)) => *start_year, - Err(e) => return Err(e), // Propagate other errors + Err(e) => { + if !e.is_incomplete() { + *start_year + } else { + return Err(e); + } + } }; Ok(PartialDate::Year { year }) @@ -174,15 +185,19 @@ where } => partial_end_date_ymd(input, start_date), } }) - .parse_next(i) + .parse_next(input) } /// Sifts through portions of end_date parses for a Date::YDDD start_date -fn partial_end_date_yddd<'i, Input>(i: &mut Input, start_date: &PartialDate) -> PResult +pub(crate) fn partial_end_date_yddd<'i, Input, Error>( + input: &mut Input, + start_date: &PartialDate, +) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_end_date_ydd", move |input: &mut Input| { match start_date { @@ -215,15 +230,19 @@ where _ => fail.parse_next(input), } }) - .parse_next(i) + .parse_next(input) } /// Sifts through portions of end_date based on a Date::YMD start_date -fn partial_end_date_ymd<'i, Input>(i: &mut Input, start_date: &PartialDate) -> PResult +fn partial_end_date_ymd<'i, Input, Error>( + input: &mut Input, + start_date: &PartialDate, +) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_end_date_ymd", move |input: &mut Input| { match start_date { @@ -289,18 +308,22 @@ where [_, _, _] => fail.parse_next(input), } } - _ => return Err(ErrMode::Backtrack(ContextError::new())), + _ => return Err(ParserError::from_input(input)), } }) - .parse_next(i) + .parse_next(input) } /// Sifts through portions of end_date based on a Date::YMD start_date -fn partial_end_date_ywd<'i, Input>(i: &mut Input, start_date: &PartialDate) -> PResult +pub(crate) fn partial_end_date_ywd<'i, Input, Error>( + i: &mut Input, + start_date: &PartialDate, +) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_end_date_ywd", move |input: &mut Input| { match start_date { @@ -374,19 +397,20 @@ where #[cfg(test)] mod parsers { use crate::partial_date::{partial_date, partial_end_date}; - use winnow::stream::AsBStr; + use winnow::error::InputError; + use winnow_datetime::types::PartialDate; #[test] fn partial_date_parsing() { // Year assert_eq!( - partial_date(&mut "2015".as_bstr()).unwrap(), + partial_date::<_, InputError<_>>(&mut "2015").unwrap(), PartialDate::Year { year: Some(2015) } ); // YMD assert_eq!( - partial_date(&mut "2015-06-26".as_bstr()).unwrap(), + partial_date::<_, InputError<_>>(&mut "2015-06-26").unwrap(), PartialDate::YMD { year: Some(2015), month: Some(6), @@ -394,7 +418,7 @@ mod parsers { } ); assert_eq!( - partial_date(&mut "2015-06".as_bstr()).unwrap(), + partial_date::<_, InputError<_>>(&mut "2015-06").unwrap(), PartialDate::YMD { year: Some(2015), month: Some(6), @@ -403,7 +427,7 @@ mod parsers { ); // YWD assert_eq!( - partial_date(&mut "2015-W05-6".as_bstr()).unwrap(), + partial_date::<_, InputError<_>>(&mut "2015-W05-6").unwrap(), PartialDate::YWD { year: Some(2015), week: Some(5), @@ -411,7 +435,7 @@ mod parsers { } ); assert_eq!( - partial_date(&mut "2015-W05".as_bstr()).unwrap(), + partial_date::<_, InputError<_>>(&mut "2015-W05").unwrap(), PartialDate::YWD { year: Some(2015), week: Some(5), @@ -420,14 +444,14 @@ mod parsers { ); //Ordinal assert_eq!( - partial_date(&mut "2015-156".as_bstr()).unwrap(), + partial_date::<_, InputError<_>>(&mut "2015-156").unwrap(), PartialDate::YDDD { year: Some(2015), day: Some(156) } ); assert_eq!( - partial_date(&mut "2015-156".as_bstr()).unwrap(), + partial_date::<_, InputError<_>>(&mut "2015-156").unwrap(), PartialDate::YDDD { year: Some(2015), day: Some(156) @@ -438,8 +462,8 @@ mod parsers { #[test] fn partial_ymd() { assert_eq!( - partial_end_date( - &mut "2015-06-26".as_bstr(), + partial_end_date::<_, InputError<_>>( + &mut "2015-06-26", &PartialDate::YMD { year: Some(2015), month: Some(6), @@ -454,8 +478,8 @@ mod parsers { } ); assert_eq!( - partial_end_date( - &mut "06-26".as_bstr(), + partial_end_date::<_, InputError<_>>( + &mut "06-26", &PartialDate::YMD { year: Some(2015), month: Some(6), @@ -470,8 +494,8 @@ mod parsers { } ); assert_eq!( - partial_end_date( - &mut "26".as_bstr(), + partial_end_date::<_, InputError<_>>( + &mut "26", &PartialDate::YMD { year: Some(2015), month: Some(6), @@ -490,8 +514,8 @@ mod parsers { #[test] fn partial_ywd() { assert_eq!( - partial_end_date( - &mut "2024-W51-4".as_bstr(), + partial_end_date::<_, InputError<_>>( + &mut "2024-W51-4", &PartialDate::YWD { year: Some(2024), week: Some(51), @@ -506,8 +530,8 @@ mod parsers { } ); assert_eq!( - partial_end_date( - &mut "W51-4".as_bstr(), + partial_end_date::<_, InputError<_>>( + &mut "W51-4", &PartialDate::YWD { year: Some(2024), week: Some(51), @@ -522,8 +546,8 @@ mod parsers { } ); assert_eq!( - partial_end_date( - &mut "4".as_bstr(), + partial_end_date::<_, InputError<_>>( + &mut "4", &PartialDate::YWD { year: Some(2024), week: Some(51), @@ -542,8 +566,8 @@ mod parsers { #[test] fn partial_yddd() { assert_eq!( - partial_end_date( - &mut "2025-083".as_bstr(), + partial_end_date::<_, InputError<_>>( + &mut "2025-083", &PartialDate::YDDD { year: Some(2025), day: Some(82) diff --git a/crates/winnow-iso8601/src/partial_datetime.rs b/crates/winnow-iso8601/src/partial_datetime.rs index ddfed4a..e836ca0 100644 --- a/crates/winnow-iso8601/src/partial_datetime.rs +++ b/crates/winnow-iso8601/src/partial_datetime.rs @@ -2,19 +2,21 @@ use crate::partial_date::{partial_date, partial_end_date}; use crate::partial_time::{partial_end_base_time, partial_time}; use core::str; use winnow::combinator::{alt, opt, preceded, trace}; -use winnow::error::ContextError; -use winnow::error::ErrMode; -use winnow::stream::{AsBStr, AsChar, Compare, Stream as InputStream, StreamIsPartial}; +use winnow::error::ParserError; +use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial}; use winnow::token::literal; -use winnow::{seq, PResult, Parser}; +use winnow::{seq, Parser, Result}; use winnow_datetime::types::PartialDateTime; // partial date time -pub(crate) fn partial_datetime<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn partial_datetime<'i, Input, Error>( + input: &mut Input, +) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_datetime", move |input: &mut Input| { seq!(( @@ -25,17 +27,18 @@ where .map(|(d, t)| PartialDateTime { date: d, time: t }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } -pub(crate) fn partial_end_datetime<'i, Input>( - i: &mut Input, +pub(crate) fn partial_end_datetime<'i, Input, Error>( + input: &mut Input, start_datetime: &PartialDateTime, -) -> PResult +) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace( "partial_end_datetime", @@ -48,7 +51,7 @@ where let mut end_time = None; if start_date.is_none() && start_date.is_none() { - return Err(ErrMode::Backtrack(ContextError::new())); + return Err(ParserError::from_input(input)); } if let Some(d) = start_date { @@ -68,19 +71,20 @@ where } }, ) - .parse_next(i) + .parse_next(input) } #[cfg(test)] mod parsers { use crate::partial_datetime::{partial_datetime, partial_end_datetime}; - use winnow::stream::AsBStr; + use winnow::error::InputError; + use winnow_datetime::types::{PartialDate, PartialDateTime}; #[test] fn partial_datetime_parsing() { // Year assert_eq!( - partial_datetime(&mut "2015".as_bstr()).unwrap(), + partial_datetime::<_, InputError<_>>(&mut "2015").unwrap(), PartialDateTime { date: Some(PartialDate::Year { year: Some(2015) }), time: None, @@ -88,7 +92,7 @@ mod parsers { ); // YMD assert_eq!( - partial_datetime(&mut "2015-06-26".as_bstr()).unwrap(), + partial_datetime::<_, InputError<_>>(&mut "2015-06-26").unwrap(), PartialDateTime { date: Some(PartialDate::YMD { year: Some(2015), @@ -99,7 +103,7 @@ mod parsers { } ); assert_eq!( - partial_datetime(&mut "2015-06".as_bstr()).unwrap(), + partial_datetime::<_, InputError<_>>(&mut "2015-06").unwrap(), PartialDateTime { date: Some(PartialDate::YMD { year: Some(2015), @@ -111,7 +115,7 @@ mod parsers { ); // YWD assert_eq!( - partial_datetime(&mut "2015-W05-6".as_bstr()).unwrap(), + partial_datetime::<_, InputError<_>>(&mut "2015-W05-6").unwrap(), PartialDateTime { date: Some(PartialDate::YWD { year: Some(2015), @@ -122,7 +126,7 @@ mod parsers { } ); assert_eq!( - partial_datetime(&mut "2015-W05-1".as_bstr()).unwrap(), + partial_datetime::<_, InputError<_>>(&mut "2015-W05-1").unwrap(), PartialDateTime { date: Some(PartialDate::YWD { year: Some(2015), @@ -133,7 +137,7 @@ mod parsers { } ); assert_eq!( - partial_datetime(&mut "2015-W05".as_bstr()).unwrap(), + partial_datetime::<_, InputError<_>>(&mut "2015-W05").unwrap(), PartialDateTime { date: Some(PartialDate::YWD { year: Some(2015), @@ -145,7 +149,7 @@ mod parsers { ); //Ordinal assert_eq!( - partial_datetime(&mut "2015-156".as_bstr()).unwrap(), + partial_datetime::<_, InputError<_>>(&mut "2015-156").unwrap(), PartialDateTime { date: Some(PartialDate::YDDD { year: Some(2015), @@ -155,7 +159,7 @@ mod parsers { } ); assert_eq!( - partial_datetime(&mut "2015-156".as_bstr()).unwrap(), + partial_datetime::<_, InputError<_>>(&mut "2015-156").unwrap(), PartialDateTime { date: Some(PartialDate::YDDD { year: Some(2015), @@ -169,8 +173,8 @@ mod parsers { #[test] fn partial_ymd() { assert_eq!( - partial_end_datetime( - &mut "2015-06-26".as_bstr(), + partial_end_datetime::<_, InputError<_>>( + &mut "2015-06-26", &PartialDateTime { date: Some(PartialDate::YMD { year: Some(2015), @@ -191,8 +195,8 @@ mod parsers { } ); assert_eq!( - partial_end_datetime( - &mut "06-26".as_bstr(), + partial_end_datetime::<_, InputError<_>>( + &mut "06-26", &PartialDateTime { date: Some(PartialDate::YMD { year: Some(2015), @@ -213,8 +217,8 @@ mod parsers { } ); assert_eq!( - partial_end_datetime( - &mut "26".as_bstr(), + partial_end_datetime::<_, InputError<_>>( + &mut "26", &PartialDateTime { date: Some(PartialDate::YMD { year: Some(2015), @@ -239,8 +243,8 @@ mod parsers { #[test] fn partial_ywd() { assert_eq!( - partial_end_datetime( - &mut "2024-W51-4".as_bstr(), + partial_end_datetime::<_, InputError<_>>( + &mut "2024-W51-4", &PartialDateTime { date: Some(PartialDate::YWD { year: Some(2024), @@ -261,8 +265,8 @@ mod parsers { } ); assert_eq!( - partial_end_datetime( - &mut "W51-4".as_bstr(), + partial_end_datetime::<_, InputError<_>>( + &mut "W51-4", &PartialDateTime { date: Some(PartialDate::YWD { year: Some(2024), @@ -283,8 +287,8 @@ mod parsers { } ); assert_eq!( - partial_end_datetime( - &mut "4".as_bstr(), + partial_end_datetime::<_, InputError<_>>( + &mut "4", &PartialDateTime { date: Some(PartialDate::YWD { year: Some(2024), @@ -309,8 +313,8 @@ mod parsers { #[test] fn partial_yddd() { assert_eq!( - partial_end_datetime( - &mut "083".as_bstr(), + partial_end_datetime::<_, InputError<_>>( + &mut "083", &PartialDateTime { date: Some(PartialDate::YDDD { year: Some(2025), diff --git a/crates/winnow-iso8601/src/partial_time.rs b/crates/winnow-iso8601/src/partial_time.rs index ed97acb..239456e 100644 --- a/crates/winnow-iso8601/src/partial_time.rs +++ b/crates/winnow-iso8601/src/partial_time.rs @@ -1,10 +1,11 @@ use crate::offset::offset; use core::str; use winnow::combinator::{alt, empty, fail, opt, preceded, trace}; -use winnow::stream::{AsBStr, AsChar, Compare, Stream as InputStream, StreamIsPartial}; +use winnow::error::ParserError; +use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial}; use winnow::token::literal; use winnow::token::one_of; -use winnow::{seq, PResult, Parser}; +use winnow::{seq, Parser, Result}; use winnow_datetime::parser::fraction_millisecond; use winnow_datetime::parser::time_hour; use winnow_datetime::parser::time_minute; @@ -16,11 +17,12 @@ use winnow_datetime::types::PartialTime; /// /// See [`time()`][`crate::time()`] for the supported formats. // HH:MM:[SS][.(m*)][(Z|+...|-...)] -pub(crate) fn partial_time<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn partial_time<'i, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_time", move |input: &mut Input| { seq!(( @@ -30,18 +32,19 @@ where .map(|r| r.0) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } /// Parses a partial time string. /// /// See [`time()`][`crate::time()`] for the supported formats. // HH:MM:[SS][.(m*)][(Z|+...|-...)] -pub(crate) fn partial_base_time<'i, Input>(i: &mut Input) -> PResult +pub(crate) fn partial_base_time<'a, Input, Error>(input: &mut Input) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'a str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_base_time", move |input: &mut Input| { seq!(PartialTime { @@ -56,37 +59,39 @@ where }) .parse_next(input) }) - .parse_next(i) + .parse_next(input) } // NOTE: this is marked as dead code because this is likely going to be made public #[allow(dead_code)] -pub(crate) fn partial_end_time<'i, Input>( - i: &mut Input, +pub(crate) fn partial_end_time<'i, Input, Error>( + input: &mut Input, start_time: &PartialTime, -) -> PResult +) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'i str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_end_time", move |input: &mut Input| { let _ = opt(alt((literal(" "), literal("T")))).parse_next(input)?; partial_end_base_time(input, start_time) }) - .parse_next(i) + .parse_next(input) } /// a partial time string which can be truncated depending on a partial start time -pub(crate) fn partial_end_base_time<'i, Input>( - i: &mut Input, +pub(crate) fn partial_end_base_time<'a, Input, Error>( + input: &mut Input, start_time: &PartialTime, -) -> PResult +) -> Result where - Input: StreamIsPartial + InputStream + Compare<&'i str>, - ::Slice: AsBStr, - ::Token: AsChar + Clone, + Input: StreamIsPartial + Stream + Compare<&'a str>, + ::Slice: AsBStr, + ::Token: AsChar + Clone, + Error: ParserError, { trace("partial_end_base_time", move |input: &mut Input| { match [ @@ -186,19 +191,20 @@ where [_, _, _, _] => fail.parse_next(input), } }) - .parse_next(i) + .parse_next(input) } #[cfg(test)] mod parsers { use crate::partial_time::{partial_end_time, partial_time}; - use winnow::stream::AsBStr; + use winnow::error::InputError; + use winnow_datetime::types::PartialTime; #[test] fn partial_time_parsing() { assert_eq!( - partial_time(&mut "12:01:30".as_bstr()).unwrap(), + partial_time::<_, InputError<_>>(&mut "12:01:30").unwrap(), PartialTime { hour: Some(12), minute: Some(1), @@ -208,7 +214,7 @@ mod parsers { } ); assert_eq!( - partial_time(&mut "12:01".as_bstr()).unwrap(), + partial_time::<_, InputError<_>>(&mut "12:01").unwrap(), PartialTime { hour: Some(12), minute: Some(1), @@ -218,7 +224,7 @@ mod parsers { } ); assert_eq!( - partial_time(&mut "12:01:30.123".as_bstr()).unwrap(), + partial_time::<_, InputError<_>>(&mut "12:01:30.123").unwrap(), PartialTime { hour: Some(12), minute: Some(1), @@ -232,8 +238,8 @@ mod parsers { #[test] fn partial_end_time_parsing() { assert_eq!( - partial_end_time( - &mut "12:01:30".as_bstr(), + partial_end_time::<_, InputError<_>>( + &mut "12:01:30", &PartialTime { hour: Some(12), minute: Some(1), @@ -252,8 +258,8 @@ mod parsers { } ); assert_eq!( - partial_end_time( - &mut "12:01".as_bstr(), + partial_end_time::<_, InputError<_>>( + &mut "12:01", &PartialTime { hour: Some(12), minute: Some(0), @@ -272,8 +278,8 @@ mod parsers { } ); assert_eq!( - partial_end_time( - &mut "12:01:30.123".as_bstr(), + partial_end_time::<_, InputError<_>>( + &mut "12:01:30.123", &PartialTime { hour: Some(12), minute: Some(1), diff --git a/crates/winnow-iso8601/src/time.rs b/crates/winnow-iso8601/src/time.rs index 9c46db8..10f37bd 100644 --- a/crates/winnow-iso8601/src/time.rs +++ b/crates/winnow-iso8601/src/time.rs @@ -1,9 +1,9 @@ use crate::offset::offset; -use alloc::string::String; -use winnow::combinator::{opt, preceded, trace}; -use winnow::stream::{AsBStr, AsChar, Compare, Stream as InputStream, StreamIsPartial}; +use winnow::combinator::{eof, opt, preceded, terminated, trace}; +use winnow::error::{InputError, ParserError}; +use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial}; use winnow::token::{literal, one_of}; -use winnow::{seq, PResult, Parser}; +use winnow::{seq, Parser, Result}; use winnow_datetime::parser::{fraction_millisecond, time_hour, time_minute, time_second}; use winnow_datetime::Time; @@ -15,12 +15,8 @@ use winnow_datetime::Time; /// ```rust /// let time = winnow_iso8601::parse_time("21:56:42").unwrap(); /// ``` -pub fn parse_time(mut i: &str) -> Result { - if let Ok(parsed) = time(&mut i) { - Ok(parsed) - } else { - Err(format!("Failed to parse time: {}", i)) - } +pub fn parse_time(mut i: &str) -> Result> { + terminated(time, eof).parse_next(&mut i) } /// Parses a time with an optional preceding 'T'. @@ -33,11 +29,12 @@ pub fn parse_time(mut i: &str) -> Result { /// * `0735[00][.123][(Z|(+|-)0000)]` /// // HH:MM:[SS][.(m*)][(Z|+...|-...)] -pub fn time<'i, Input>(i: &mut Input) -> PResult