winnow/_tutorial/chapter_6.rs
1//! # Chapter 6: Integrating the Parser
2//!
3//! So far, we've highlighted how to incrementally parse, but how do we bring this all together
4//! into our application?
5//!
6//! Parsers we've been working with look like:
7//! ```rust
8//! # use winnow::error::ContextError;
9//! # use winnow::Parser;
10//! use winnow::Result;
11//!
12//! pub fn parser<'s>(input: &mut &'s str) -> Result<&'s str> {
13//! // ...
14//! # Ok("")
15//! }
16//! ```
17//! 1. We have to decide what to do about the "remainder" of the `input`.
18//! 2. The [`Result`] may not be compatible with the rest of the Rust ecosystem.
19//! Normally, Rust applications want errors that are `std::error::Error + Send + Sync + 'static`
20//! meaning:
21//! - They implement the [`std::error::Error`] trait
22//! - They can be sent across threads
23//! - They are safe to be referenced across threads
24//! - They do not borrow
25//!
26//! winnow provides [`Parser::parse`] to help with this:
27//! - Ensures we hit [`eof`]
28//! - Wraps the error in [`ParseError`]
29//! - For simple cases, [`ParseError`] provides a [`std::fmt::Display`] impl to render the error.
30//! - For more involved cases, [`ParseError`] provides the original [`input`][ParseError::input] and the
31//! [`offset`][ParseError::offset] of where it failed so you can capture this information in
32//! your error, [rendering it as you wish][chapter_7#error-adaptation-and-rendering].
33//! - Converts from [`ModalResult`] to [`Result`] (if used, more on this in [`chapter_7`])
34//!
35//! However, [`ParseError`] will still need some level of adaptation to integrate with your
36//! application's error type (like with `?`).
37//!
38//! ```rust
39//! # use winnow::prelude::*;
40//! # use winnow::Result;
41//! # use winnow::token::take_while;
42//! # use winnow::combinator::dispatch;
43//! # use winnow::token::take;
44//! # use winnow::combinator::fail;
45//! use winnow::Parser;
46//!
47//! #[derive(Debug, PartialEq, Eq)]
48//! pub struct Hex(usize);
49//!
50//! impl std::str::FromStr for Hex {
51//! type Err = anyhow::Error;
52//!
53//! fn from_str(input: &str) -> Result<Self, Self::Err> {
54//! parse_digits
55//! .map(Hex)
56//! .parse(input)
57//! .map_err(|e| anyhow::format_err!("{e}"))
58//! }
59//! }
60//!
61//! // ...
62//! # fn parse_digits<'s>(input: &mut &'s str) -> Result<usize> {
63//! # dispatch!(take(2usize);
64//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
65//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
66//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
67//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
68//! # _ => fail,
69//! # ).parse_next(input)
70//! # }
71//! #
72//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
73//! # take_while(1.., (
74//! # ('0'..='1'),
75//! # )).parse_next(input)
76//! # }
77//! #
78//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
79//! # take_while(1.., (
80//! # ('0'..='7'),
81//! # )).parse_next(input)
82//! # }
83//! #
84//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
85//! # take_while(1.., (
86//! # ('0'..='9'),
87//! # )).parse_next(input)
88//! # }
89//! #
90//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
91//! # take_while(1.., (
92//! # ('0'..='9'),
93//! # ('A'..='F'),
94//! # ('a'..='f'),
95//! # )).parse_next(input)
96//! # }
97//!
98//! fn main() {
99//! let input = "0x1a2b";
100//! assert_eq!(input.parse::<Hex>().unwrap(), Hex(0x1a2b));
101//!
102//! let input = "0x1a2b Hello";
103//! assert!(input.parse::<Hex>().is_err());
104//! let input = "ghiHello";
105//! assert!(input.parse::<Hex>().is_err());
106//! }
107//! ```
108
109#![allow(unused_imports)]
110use super::chapter_1;
111use super::chapter_7;
112use crate::combinator::eof;
113use crate::error::ErrMode;
114use crate::error::ParseError;
115use crate::ModalResult;
116use crate::Parser;
117
118pub use super::chapter_5 as previous;
119pub use super::chapter_7 as next;
120pub use crate::_tutorial as table_of_contents;