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

#![allow(unused_imports)]
use crate::PResult;
use crate::Parser;
use std::str::FromStr;

pub use super::chapter_3 as previous;
pub use super::chapter_5 as next;
pub use crate::_tutorial as table_of_content;