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