• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! # Chapter 6: Error Reporting
2 //!
3 //! ## `Error`
4 //!
5 //! Back in [`chapter_1`], we glossed over the `Err` side of [`PResult`].  `PResult<O>` is
6 //! actually short for `PResult<O, E=ContextError>` where [`ContextError`] is a relatively cheap
7 //! way of building up reasonable errors for humans.
8 //!
9 //! You can use [`Parser::context`] to annotate the error with custom types
10 //! while unwinding to further improve the error quality.
11 //!
12 //! ```rust
13 //! # use winnow::prelude::*;
14 //! # use winnow::token::take_while;
15 //! # use winnow::combinator::alt;
16 //! use winnow::error::StrContext;
17 //!
18 //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
19 //!     alt((
20 //!         ("0b", parse_bin_digits).context(StrContext::Label("binary")),
21 //!         ("0o", parse_oct_digits).context(StrContext::Label("octal")),
22 //!         ("0d", parse_dec_digits).context(StrContext::Label("decimal")),
23 //!         ("0x", parse_hex_digits).context(StrContext::Label("hexadecimal")),
24 //!     )).parse_next(input)
25 //! }
26 //!
27 //! // ...
28 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
29 //! #     take_while(1.., (
30 //! #         ('0'..='7'),
31 //! #     )).parse_next(input)
32 //! # }
33 //! #
34 //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
35 //! #     take_while(1.., (
36 //! #         ('0'..='7'),
37 //! #     )).parse_next(input)
38 //! # }
39 //! #
40 //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
41 //! #     take_while(1.., (
42 //! #         ('0'..='9'),
43 //! #     )).parse_next(input)
44 //! # }
45 //! #
46 //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
47 //! #     take_while(1.., (
48 //! #         ('0'..='9'),
49 //! #         ('A'..='F'),
50 //! #         ('a'..='f'),
51 //! #     )).parse_next(input)
52 //! # }
53 //!
54 //! fn main() {
55 //!     let mut input = "0x1a2b Hello";
56 //!
57 //!     let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
58 //!
59 //!     assert_eq!(input, " Hello");
60 //!     assert_eq!(prefix, "0x");
61 //!     assert_eq!(digits, "1a2b");
62 //! }
63 //! ```
64 //!
65 //! At first glance, this looks correct but what `context` will be reported when parsing `"0b5"`?
66 //! If you remember back to [`chapter_3`], [`alt`] will only report the last error by default which
67 //! means when parsing `"0b5"`, the `context` will be `"hexadecimal"`.
68 //!
69 //! ## `ErrMode`
70 //!
71 //! Let's break down `PResult<O, E>` one step further:
72 //! ```rust
73 //! # use winnow::error::ErrorKind;
74 //! # use winnow::error::ErrMode;
75 //! pub type PResult<O, E = ErrorKind> = Result<O, ErrMode<E>>;
76 //! ```
77 //! [`PResult`] is just a fancy wrapper around `Result` that wraps our error in an [`ErrMode`]
78 //! type.
79 //!
80 //! [`ErrMode`] is an enum with [`Backtrack`] and [`Cut`] variants (ignore [`Incomplete`] as its only
81 //! relevant for [streaming][_topic::stream]). By default, errors are [`Backtrack`], meaning that
82 //! other parsing branches will be attempted on failure, like the next case of an [`alt`].  [`Cut`]
83 //! shortcircuits all other branches, immediately reporting the error.
84 //!
85 //! So we can get the correct `context` by modifying the above example with [`cut_err`]:
86 //! ```rust
87 //! # use winnow::prelude::*;
88 //! # use winnow::token::take_while;
89 //! # use winnow::combinator::alt;
90 //! # use winnow::error::StrContext;
91 //! use winnow::combinator::cut_err;
92 //!
93 //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
94 //!     alt((
95 //!         ("0b", cut_err(parse_bin_digits)).context(StrContext::Label("binary")),
96 //!         ("0o", cut_err(parse_oct_digits)).context(StrContext::Label("octal")),
97 //!         ("0d", cut_err(parse_dec_digits)).context(StrContext::Label("decimal")),
98 //!         ("0x", cut_err(parse_hex_digits)).context(StrContext::Label("hexadecimal")),
99 //!     )).parse_next(input)
100 //! }
101 //!
102 //! // ...
103 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
104 //! #     take_while(1.., (
105 //! #         ('0'..='7'),
106 //! #     )).parse_next(input)
107 //! # }
108 //! #
109 //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
110 //! #     take_while(1.., (
111 //! #         ('0'..='7'),
112 //! #     )).parse_next(input)
113 //! # }
114 //! #
115 //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
116 //! #     take_while(1.., (
117 //! #         ('0'..='9'),
118 //! #     )).parse_next(input)
119 //! # }
120 //! #
121 //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
122 //! #     take_while(1.., (
123 //! #         ('0'..='9'),
124 //! #         ('A'..='F'),
125 //! #         ('a'..='f'),
126 //! #     )).parse_next(input)
127 //! # }
128 //!
129 //! fn main() {
130 //!     let mut input = "0x1a2b Hello";
131 //!
132 //!     let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
133 //!
134 //!     assert_eq!(input, " Hello");
135 //!     assert_eq!(prefix, "0x");
136 //!     assert_eq!(digits, "1a2b");
137 //! }
138 //! ```
139 //! Now, when parsing `"0b5"`, the `context` will be `"binary"`.
140 
141 #![allow(unused_imports)]
142 use super::chapter_1;
143 use super::chapter_3;
144 use crate::combinator::alt;
145 use crate::combinator::cut_err;
146 use crate::error::ContextError;
147 use crate::error::ErrMode;
148 use crate::error::ErrMode::*;
149 use crate::error::ErrorKind;
150 use crate::PResult;
151 use crate::Parser;
152 use crate::_topic;
153 
154 pub use super::chapter_5 as previous;
155 pub use super::chapter_7 as next;
156 pub use crate::_tutorial as table_of_contents;
157