Skip to content

Commit

Permalink
Merge pull request #581 from epage/examples
Browse files Browse the repository at this point in the history
docs(examples): Switch to ContextError (default)
  • Loading branch information
epage authored Aug 2, 2024
2 parents 132b280 + 5657a5f commit bf4ae12
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 142 deletions.
21 changes: 6 additions & 15 deletions examples/json/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ fn json_bench(c: &mut criterion::Criterion) {
let len = sample.len();
group.throughput(criterion::Throughput::Bytes(len as u64));

group.bench_with_input(criterion::BenchmarkId::new("basic", name), &len, |b, _| {
type Error<'i> = winnow::error::InputError<parser::Stream<'i>>;

b.iter(|| parser::json::<Error<'_>>.parse_peek(sample).unwrap());
});
group.bench_with_input(criterion::BenchmarkId::new("unit", name), &len, |b, _| {
type Error<'i> = ();

Expand All @@ -27,32 +22,28 @@ fn json_bench(c: &mut criterion::Criterion) {
criterion::BenchmarkId::new("context", name),
&len,
|b, _| {
type Error<'i> = winnow::error::ContextError<parser::Stream<'i>>;
type Error = winnow::error::ContextError;

b.iter(|| parser::json::<Error<'_>>.parse_peek(sample).unwrap());
b.iter(|| parser::json::<Error>.parse_peek(sample).unwrap());
},
);
group.bench_with_input(
criterion::BenchmarkId::new("dispatch", name),
&len,
|b, _| {
type Error<'i> = winnow::error::InputError<parser_dispatch::Stream<'i>>;
type Error = winnow::error::ContextError;

b.iter(|| {
parser_dispatch::json::<Error<'_>>
.parse_peek(sample)
.unwrap()
});
b.iter(|| parser_dispatch::json::<Error>.parse_peek(sample).unwrap());
},
);
group.bench_with_input(
criterion::BenchmarkId::new("streaming", name),
&len,
|b, _| {
type Error<'i> = winnow::error::InputError<parser_partial::Stream<'i>>;
type Error = winnow::error::ContextError;

b.iter(|| {
parser_partial::json::<Error<'_>>
parser_partial::json::<Error>
.parse_peek(Partial::new(sample))
.unwrap()
});
Expand Down
59 changes: 27 additions & 32 deletions examples/json/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use winnow::{
combinator::cut_err,
combinator::{delimited, preceded, separated_pair, terminated},
combinator::{repeat, separated},
error::{AddContext, ParserError},
error::{AddContext, ParserError, StrContext},
token::{any, none_of, take, take_while},
};

Expand All @@ -19,7 +19,7 @@ pub(crate) type Stream<'i> = &'i str;
/// The root element of a JSON parser is any value
///
/// A parser has the following signature:
/// `&mut Stream -> PResult<Output, InputError>`, with `PResult` defined as:
/// `&mut Stream -> PResult<Output, ContextError>`, with `PResult` defined as:
/// `type PResult<O, E = (I, ErrorKind)> = Result<O, Err<E>>;`
///
/// most of the times you can ignore the error type and use the default (but this
Expand All @@ -28,15 +28,15 @@ pub(crate) type Stream<'i> = &'i str;
/// Here we use `&str` as input type, but parsers can be generic over
/// the input type, work directly with `&[u8]`, or any other type that
/// implements the required traits.
pub(crate) fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
pub(crate) fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<JsonValue, E> {
delimited(ws, json_value, ws).parse_next(input)
}

/// `alt` is a combinator that tries multiple parsers one by one, until
/// one of them succeeds
fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<JsonValue, E> {
// `alt` combines the each value parser. It returns the result of the first
Expand Down Expand Up @@ -77,7 +77,7 @@ fn boolean<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<bo

/// This parser gathers all `char`s up into a `String`with a parse to take the double quote
/// character, before the string (using `preceded`) and after the string (using `terminated`).
fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<String, E> {
preceded(
Expand All @@ -96,7 +96,7 @@ fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>
)
// `context` lets you add a static string to errors to provide more information in the
// error chain (to indicate which parser had an error)
.context("string")
.context(StrContext::Expected("string".into()))
.parse_next(input)
}

Expand Down Expand Up @@ -157,7 +157,7 @@ fn u16_hex<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<u1
/// accumulating results in a `Vec`, until it encounters an error.
/// If you want more control on the parser application, check out the `iterator`
/// combinator (cf `examples/iterator.rs`)
fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<Vec<JsonValue>, E> {
preceded(
Expand All @@ -167,11 +167,11 @@ fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
(ws, ']'),
)),
)
.context("array")
.context(StrContext::Expected("array".into()))
.parse_next(input)
}

fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<HashMap<String, JsonValue>, E> {
preceded(
Expand All @@ -181,11 +181,11 @@ fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>
(ws, '}'),
)),
)
.context("object")
.context(StrContext::Expected("object".into()))
.parse_next(input)
}

fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<(String, JsonValue), E> {
separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input)
Expand All @@ -205,42 +205,37 @@ const WS: &[char] = &[' ', '\t', '\r', '\n'];
#[cfg(test)]
mod test {
#[allow(clippy::useless_attribute)]
#[allow(dead_code)] // its dead for benches
#[allow(unused_imports)] // its dead for benches
use super::*;

#[allow(clippy::useless_attribute)]
#[allow(dead_code)] // its dead for benches
type Error<'i> = winnow::error::InputError<&'i str>;
type Error = winnow::error::ContextError;

#[test]
fn json_string() {
assert_eq!(string::<Error>.parse_peek("\"\""), Ok(("", "".to_owned())));
assert_eq!(
string::<Error<'_>>.parse_peek("\"\""),
Ok(("", "".to_owned()))
);
assert_eq!(
string::<Error<'_>>.parse_peek("\"abc\""),
string::<Error>.parse_peek("\"abc\""),
Ok(("", "abc".to_owned()))
);
assert_eq!(
string::<Error<'_>>
string::<Error>
.parse_peek("\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""),
Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_owned())),
);
assert_eq!(
string::<Error<'_>>.parse_peek("\"\\uD83D\\uDE10\""),
string::<Error>.parse_peek("\"\\uD83D\\uDE10\""),
Ok(("", "😐".to_owned()))
);

assert!(string::<Error<'_>>.parse_peek("\"").is_err());
assert!(string::<Error<'_>>.parse_peek("\"abc").is_err());
assert!(string::<Error<'_>>.parse_peek("\"\\\"").is_err());
assert!(string::<Error<'_>>.parse_peek("\"\\u123\"").is_err());
assert!(string::<Error<'_>>.parse_peek("\"\\uD800\"").is_err());
assert!(string::<Error<'_>>
.parse_peek("\"\\uD800\\uD800\"")
.is_err());
assert!(string::<Error<'_>>.parse_peek("\"\\uDC00\"").is_err());
assert!(string::<Error>.parse_peek("\"").is_err());
assert!(string::<Error>.parse_peek("\"abc").is_err());
assert!(string::<Error>.parse_peek("\"\\\"").is_err());
assert!(string::<Error>.parse_peek("\"\\u123\"").is_err());
assert!(string::<Error>.parse_peek("\"\\uD800\"").is_err());
assert!(string::<Error>.parse_peek("\"\\uD800\\uD800\"").is_err());
assert!(string::<Error>.parse_peek("\"\\uDC00\"").is_err());
}

#[test]
Expand All @@ -258,7 +253,7 @@ mod test {
.collect(),
);

assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
assert_eq!(json::<Error>.parse_peek(input), Ok(("", expected)));
}

#[test]
Expand All @@ -269,7 +264,7 @@ mod test {

let expected = Array(vec![Num(42.0), Str("x".to_owned())]);

assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
assert_eq!(json::<Error>.parse_peek(input), Ok(("", expected)));
}

#[test]
Expand All @@ -291,7 +286,7 @@ mod test {
"#;

assert_eq!(
json::<Error<'_>>.parse_peek(input),
json::<Error>.parse_peek(input),
Ok((
"",
Object(
Expand Down
59 changes: 27 additions & 32 deletions examples/json/parser_dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use winnow::{
combinator::{alt, dispatch},
combinator::{delimited, preceded, separated_pair, terminated},
combinator::{repeat, separated},
error::{AddContext, ParserError},
error::{AddContext, ParserError, StrContext},
token::{any, none_of, take, take_while},
};

Expand All @@ -22,7 +22,7 @@ pub(crate) type Stream<'i> = &'i str;
/// The root element of a JSON parser is any value
///
/// A parser has the following signature:
/// `&mut Stream -> PResult<Output, InputError>`, with `PResult` defined as:
/// `&mut Stream -> PResult<Output ContextError>`, with `PResult` defined as:
/// `type PResult<O, E = ErrorKind> = Result<O, ErrMode<E>>;`
///
/// most of the times you can ignore the error type and use the default (but this
Expand All @@ -31,15 +31,15 @@ pub(crate) type Stream<'i> = &'i str;
/// Here we use `&str` as input type, but parsers can be generic over
/// the input type, work directly with `&[u8]`, or any other type that
/// implements the required traits.
pub(crate) fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
pub(crate) fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<JsonValue, E> {
delimited(ws, json_value, ws).parse_next(input)
}

/// `alt` is a combinator that tries multiple parsers one by one, until
/// one of them succeeds
fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<JsonValue, E> {
// `dispatch` gives you `match`-like behavior compared to `alt` successively trying different
Expand Down Expand Up @@ -86,7 +86,7 @@ fn false_<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<boo

/// This parser gathers all `char`s up into a `String`with a parse to take the double quote
/// character, before the string (using `preceded`) and after the string (using `terminated`).
fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<String, E> {
preceded(
Expand All @@ -105,7 +105,7 @@ fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>
)
// `context` lets you add a static string to errors to provide more information in the
// error chain (to indicate which parser had an error)
.context("string")
.context(StrContext::Expected("string".into()))
.parse_next(input)
}

Expand Down Expand Up @@ -164,7 +164,7 @@ fn u16_hex<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<u1
/// accumulating results in a `Vec`, until it encounters an error.
/// If you want more control on the parser application, check out the `iterator`
/// combinator (cf `examples/iterator.rs`)
fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<Vec<JsonValue>, E> {
preceded(
Expand All @@ -174,11 +174,11 @@ fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
(ws, ']'),
)),
)
.context("array")
.context(StrContext::Expected("array".into()))
.parse_next(input)
}

fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<HashMap<String, JsonValue>, E> {
preceded(
Expand All @@ -188,11 +188,11 @@ fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>
(ws, '}'),
)),
)
.context("object")
.context(StrContext::Expected("object".into()))
.parse_next(input)
}

fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
input: &mut Stream<'i>,
) -> PResult<(String, JsonValue), E> {
separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input)
Expand All @@ -212,42 +212,37 @@ const WS: &[char] = &[' ', '\t', '\r', '\n'];
#[cfg(test)]
mod test {
#[allow(clippy::useless_attribute)]
#[allow(dead_code)] // its dead for benches
#[allow(unused_imports)] // its dead for benches
use super::*;

#[allow(clippy::useless_attribute)]
#[allow(dead_code)] // its dead for benches
type Error<'i> = winnow::error::InputError<&'i str>;
type Error = winnow::error::ContextError;

#[test]
fn json_string() {
assert_eq!(string::<Error>.parse_peek("\"\""), Ok(("", "".to_owned())));
assert_eq!(
string::<Error<'_>>.parse_peek("\"\""),
Ok(("", "".to_owned()))
);
assert_eq!(
string::<Error<'_>>.parse_peek("\"abc\""),
string::<Error>.parse_peek("\"abc\""),
Ok(("", "abc".to_owned()))
);
assert_eq!(
string::<Error<'_>>
string::<Error>
.parse_peek("\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""),
Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_owned())),
);
assert_eq!(
string::<Error<'_>>.parse_peek("\"\\uD83D\\uDE10\""),
string::<Error>.parse_peek("\"\\uD83D\\uDE10\""),
Ok(("", "😐".to_owned()))
);

assert!(string::<Error<'_>>.parse_peek("\"").is_err());
assert!(string::<Error<'_>>.parse_peek("\"abc").is_err());
assert!(string::<Error<'_>>.parse_peek("\"\\\"").is_err());
assert!(string::<Error<'_>>.parse_peek("\"\\u123\"").is_err());
assert!(string::<Error<'_>>.parse_peek("\"\\uD800\"").is_err());
assert!(string::<Error<'_>>
.parse_peek("\"\\uD800\\uD800\"")
.is_err());
assert!(string::<Error<'_>>.parse_peek("\"\\uDC00\"").is_err());
assert!(string::<Error>.parse_peek("\"").is_err());
assert!(string::<Error>.parse_peek("\"abc").is_err());
assert!(string::<Error>.parse_peek("\"\\\"").is_err());
assert!(string::<Error>.parse_peek("\"\\u123\"").is_err());
assert!(string::<Error>.parse_peek("\"\\uD800\"").is_err());
assert!(string::<Error>.parse_peek("\"\\uD800\\uD800\"").is_err());
assert!(string::<Error>.parse_peek("\"\\uDC00\"").is_err());
}

#[test]
Expand All @@ -265,7 +260,7 @@ mod test {
.collect(),
);

assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
assert_eq!(json::<Error>.parse_peek(input), Ok(("", expected)));
}

#[test]
Expand All @@ -276,7 +271,7 @@ mod test {

let expected = Array(vec![Num(42.0), Str("x".to_owned())]);

assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
assert_eq!(json::<Error>.parse_peek(input), Ok(("", expected)));
}

#[test]
Expand All @@ -298,7 +293,7 @@ mod test {
"#;

assert_eq!(
json::<Error<'_>>.parse_peek(input),
json::<Error>.parse_peek(input),
Ok((
"",
Object(
Expand Down
Loading

0 comments on commit bf4ae12

Please sign in to comment.