• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! # Chapter 4: Parsers With Custom Return Types
2 //!
3 //! So far, we have seen mostly functions that take an `&str`, and return a
4 //! `PResult<&str>`. Splitting strings into smaller strings and characters is certainly
5 //! useful, but it's not the only thing winnow is capable of!
6 //!
7 //! A useful operation when parsing is to convert between types; for example
8 //! parsing from `&str` to another primitive, like [`usize`].
9 //!
10 //! All we need to do for our parser to return a different type is to change
11 //! the type parameter of [`PResult`] to the desired return type.
12 //! For example, to return a `usize`, return a `PResult<usize>`.
13 //!
14 //! One winnow-native way of doing a type conversion is to use the
15 //! [`Parser::parse_to`] combinator
16 //! to convert from a successful parse to a particular type using [`FromStr`].
17 //!
18 //! The following code converts from a string containing a number to `usize`:
19 //! ```rust
20 //! # use winnow::prelude::*;
21 //! # use winnow::ascii::digit1;
22 //! #
23 //! fn parse_digits(input: &mut &str) -> PResult<usize> {
24 //!     digit1
25 //!         .parse_to()
26 //!         .parse_next(input)
27 //! }
28 //!
29 //! fn main() {
30 //!     let mut input = "1024 Hello";
31 //!
32 //!     let output = parse_digits.parse_next(&mut input).unwrap();
33 //!     assert_eq!(input, " Hello");
34 //!     assert_eq!(output, 1024);
35 //!
36 //!     assert!(parse_digits(&mut "Z").is_err());
37 //! }
38 //! ```
39 //!
40 //! `Parser::parse_to` is just a convenient form of [`Parser::try_map`] which we can use to handle
41 //! all radices of numbers:
42 //! ```rust
43 //! # use winnow::prelude::*;
44 //! # use winnow::token::take_while;
45 //! use winnow::combinator::dispatch;
46 //! use winnow::token::take;
47 //! use winnow::combinator::fail;
48 //!
49 //! fn parse_digits(input: &mut &str) -> PResult<usize> {
50 //!     dispatch!(take(2usize);
51 //!         "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
52 //!         "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
53 //!         "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
54 //!         "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
55 //!         _ => fail,
56 //!     ).parse_next(input)
57 //! }
58 //!
59 //! // ...
60 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
61 //! #     take_while(1.., (
62 //! #         ('0'..='1'),
63 //! #     )).parse_next(input)
64 //! # }
65 //! #
66 //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
67 //! #     take_while(1.., (
68 //! #         ('0'..='7'),
69 //! #     )).parse_next(input)
70 //! # }
71 //! #
72 //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
73 //! #     take_while(1.., (
74 //! #         ('0'..='9'),
75 //! #     )).parse_next(input)
76 //! # }
77 //! #
78 //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
79 //! #     take_while(1.., (
80 //! #         ('0'..='9'),
81 //! #         ('A'..='F'),
82 //! #         ('a'..='f'),
83 //! #     )).parse_next(input)
84 //! # }
85 //!
86 //! fn main() {
87 //!     let mut input = "0x1a2b Hello";
88 //!
89 //!     let digits = parse_digits.parse_next(&mut input).unwrap();
90 //!
91 //!     assert_eq!(input, " Hello");
92 //!     assert_eq!(digits, 0x1a2b);
93 //!
94 //!     assert!(parse_digits(&mut "ghiWorld").is_err());
95 //! }
96 //! ```
97 //!
98 //! See also [`Parser`] for more output-modifying parsers.
99 
100 #![allow(unused_imports)]
101 use crate::PResult;
102 use crate::Parser;
103 use std::str::FromStr;
104 
105 pub use super::chapter_3 as previous;
106 pub use super::chapter_5 as next;
107 pub use crate::_tutorial as table_of_contents;
108