1 //! Character specific parsers and combinators
2 //!
3 //! Functions recognizing specific characters
4
5 #[cfg(test)]
6 mod tests;
7
8 use crate::lib::std::ops::{Add, Shl};
9
10 use crate::combinator::alt;
11 use crate::combinator::cut_err;
12 use crate::combinator::opt;
13 use crate::combinator::trace;
14 use crate::error::ParserError;
15 use crate::error::{ErrMode, ErrorKind, Needed};
16 use crate::stream::{AsBStr, AsChar, ParseSlice, Stream, StreamIsPartial};
17 use crate::stream::{Compare, CompareResult};
18 use crate::token::one_of;
19 use crate::token::take_till;
20 use crate::token::take_while;
21 use crate::PResult;
22 use crate::Parser;
23
24 /// Mark a value as case-insensitive for ASCII characters
25 ///
26 /// # Example
27 /// ```rust
28 /// # use winnow::prelude::*;
29 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}};
30 /// # use winnow::ascii::Caseless;
31 ///
32 /// fn parser<'s>(s: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
33 /// Caseless("hello").parse_next(s)
34 /// }
35 ///
36 /// assert_eq!(parser.parse_peek("Hello, World!"), Ok((", World!", "Hello")));
37 /// assert_eq!(parser.parse_peek("hello, World!"), Ok((", World!", "hello")));
38 /// assert_eq!(parser.parse_peek("HeLlo, World!"), Ok((", World!", "HeLlo")));
39 /// assert_eq!(parser.parse_peek("Some"), Err(ErrMode::Backtrack(InputError::new("Some", ErrorKind::Tag))));
40 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
41 /// ```
42 #[derive(Copy, Clone, Debug)]
43 pub struct Caseless<T>(pub T);
44
45 impl Caseless<&str> {
46 /// Get the byte-representation of this case-insensitive value
47 #[inline(always)]
as_bytes(&self) -> Caseless<&[u8]>48 pub fn as_bytes(&self) -> Caseless<&[u8]> {
49 Caseless(self.0.as_bytes())
50 }
51 }
52
53 /// Recognizes the string `"\r\n"`.
54 ///
55 /// *Complete version*: Will return an error if there's not enough input data.
56 ///
57 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
58 ///
59 /// # Example
60 ///
61 /// ```
62 /// # use winnow::prelude::*;
63 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}};
64 /// # use winnow::ascii::crlf;
65 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
66 /// crlf.parse_next(input)
67 /// }
68 ///
69 /// assert_eq!(parser.parse_peek("\r\nc"), Ok(("c", "\r\n")));
70 /// assert_eq!(parser.parse_peek("ab\r\nc"), Err(ErrMode::Backtrack(InputError::new("ab\r\nc", ErrorKind::Tag))));
71 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
72 /// ```
73 ///
74 /// ```
75 /// # use winnow::prelude::*;
76 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
77 /// # use winnow::Partial;
78 /// # use winnow::ascii::crlf;
79 /// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Ok((Partial::new("c"), "\r\n")));
80 /// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("ab\r\nc"), ErrorKind::Tag))));
81 /// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(2))));
82 /// ```
83 #[inline(always)]
crlf<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>,84 pub fn crlf<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
85 where
86 I: StreamIsPartial,
87 I: Stream,
88 I: Compare<&'static str>,
89 {
90 trace("crlf", "\r\n").parse_next(input)
91 }
92
93 /// Recognizes a string of any char except `"\r\n"` or `"\n"`.
94 ///
95 /// *Complete version*: Will return an error if there's not enough input data.
96 ///
97 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
98 ///
99 /// # Example
100 ///
101 /// ```
102 /// # use winnow::prelude::*;
103 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
104 /// # use winnow::ascii::till_line_ending;
105 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
106 /// till_line_ending.parse_next(input)
107 /// }
108 ///
109 /// assert_eq!(parser.parse_peek("ab\r\nc"), Ok(("\r\nc", "ab")));
110 /// assert_eq!(parser.parse_peek("ab\nc"), Ok(("\nc", "ab")));
111 /// assert_eq!(parser.parse_peek("abc"), Ok(("", "abc")));
112 /// assert_eq!(parser.parse_peek(""), Ok(("", "")));
113 /// assert_eq!(parser.parse_peek("a\rb\nc"), Err(ErrMode::Backtrack(InputError::new("\rb\nc", ErrorKind::Tag ))));
114 /// assert_eq!(parser.parse_peek("a\rbc"), Err(ErrMode::Backtrack(InputError::new("\rbc", ErrorKind::Tag ))));
115 /// ```
116 ///
117 /// ```
118 /// # use winnow::prelude::*;
119 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
120 /// # use winnow::Partial;
121 /// # use winnow::ascii::till_line_ending;
122 /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Ok((Partial::new("\r\nc"), "ab")));
123 /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("abc")), Err(ErrMode::Incomplete(Needed::new(1))));
124 /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
125 /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rb\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rb\nc"), ErrorKind::Tag ))));
126 /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rbc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rbc"), ErrorKind::Tag ))));
127 /// ```
128 #[inline(always)]
till_line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, <I as Stream>::Token: AsChar + Clone,129 pub fn till_line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
130 where
131 I: StreamIsPartial,
132 I: Stream,
133 I: Compare<&'static str>,
134 <I as Stream>::Token: AsChar + Clone,
135 {
136 trace("till_line_ending", move |input: &mut I| {
137 if <I as StreamIsPartial>::is_partial_supported() {
138 till_line_ending_::<_, _, true>(input)
139 } else {
140 till_line_ending_::<_, _, false>(input)
141 }
142 })
143 .parse_next(input)
144 }
145
146 /// Deprecated, replaced with [`till_line_ending`]
147 #[deprecated(since = "0.5.35", note = "Replaced with `till_line_ending`")]
148 #[inline(always)]
not_line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, <I as Stream>::Token: AsChar + Clone,149 pub fn not_line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
150 where
151 I: StreamIsPartial,
152 I: Stream,
153 I: Compare<&'static str>,
154 <I as Stream>::Token: AsChar + Clone,
155 {
156 till_line_ending(input)
157 }
158
till_line_ending_<I, E: ParserError<I>, const PARTIAL: bool>( input: &mut I, ) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, <I as Stream>::Token: AsChar + Clone,159 fn till_line_ending_<I, E: ParserError<I>, const PARTIAL: bool>(
160 input: &mut I,
161 ) -> PResult<<I as Stream>::Slice, E>
162 where
163 I: StreamIsPartial,
164 I: Stream,
165 I: Compare<&'static str>,
166 <I as Stream>::Token: AsChar + Clone,
167 {
168 let res = take_till(0.., ('\r', '\n')).parse_next(input)?;
169 if input.compare("\r") == CompareResult::Ok {
170 let comp = input.compare("\r\n");
171 match comp {
172 //FIXME: calculate the right index
173 CompareResult::Ok => {}
174 CompareResult::Incomplete if PARTIAL && input.is_partial() => {
175 return Err(ErrMode::Incomplete(Needed::Unknown));
176 }
177 CompareResult::Incomplete | CompareResult::Error => {
178 let e: ErrorKind = ErrorKind::Tag;
179 return Err(ErrMode::from_error_kind(input, e));
180 }
181 }
182 }
183 Ok(res)
184 }
185
186 /// Recognizes an end of line (both `"\n"` and `"\r\n"`).
187 ///
188 /// *Complete version*: Will return an error if there's not enough input data.
189 ///
190 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
191 ///
192 /// # Example
193 ///
194 /// ```
195 /// # use winnow::prelude::*;
196 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
197 /// # use winnow::ascii::line_ending;
198 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
199 /// line_ending.parse_next(input)
200 /// }
201 ///
202 /// assert_eq!(parser.parse_peek("\r\nc"), Ok(("c", "\r\n")));
203 /// assert_eq!(parser.parse_peek("ab\r\nc"), Err(ErrMode::Backtrack(InputError::new("ab\r\nc", ErrorKind::Tag))));
204 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
205 /// ```
206 ///
207 /// ```
208 /// # use winnow::prelude::*;
209 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
210 /// # use winnow::Partial;
211 /// # use winnow::ascii::line_ending;
212 /// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Ok((Partial::new("c"), "\r\n")));
213 /// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("ab\r\nc"), ErrorKind::Tag))));
214 /// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
215 /// ```
216 #[inline(always)]
line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>,217 pub fn line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
218 where
219 I: StreamIsPartial,
220 I: Stream,
221 I: Compare<&'static str>,
222 {
223 trace("line_ending", alt(("\n", "\r\n"))).parse_next(input)
224 }
225
226 /// Matches a newline character `'\n'`.
227 ///
228 /// *Complete version*: Will return an error if there's not enough input data.
229 ///
230 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
231 ///
232 /// # Example
233 ///
234 /// ```
235 /// # use winnow::prelude::*;
236 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
237 /// # use winnow::ascii::newline;
238 /// fn parser<'s>(input: &mut &'s str) -> PResult<char, InputError<&'s str>> {
239 /// newline.parse_next(input)
240 /// }
241 ///
242 /// assert_eq!(parser.parse_peek("\nc"), Ok(("c", '\n')));
243 /// assert_eq!(parser.parse_peek("\r\nc"), Err(ErrMode::Backtrack(InputError::new("\r\nc", ErrorKind::Verify))));
244 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
245 /// ```
246 ///
247 /// ```
248 /// # use winnow::prelude::*;
249 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
250 /// # use winnow::Partial;
251 /// # use winnow::ascii::newline;
252 /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("\nc")), Ok((Partial::new("c"), '\n')));
253 /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\r\nc"), ErrorKind::Verify))));
254 /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
255 /// ```
256 #[inline(always)]
newline<I, Error: ParserError<I>>(input: &mut I) -> PResult<char, Error> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar + Clone,257 pub fn newline<I, Error: ParserError<I>>(input: &mut I) -> PResult<char, Error>
258 where
259 I: StreamIsPartial,
260 I: Stream,
261 <I as Stream>::Token: AsChar + Clone,
262 {
263 trace("newline", '\n'.map(AsChar::as_char)).parse_next(input)
264 }
265
266 /// Matches a tab character `'\t'`.
267 ///
268 /// *Complete version*: Will return an error if there's not enough input data.
269 ///
270 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
271 ///
272 /// # Example
273 ///
274 /// ```
275 /// # use winnow::prelude::*;
276 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
277 /// # use winnow::ascii::tab;
278 /// fn parser<'s>(input: &mut &'s str) -> PResult<char, InputError<&'s str>> {
279 /// tab.parse_next(input)
280 /// }
281 ///
282 /// assert_eq!(parser.parse_peek("\tc"), Ok(("c", '\t')));
283 /// assert_eq!(parser.parse_peek("\r\nc"), Err(ErrMode::Backtrack(InputError::new("\r\nc", ErrorKind::Verify))));
284 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
285 /// ```
286 ///
287 /// ```
288 /// # use winnow::prelude::*;
289 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
290 /// # use winnow::Partial;
291 /// # use winnow::ascii::tab;
292 /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("\tc")), Ok((Partial::new("c"), '\t')));
293 /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\r\nc"), ErrorKind::Verify))));
294 /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
295 /// ```
296 #[inline(always)]
tab<I, Error: ParserError<I>>(input: &mut I) -> PResult<char, Error> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar + Clone,297 pub fn tab<I, Error: ParserError<I>>(input: &mut I) -> PResult<char, Error>
298 where
299 I: StreamIsPartial,
300 I: Stream,
301 <I as Stream>::Token: AsChar + Clone,
302 {
303 trace("tab", '\t'.map(AsChar::as_char)).parse_next(input)
304 }
305
306 /// Recognizes zero or more lowercase and uppercase ASCII alphabetic characters: `'a'..='z'`, `'A'..='Z'`
307 ///
308 /// *Complete version*: Will return the whole input if no terminating token is found (a non
309 /// alphabetic character).
310 ///
311 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
312 /// or if no terminating token is found (a non alphabetic character).
313 ///
314 /// # Example
315 ///
316 /// ```
317 /// # use winnow::prelude::*;
318 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
319 /// # use winnow::ascii::alpha0;
320 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
321 /// alpha0.parse_next(input)
322 /// }
323 ///
324 /// assert_eq!(parser.parse_peek("ab1c"), Ok(("1c", "ab")));
325 /// assert_eq!(parser.parse_peek("1c"), Ok(("1c", "")));
326 /// assert_eq!(parser.parse_peek(""), Ok(("", "")));
327 /// ```
328 ///
329 /// ```
330 /// # use winnow::prelude::*;
331 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
332 /// # use winnow::Partial;
333 /// # use winnow::ascii::alpha0;
334 /// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("ab1c")), Ok((Partial::new("1c"), "ab")));
335 /// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("1c")), Ok((Partial::new("1c"), "")));
336 /// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
337 /// ```
338 #[inline(always)]
alpha0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar,339 pub fn alpha0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
340 where
341 I: StreamIsPartial,
342 I: Stream,
343 <I as Stream>::Token: AsChar,
344 {
345 trace("alpha0", take_while(0.., AsChar::is_alpha)).parse_next(input)
346 }
347
348 /// Recognizes one or more lowercase and uppercase ASCII alphabetic characters: `'a'..='z'`, `'A'..='Z'`
349 ///
350 /// *Complete version*: Will return an error if there's not enough input data,
351 /// or the whole input if no terminating token is found (a non alphabetic character).
352 ///
353 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
354 /// or if no terminating token is found (a non alphabetic character).
355 ///
356 /// # Example
357 ///
358 /// ```
359 /// # use winnow::prelude::*;
360 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
361 /// # use winnow::ascii::alpha1;
362 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
363 /// alpha1.parse_next(input)
364 /// }
365 ///
366 /// assert_eq!(parser.parse_peek("aB1c"), Ok(("1c", "aB")));
367 /// assert_eq!(parser.parse_peek("1c"), Err(ErrMode::Backtrack(InputError::new("1c", ErrorKind::Slice))));
368 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
369 /// ```
370 ///
371 /// ```
372 /// # use winnow::prelude::*;
373 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
374 /// # use winnow::Partial;
375 /// # use winnow::ascii::alpha1;
376 /// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("aB1c")), Ok((Partial::new("1c"), "aB")));
377 /// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("1c")), Err(ErrMode::Backtrack(InputError::new(Partial::new("1c"), ErrorKind::Slice))));
378 /// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
379 /// ```
380 #[inline(always)]
alpha1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar,381 pub fn alpha1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
382 where
383 I: StreamIsPartial,
384 I: Stream,
385 <I as Stream>::Token: AsChar,
386 {
387 trace("alpha1", take_while(1.., AsChar::is_alpha)).parse_next(input)
388 }
389
390 /// Recognizes zero or more ASCII numerical characters: `'0'..='9'`
391 ///
392 /// *Complete version*: Will return an error if there's not enough input data,
393 /// or the whole input if no terminating token is found (a non digit character).
394 ///
395 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
396 /// or if no terminating token is found (a non digit character).
397 ///
398 /// # Example
399 ///
400 /// ```
401 /// # use winnow::prelude::*;
402 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
403 /// # use winnow::ascii::digit0;
404 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
405 /// digit0.parse_next(input)
406 /// }
407 ///
408 /// assert_eq!(parser.parse_peek("21c"), Ok(("c", "21")));
409 /// assert_eq!(parser.parse_peek("21"), Ok(("", "21")));
410 /// assert_eq!(parser.parse_peek("a21c"), Ok(("a21c", "")));
411 /// assert_eq!(parser.parse_peek(""), Ok(("", "")));
412 /// ```
413 ///
414 /// ```
415 /// # use winnow::prelude::*;
416 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
417 /// # use winnow::Partial;
418 /// # use winnow::ascii::digit0;
419 /// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("21c")), Ok((Partial::new("c"), "21")));
420 /// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("a21c")), Ok((Partial::new("a21c"), "")));
421 /// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
422 /// ```
423 #[inline(always)]
digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar,424 pub fn digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
425 where
426 I: StreamIsPartial,
427 I: Stream,
428 <I as Stream>::Token: AsChar,
429 {
430 trace("digit0", take_while(0.., AsChar::is_dec_digit)).parse_next(input)
431 }
432
433 /// Recognizes one or more ASCII numerical characters: `'0'..='9'`
434 ///
435 /// *Complete version*: Will return an error if there's not enough input data,
436 /// or the whole input if no terminating token is found (a non digit character).
437 ///
438 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
439 /// or if no terminating token is found (a non digit character).
440 ///
441 /// # Example
442 ///
443 /// ```
444 /// # use winnow::prelude::*;
445 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
446 /// # use winnow::ascii::digit1;
447 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
448 /// digit1.parse_next(input)
449 /// }
450 ///
451 /// assert_eq!(parser.parse_peek("21c"), Ok(("c", "21")));
452 /// assert_eq!(parser.parse_peek("c1"), Err(ErrMode::Backtrack(InputError::new("c1", ErrorKind::Slice))));
453 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
454 /// ```
455 ///
456 /// ```
457 /// # use winnow::prelude::*;
458 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
459 /// # use winnow::Partial;
460 /// # use winnow::ascii::digit1;
461 /// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("21c")), Ok((Partial::new("c"), "21")));
462 /// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("c1")), Err(ErrMode::Backtrack(InputError::new(Partial::new("c1"), ErrorKind::Slice))));
463 /// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
464 /// ```
465 ///
466 /// ## Parsing an integer
467 ///
468 /// You can use `digit1` in combination with [`Parser::try_map`] to parse an integer:
469 ///
470 /// ```
471 /// # use winnow::prelude::*;
472 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed, Parser};
473 /// # use winnow::ascii::digit1;
474 /// fn parser<'s>(input: &mut &'s str) -> PResult<u32, InputError<&'s str>> {
475 /// digit1.try_map(str::parse).parse_next(input)
476 /// }
477 ///
478 /// assert_eq!(parser.parse_peek("416"), Ok(("", 416)));
479 /// assert_eq!(parser.parse_peek("12b"), Ok(("b", 12)));
480 /// assert!(parser.parse_peek("b").is_err());
481 /// ```
482 #[inline(always)]
digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar,483 pub fn digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
484 where
485 I: StreamIsPartial,
486 I: Stream,
487 <I as Stream>::Token: AsChar,
488 {
489 trace("digit1", take_while(1.., AsChar::is_dec_digit)).parse_next(input)
490 }
491
492 /// Recognizes zero or more ASCII hexadecimal numerical characters: `'0'..='9'`, `'A'..='F'`,
493 /// `'a'..='f'`
494 ///
495 /// *Complete version*: Will return the whole input if no terminating token is found (a non hexadecimal digit character).
496 ///
497 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
498 /// or if no terminating token is found (a non hexadecimal digit character).
499 ///
500 /// # Example
501 ///
502 /// ```
503 /// # use winnow::prelude::*;
504 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
505 /// # use winnow::ascii::hex_digit0;
506 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
507 /// hex_digit0.parse_next(input)
508 /// }
509 ///
510 /// assert_eq!(parser.parse_peek("21cZ"), Ok(("Z", "21c")));
511 /// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", "")));
512 /// assert_eq!(parser.parse_peek(""), Ok(("", "")));
513 /// ```
514 ///
515 /// ```
516 /// # use winnow::prelude::*;
517 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
518 /// # use winnow::Partial;
519 /// # use winnow::ascii::hex_digit0;
520 /// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("Z"), "21c")));
521 /// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), "")));
522 /// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
523 /// ```
524 #[inline(always)]
hex_digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar,525 pub fn hex_digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
526 where
527 I: StreamIsPartial,
528 I: Stream,
529 <I as Stream>::Token: AsChar,
530 {
531 trace("hex_digit0", take_while(0.., AsChar::is_hex_digit)).parse_next(input)
532 }
533
534 /// Recognizes one or more ASCII hexadecimal numerical characters: `'0'..='9'`, `'A'..='F'`,
535 /// `'a'..='f'`
536 ///
537 /// *Complete version*: Will return an error if there's not enough input data,
538 /// or the whole input if no terminating token is found (a non hexadecimal digit character).
539 ///
540 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
541 /// or if no terminating token is found (a non hexadecimal digit character).
542 ///
543 /// # Example
544 ///
545 /// ```
546 /// # use winnow::prelude::*;
547 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
548 /// # use winnow::ascii::hex_digit1;
549 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
550 /// hex_digit1.parse_next(input)
551 /// }
552 ///
553 /// assert_eq!(parser.parse_peek("21cZ"), Ok(("Z", "21c")));
554 /// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice))));
555 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
556 /// ```
557 ///
558 /// ```
559 /// # use winnow::prelude::*;
560 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
561 /// # use winnow::Partial;
562 /// # use winnow::ascii::hex_digit1;
563 /// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("Z"), "21c")));
564 /// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice))));
565 /// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
566 /// ```
567 #[inline(always)]
hex_digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar,568 pub fn hex_digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
569 where
570 I: StreamIsPartial,
571 I: Stream,
572 <I as Stream>::Token: AsChar,
573 {
574 trace("hex_digit1", take_while(1.., AsChar::is_hex_digit)).parse_next(input)
575 }
576
577 /// Recognizes zero or more octal characters: `'0'..='7'`
578 ///
579 /// *Complete version*: Will return the whole input if no terminating token is found (a non octal
580 /// digit character).
581 ///
582 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
583 /// or if no terminating token is found (a non octal digit character).
584 ///
585 /// # Example
586 ///
587 /// ```
588 /// # use winnow::prelude::*;
589 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
590 /// # use winnow::ascii::oct_digit0;
591 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
592 /// oct_digit0.parse_next(input)
593 /// }
594 ///
595 /// assert_eq!(parser.parse_peek("21cZ"), Ok(("cZ", "21")));
596 /// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", "")));
597 /// assert_eq!(parser.parse_peek(""), Ok(("", "")));
598 /// ```
599 ///
600 /// ```
601 /// # use winnow::prelude::*;
602 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
603 /// # use winnow::Partial;
604 /// # use winnow::ascii::oct_digit0;
605 /// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("cZ"), "21")));
606 /// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), "")));
607 /// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
608 /// ```
609 #[inline(always)]
oct_digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar,610 pub fn oct_digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
611 where
612 I: StreamIsPartial,
613 I: Stream,
614 <I as Stream>::Token: AsChar,
615 {
616 trace("oct_digit0", take_while(0.., AsChar::is_oct_digit)).parse_next(input)
617 }
618
619 /// Recognizes one or more octal characters: `'0'..='7'`
620 ///
621 /// *Complete version*: Will return an error if there's not enough input data,
622 /// or the whole input if no terminating token is found (a non octal digit character).
623 ///
624 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
625 /// or if no terminating token is found (a non octal digit character).
626 ///
627 /// # Example
628 ///
629 /// ```
630 /// # use winnow::prelude::*;
631 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
632 /// # use winnow::ascii::oct_digit1;
633 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
634 /// oct_digit1.parse_next(input)
635 /// }
636 ///
637 /// assert_eq!(parser.parse_peek("21cZ"), Ok(("cZ", "21")));
638 /// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice))));
639 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
640 /// ```
641 ///
642 /// ```
643 /// # use winnow::prelude::*;
644 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
645 /// # use winnow::Partial;
646 /// # use winnow::ascii::oct_digit1;
647 /// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("cZ"), "21")));
648 /// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice))));
649 /// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
650 /// ```
651 #[inline(always)]
oct_digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar,652 pub fn oct_digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
653 where
654 I: StreamIsPartial,
655 I: Stream,
656 <I as Stream>::Token: AsChar,
657 {
658 trace("oct_digit0", take_while(1.., AsChar::is_oct_digit)).parse_next(input)
659 }
660
661 /// Recognizes zero or more ASCII numerical and alphabetic characters: `'a'..='z'`, `'A'..='Z'`, `'0'..='9'`
662 ///
663 /// *Complete version*: Will return the whole input if no terminating token is found (a non
664 /// alphanumerical character).
665 ///
666 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
667 /// or if no terminating token is found (a non alphanumerical character).
668 ///
669 /// # Example
670 ///
671 /// ```
672 /// # use winnow::prelude::*;
673 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
674 /// # use winnow::ascii::alphanumeric0;
675 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
676 /// alphanumeric0.parse_next(input)
677 /// }
678 ///
679 /// assert_eq!(parser.parse_peek("21cZ%1"), Ok(("%1", "21cZ")));
680 /// assert_eq!(parser.parse_peek("&Z21c"), Ok(("&Z21c", "")));
681 /// assert_eq!(parser.parse_peek(""), Ok(("", "")));
682 /// ```
683 ///
684 /// ```
685 /// # use winnow::prelude::*;
686 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
687 /// # use winnow::Partial;
688 /// # use winnow::ascii::alphanumeric0;
689 /// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("21cZ%1")), Ok((Partial::new("%1"), "21cZ")));
690 /// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("&Z21c")), Ok((Partial::new("&Z21c"), "")));
691 /// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
692 /// ```
693 #[inline(always)]
alphanumeric0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar,694 pub fn alphanumeric0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
695 where
696 I: StreamIsPartial,
697 I: Stream,
698 <I as Stream>::Token: AsChar,
699 {
700 trace("alphanumeric0", take_while(0.., AsChar::is_alphanum)).parse_next(input)
701 }
702
703 /// Recognizes one or more ASCII numerical and alphabetic characters: `'a'..='z'`, `'A'..='Z'`, `'0'..='9'`
704 ///
705 /// *Complete version*: Will return an error if there's not enough input data,
706 /// or the whole input if no terminating token is found (a non alphanumerical character).
707 ///
708 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
709 /// or if no terminating token is found (a non alphanumerical character).
710 ///
711 /// # Example
712 ///
713 /// ```
714 /// # use winnow::prelude::*;
715 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
716 /// # use winnow::ascii::alphanumeric1;
717 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
718 /// alphanumeric1.parse_next(input)
719 /// }
720 ///
721 /// assert_eq!(parser.parse_peek("21cZ%1"), Ok(("%1", "21cZ")));
722 /// assert_eq!(parser.parse_peek("&H2"), Err(ErrMode::Backtrack(InputError::new("&H2", ErrorKind::Slice))));
723 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
724 /// ```
725 ///
726 /// ```
727 /// # use winnow::prelude::*;
728 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
729 /// # use winnow::Partial;
730 /// # use winnow::ascii::alphanumeric1;
731 /// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("21cZ%1")), Ok((Partial::new("%1"), "21cZ")));
732 /// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("&H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("&H2"), ErrorKind::Slice))));
733 /// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
734 /// ```
735 #[inline(always)]
alphanumeric1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar,736 pub fn alphanumeric1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
737 where
738 I: StreamIsPartial,
739 I: Stream,
740 <I as Stream>::Token: AsChar,
741 {
742 trace("alphanumeric1", take_while(1.., AsChar::is_alphanum)).parse_next(input)
743 }
744
745 /// Recognizes zero or more spaces and tabs.
746 ///
747 /// *Complete version*: Will return the whole input if no terminating token is found (a non space
748 /// character).
749 ///
750 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
751 /// or if no terminating token is found (a non space character).
752 ///
753 /// # Example
754 ///
755 /// ```
756 /// # use winnow::prelude::*;
757 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
758 /// # use winnow::Partial;
759 /// # use winnow::ascii::space0;
760 /// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new(" \t21c")), Ok((Partial::new("21c"), " \t")));
761 /// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), "")));
762 /// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
763 /// ```
764 #[inline(always)]
space0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar + Clone,765 pub fn space0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
766 where
767 I: StreamIsPartial,
768 I: Stream,
769 <I as Stream>::Token: AsChar + Clone,
770 {
771 trace("space0", take_while(0.., AsChar::is_space)).parse_next(input)
772 }
773
774 /// Recognizes zero or more spaces and tabs.
775 ///
776 /// *Complete version*: Will return the whole input if no terminating token is found (a non space
777 /// character).
778 ///
779 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
780 /// or if no terminating token is found (a non space character).
781 ///
782 /// # Example
783 ///
784 /// ```
785 /// # use winnow::prelude::*;
786 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
787 /// # use winnow::ascii::space1;
788 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
789 /// space1.parse_next(input)
790 /// }
791 ///
792 /// assert_eq!(parser.parse_peek(" \t21c"), Ok(("21c", " \t")));
793 /// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice))));
794 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
795 /// ```
796 ///
797 /// ```
798 /// # use winnow::prelude::*;
799 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
800 /// # use winnow::Partial;
801 /// # use winnow::ascii::space1;
802 /// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new(" \t21c")), Ok((Partial::new("21c"), " \t")));
803 /// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice))));
804 /// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
805 /// ```
806 #[inline(always)]
space1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar + Clone,807 pub fn space1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
808 where
809 I: StreamIsPartial,
810 I: Stream,
811 <I as Stream>::Token: AsChar + Clone,
812 {
813 trace("space1", take_while(1.., AsChar::is_space)).parse_next(input)
814 }
815
816 /// Recognizes zero or more spaces, tabs, carriage returns and line feeds.
817 ///
818 /// *Complete version*: will return the whole input if no terminating token is found (a non space
819 /// character).
820 ///
821 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
822 /// or if no terminating token is found (a non space character).
823 ///
824 /// # Example
825 ///
826 /// ```
827 /// # use winnow::prelude::*;
828 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
829 /// # use winnow::ascii::multispace0;
830 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
831 /// multispace0.parse_next(input)
832 /// }
833 ///
834 /// assert_eq!(parser.parse_peek(" \t\n\r21c"), Ok(("21c", " \t\n\r")));
835 /// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", "")));
836 /// assert_eq!(parser.parse_peek(""), Ok(("", "")));
837 /// ```
838 ///
839 /// ```
840 /// # use winnow::prelude::*;
841 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
842 /// # use winnow::Partial;
843 /// # use winnow::ascii::multispace0;
844 /// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new(" \t\n\r21c")), Ok((Partial::new("21c"), " \t\n\r")));
845 /// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), "")));
846 /// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
847 /// ```
848 #[inline(always)]
multispace0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar + Clone,849 pub fn multispace0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
850 where
851 I: StreamIsPartial,
852 I: Stream,
853 <I as Stream>::Token: AsChar + Clone,
854 {
855 trace("multispace0", take_while(0.., (' ', '\t', '\r', '\n'))).parse_next(input)
856 }
857
858 /// Recognizes one or more spaces, tabs, carriage returns and line feeds.
859 ///
860 /// *Complete version*: will return an error if there's not enough input data,
861 /// or the whole input if no terminating token is found (a non space character).
862 ///
863 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
864 /// or if no terminating token is found (a non space character).
865 ///
866 /// # Example
867 ///
868 /// ```
869 /// # use winnow::prelude::*;
870 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
871 /// # use winnow::ascii::multispace1;
872 /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
873 /// multispace1.parse_next(input)
874 /// }
875 ///
876 /// assert_eq!(parser.parse_peek(" \t\n\r21c"), Ok(("21c", " \t\n\r")));
877 /// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice))));
878 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
879 /// ```
880 ///
881 /// ```
882 /// # use winnow::prelude::*;
883 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
884 /// # use winnow::Partial;
885 /// # use winnow::ascii::multispace1;
886 /// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new(" \t\n\r21c")), Ok((Partial::new("21c"), " \t\n\r")));
887 /// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice))));
888 /// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
889 /// ```
890 #[inline(always)]
multispace1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar + Clone,891 pub fn multispace1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
892 where
893 I: StreamIsPartial,
894 I: Stream,
895 <I as Stream>::Token: AsChar + Clone,
896 {
897 trace("multispace1", take_while(1.., (' ', '\t', '\r', '\n'))).parse_next(input)
898 }
899
900 /// Decode a decimal unsigned integer (e.g. [`u32`])
901 ///
902 /// *Complete version*: can parse until the end of input.
903 ///
904 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
905 #[doc(alias = "u8")]
906 #[doc(alias = "u16")]
907 #[doc(alias = "u32")]
908 #[doc(alias = "u64")]
909 #[doc(alias = "u128")]
dec_uint<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar + Clone, O: Uint,910 pub fn dec_uint<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
911 where
912 I: StreamIsPartial,
913 I: Stream,
914 <I as Stream>::Token: AsChar + Clone,
915 O: Uint,
916 {
917 trace("dec_uint", move |input: &mut I| {
918 if input.eof_offset() == 0 {
919 if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
920 return Err(ErrMode::Incomplete(Needed::new(1)));
921 } else {
922 return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
923 }
924 }
925
926 let mut value = O::default();
927 for (offset, c) in input.iter_offsets() {
928 match c.as_char().to_digit(10) {
929 Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| {
930 let d = d as u8;
931 v.checked_add(d, sealed::SealedMarker)
932 }) {
933 None => return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)),
934 Some(v) => value = v,
935 },
936 None => {
937 if offset == 0 {
938 return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
939 } else {
940 let _ = input.next_slice(offset);
941 return Ok(value);
942 }
943 }
944 }
945 }
946
947 if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
948 Err(ErrMode::Incomplete(Needed::new(1)))
949 } else {
950 let _ = input.finish();
951 Ok(value)
952 }
953 })
954 .parse_next(input)
955 }
956
957 /// Metadata for parsing unsigned integers, see [`dec_uint`]
958 pub trait Uint: Default {
959 #[doc(hidden)]
checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>960 fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
961 #[doc(hidden)]
checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>962 fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
963 }
964
965 impl Uint for u8 {
checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>966 fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
967 self.checked_mul(by as Self)
968 }
checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>969 fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
970 self.checked_add(by as Self)
971 }
972 }
973
974 impl Uint for u16 {
checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>975 fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
976 self.checked_mul(by as Self)
977 }
checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>978 fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
979 self.checked_add(by as Self)
980 }
981 }
982
983 impl Uint for u32 {
checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>984 fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
985 self.checked_mul(by as Self)
986 }
checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>987 fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
988 self.checked_add(by as Self)
989 }
990 }
991
992 impl Uint for u64 {
checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>993 fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
994 self.checked_mul(by as Self)
995 }
checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>996 fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
997 self.checked_add(by as Self)
998 }
999 }
1000
1001 impl Uint for u128 {
checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1002 fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1003 self.checked_mul(by as Self)
1004 }
checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1005 fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1006 self.checked_add(by as Self)
1007 }
1008 }
1009
1010 /// Deprecated since v0.5.17
1011 impl Uint for i8 {
checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1012 fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1013 self.checked_mul(by as Self)
1014 }
checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1015 fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1016 self.checked_add(by as Self)
1017 }
1018 }
1019
1020 /// Deprecated since v0.5.17
1021 impl Uint for i16 {
checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1022 fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1023 self.checked_mul(by as Self)
1024 }
checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1025 fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1026 self.checked_add(by as Self)
1027 }
1028 }
1029
1030 /// Deprecated since v0.5.17
1031 impl Uint for i32 {
checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1032 fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1033 self.checked_mul(by as Self)
1034 }
checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1035 fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1036 self.checked_add(by as Self)
1037 }
1038 }
1039
1040 /// Deprecated since v0.5.17
1041 impl Uint for i64 {
checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1042 fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1043 self.checked_mul(by as Self)
1044 }
checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1045 fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1046 self.checked_add(by as Self)
1047 }
1048 }
1049
1050 /// Deprecated since v0.5.17
1051 impl Uint for i128 {
checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1052 fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1053 self.checked_mul(by as Self)
1054 }
checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1055 fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1056 self.checked_add(by as Self)
1057 }
1058 }
1059
1060 /// Decode a decimal signed integer (e.g. [`i32`])
1061 ///
1062 /// *Complete version*: can parse until the end of input.
1063 ///
1064 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
1065 #[doc(alias = "i8")]
1066 #[doc(alias = "i16")]
1067 #[doc(alias = "i32")]
1068 #[doc(alias = "i64")]
1069 #[doc(alias = "i128")]
dec_int<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar + Clone, O: Int,1070 pub fn dec_int<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
1071 where
1072 I: StreamIsPartial,
1073 I: Stream,
1074 <I as Stream>::Token: AsChar + Clone,
1075 O: Int,
1076 {
1077 trace("dec_int", move |input: &mut I| {
1078 fn sign(token: impl AsChar) -> bool {
1079 let token = token.as_char();
1080 token == '+' || token == '-'
1081 }
1082 let sign = opt(crate::token::one_of(sign).map(AsChar::as_char))
1083 .map(|c| c != Some('-'))
1084 .parse_next(input)?;
1085
1086 if input.eof_offset() == 0 {
1087 if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
1088 return Err(ErrMode::Incomplete(Needed::new(1)));
1089 } else {
1090 return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
1091 }
1092 }
1093
1094 let mut value = O::default();
1095 for (offset, c) in input.iter_offsets() {
1096 match c.as_char().to_digit(10) {
1097 Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| {
1098 let d = d as u8;
1099 if sign {
1100 v.checked_add(d, sealed::SealedMarker)
1101 } else {
1102 v.checked_sub(d, sealed::SealedMarker)
1103 }
1104 }) {
1105 None => return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)),
1106 Some(v) => value = v,
1107 },
1108 None => {
1109 if offset == 0 {
1110 return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
1111 } else {
1112 let _ = input.next_slice(offset);
1113 return Ok(value);
1114 }
1115 }
1116 }
1117 }
1118
1119 if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
1120 Err(ErrMode::Incomplete(Needed::new(1)))
1121 } else {
1122 let _ = input.finish();
1123 Ok(value)
1124 }
1125 })
1126 .parse_next(input)
1127 }
1128
1129 /// Metadata for parsing signed integers, see [`dec_int`]
1130 pub trait Int: Uint {
1131 #[doc(hidden)]
checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1132 fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
1133 }
1134
1135 impl Int for i8 {
checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1136 fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1137 self.checked_sub(by as Self)
1138 }
1139 }
1140
1141 impl Int for i16 {
checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1142 fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1143 self.checked_sub(by as Self)
1144 }
1145 }
1146
1147 impl Int for i32 {
checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1148 fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1149 self.checked_sub(by as Self)
1150 }
1151 }
1152
1153 impl Int for i64 {
checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1154 fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1155 self.checked_sub(by as Self)
1156 }
1157 }
1158
1159 impl Int for i128 {
checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>1160 fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
1161 self.checked_sub(by as Self)
1162 }
1163 }
1164
1165 /// Decode a variable-width hexadecimal integer (e.g. [`u32`])
1166 ///
1167 /// *Complete version*: Will parse until the end of input if it has fewer characters than the type
1168 /// supports.
1169 ///
1170 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if end-of-input
1171 /// is hit before a hard boundary (non-hex character, more characters than supported).
1172 ///
1173 /// # Example
1174 ///
1175 /// ```rust
1176 /// # use winnow::prelude::*;
1177 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError};
1178 /// use winnow::ascii::hex_uint;
1179 ///
1180 /// fn parser<'s>(s: &mut &'s [u8]) -> PResult<u32, InputError<&'s [u8]>> {
1181 /// hex_uint(s)
1182 /// }
1183 ///
1184 /// assert_eq!(parser.parse_peek(&b"01AE"[..]), Ok((&b""[..], 0x01AE)));
1185 /// assert_eq!(parser.parse_peek(&b"abc"[..]), Ok((&b""[..], 0x0ABC)));
1186 /// assert_eq!(parser.parse_peek(&b"ggg"[..]), Err(ErrMode::Backtrack(InputError::new(&b"ggg"[..], ErrorKind::Slice))));
1187 /// ```
1188 ///
1189 /// ```rust
1190 /// # use winnow::prelude::*;
1191 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
1192 /// # use winnow::Partial;
1193 /// use winnow::ascii::hex_uint;
1194 ///
1195 /// fn parser<'s>(s: &mut Partial<&'s [u8]>) -> PResult<u32, InputError<Partial<&'s [u8]>>> {
1196 /// hex_uint(s)
1197 /// }
1198 ///
1199 /// assert_eq!(parser.parse_peek(Partial::new(&b"01AE;"[..])), Ok((Partial::new(&b";"[..]), 0x01AE)));
1200 /// assert_eq!(parser.parse_peek(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
1201 /// assert_eq!(parser.parse_peek(Partial::new(&b"ggg"[..])), Err(ErrMode::Backtrack(InputError::new(Partial::new(&b"ggg"[..]), ErrorKind::Slice))));
1202 /// ```
1203 #[inline]
hex_uint<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E> where I: StreamIsPartial, I: Stream, O: HexUint, <I as Stream>::Token: AsChar, <I as Stream>::Slice: AsBStr,1204 pub fn hex_uint<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
1205 where
1206 I: StreamIsPartial,
1207 I: Stream,
1208 O: HexUint,
1209 <I as Stream>::Token: AsChar,
1210 <I as Stream>::Slice: AsBStr,
1211 {
1212 trace("hex_uint", move |input: &mut I| {
1213 let invalid_offset = input
1214 .offset_for(|c| {
1215 let c = c.as_char();
1216 !"0123456789abcdefABCDEF".contains(c)
1217 })
1218 .unwrap_or_else(|| input.eof_offset());
1219 let max_nibbles = O::max_nibbles(sealed::SealedMarker);
1220 let max_offset = input.offset_at(max_nibbles);
1221 let offset = match max_offset {
1222 Ok(max_offset) => {
1223 if max_offset < invalid_offset {
1224 // Overflow
1225 return Err(ErrMode::from_error_kind(input, ErrorKind::Verify));
1226 } else {
1227 invalid_offset
1228 }
1229 }
1230 Err(_) => {
1231 if <I as StreamIsPartial>::is_partial_supported()
1232 && input.is_partial()
1233 && invalid_offset == input.eof_offset()
1234 {
1235 // Only the next byte is guaranteed required
1236 return Err(ErrMode::Incomplete(Needed::new(1)));
1237 } else {
1238 invalid_offset
1239 }
1240 }
1241 };
1242 if offset == 0 {
1243 // Must be at least one digit
1244 return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
1245 }
1246 let parsed = input.next_slice(offset);
1247
1248 let mut res = O::default();
1249 for c in parsed.as_bstr() {
1250 let nibble = *c as char;
1251 let nibble = nibble.to_digit(16).unwrap_or(0) as u8;
1252 let nibble = O::from(nibble);
1253 res = (res << O::from(4)) + nibble;
1254 }
1255
1256 Ok(res)
1257 })
1258 .parse_next(input)
1259 }
1260
1261 /// Metadata for parsing hex numbers, see [`hex_uint`]
1262 pub trait HexUint:
1263 Default + Shl<Self, Output = Self> + Add<Self, Output = Self> + From<u8>
1264 {
1265 #[doc(hidden)]
max_nibbles(_: sealed::SealedMarker) -> usize1266 fn max_nibbles(_: sealed::SealedMarker) -> usize;
1267 }
1268
1269 impl HexUint for u8 {
1270 #[inline(always)]
max_nibbles(_: sealed::SealedMarker) -> usize1271 fn max_nibbles(_: sealed::SealedMarker) -> usize {
1272 2
1273 }
1274 }
1275
1276 impl HexUint for u16 {
1277 #[inline(always)]
max_nibbles(_: sealed::SealedMarker) -> usize1278 fn max_nibbles(_: sealed::SealedMarker) -> usize {
1279 4
1280 }
1281 }
1282
1283 impl HexUint for u32 {
1284 #[inline(always)]
max_nibbles(_: sealed::SealedMarker) -> usize1285 fn max_nibbles(_: sealed::SealedMarker) -> usize {
1286 8
1287 }
1288 }
1289
1290 impl HexUint for u64 {
1291 #[inline(always)]
max_nibbles(_: sealed::SealedMarker) -> usize1292 fn max_nibbles(_: sealed::SealedMarker) -> usize {
1293 16
1294 }
1295 }
1296
1297 impl HexUint for u128 {
1298 #[inline(always)]
max_nibbles(_: sealed::SealedMarker) -> usize1299 fn max_nibbles(_: sealed::SealedMarker) -> usize {
1300 32
1301 }
1302 }
1303
1304 /// Recognizes floating point number in text format and returns a [`f32`] or [`f64`].
1305 ///
1306 /// *Complete version*: Can parse until the end of input.
1307 ///
1308 /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
1309 ///
1310 /// # Example
1311 ///
1312 /// ```rust
1313 /// # use winnow::prelude::*;
1314 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
1315 /// # use winnow::error::Needed::Size;
1316 /// use winnow::ascii::float;
1317 ///
1318 /// fn parser<'s>(s: &mut &'s str) -> PResult<f64, InputError<&'s str>> {
1319 /// float(s)
1320 /// }
1321 ///
1322 /// assert_eq!(parser.parse_peek("11e-1"), Ok(("", 1.1)));
1323 /// assert_eq!(parser.parse_peek("123E-02"), Ok(("", 1.23)));
1324 /// assert_eq!(parser.parse_peek("123K-01"), Ok(("K-01", 123.0)));
1325 /// assert_eq!(parser.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Tag))));
1326 /// ```
1327 ///
1328 /// ```rust
1329 /// # use winnow::prelude::*;
1330 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
1331 /// # use winnow::error::Needed::Size;
1332 /// # use winnow::Partial;
1333 /// use winnow::ascii::float;
1334 ///
1335 /// fn parser<'s>(s: &mut Partial<&'s str>) -> PResult<f64, InputError<Partial<&'s str>>> {
1336 /// float(s)
1337 /// }
1338 ///
1339 /// assert_eq!(parser.parse_peek(Partial::new("11e-1 ")), Ok((Partial::new(" "), 1.1)));
1340 /// assert_eq!(parser.parse_peek(Partial::new("11e-1")), Err(ErrMode::Incomplete(Needed::new(1))));
1341 /// assert_eq!(parser.parse_peek(Partial::new("123E-02")), Err(ErrMode::Incomplete(Needed::new(1))));
1342 /// assert_eq!(parser.parse_peek(Partial::new("123K-01")), Ok((Partial::new("K-01"), 123.0)));
1343 /// assert_eq!(parser.parse_peek(Partial::new("abc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("abc"), ErrorKind::Tag))));
1344 /// ```
1345 #[inline(always)]
1346 #[doc(alias = "f32")]
1347 #[doc(alias = "double")]
1348 #[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
float<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, <I as Stream>::Slice: ParseSlice<O>, <I as Stream>::Token: AsChar + Clone, <I as Stream>::IterOffsets: Clone, I: AsBStr,1349 pub fn float<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
1350 where
1351 I: StreamIsPartial,
1352 I: Stream,
1353 I: Compare<&'static str>,
1354 <I as Stream>::Slice: ParseSlice<O>,
1355 <I as Stream>::Token: AsChar + Clone,
1356 <I as Stream>::IterOffsets: Clone,
1357 I: AsBStr,
1358 {
1359 trace("float", move |input: &mut I| {
1360 let s = recognize_float_or_exceptions(input)?;
1361 s.parse_slice()
1362 .ok_or_else(|| ErrMode::from_error_kind(input, ErrorKind::Verify))
1363 })
1364 .parse_next(input)
1365 }
1366
1367 #[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
1368 #[allow(deprecated)]
recognize_float_or_exceptions<I, E: ParserError<I>>( input: &mut I, ) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, <I as Stream>::Token: AsChar + Clone, <I as Stream>::IterOffsets: Clone, I: AsBStr,1369 fn recognize_float_or_exceptions<I, E: ParserError<I>>(
1370 input: &mut I,
1371 ) -> PResult<<I as Stream>::Slice, E>
1372 where
1373 I: StreamIsPartial,
1374 I: Stream,
1375 I: Compare<&'static str>,
1376 <I as Stream>::Token: AsChar + Clone,
1377 <I as Stream>::IterOffsets: Clone,
1378 I: AsBStr,
1379 {
1380 alt((
1381 recognize_float,
1382 crate::token::tag_no_case("nan"),
1383 (
1384 opt(one_of(['+', '-'])),
1385 crate::token::tag_no_case("infinity"),
1386 )
1387 .recognize(),
1388 (opt(one_of(['+', '-'])), crate::token::tag_no_case("inf")).recognize(),
1389 ))
1390 .parse_next(input)
1391 }
1392
1393 #[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
recognize_float<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, <I as Stream>::Token: AsChar + Clone, <I as Stream>::IterOffsets: Clone, I: AsBStr,1394 fn recognize_float<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
1395 where
1396 I: StreamIsPartial,
1397 I: Stream,
1398 I: Compare<&'static str>,
1399 <I as Stream>::Token: AsChar + Clone,
1400 <I as Stream>::IterOffsets: Clone,
1401 I: AsBStr,
1402 {
1403 (
1404 opt(one_of(['+', '-'])),
1405 alt((
1406 (digit1, opt(('.', opt(digit1)))).map(|_| ()),
1407 ('.', digit1).map(|_| ()),
1408 )),
1409 opt((one_of(['e', 'E']), opt(one_of(['+', '-'])), cut_err(digit1))),
1410 )
1411 .recognize()
1412 .parse_next(input)
1413 }
1414
1415 /// Matches a byte string with escaped characters.
1416 ///
1417 /// * The first argument matches the normal characters (it must not accept the control character)
1418 /// * The second argument is the control character (like `\` in most languages)
1419 /// * The third argument matches the escaped characters
1420 ///
1421 /// # Example
1422 ///
1423 /// ```rust
1424 /// # use winnow::prelude::*;
1425 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed, IResult};
1426 /// # use winnow::ascii::digit1;
1427 /// # use winnow::prelude::*;
1428 /// use winnow::ascii::escaped;
1429 /// use winnow::token::one_of;
1430 ///
1431 /// fn esc(s: &str) -> IResult<&str, &str> {
1432 /// escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s)
1433 /// }
1434 ///
1435 /// assert_eq!(esc("123;"), Ok((";", "123")));
1436 /// assert_eq!(esc(r#"12\"34;"#), Ok((";", r#"12\"34"#)));
1437 /// ```
1438 ///
1439 /// ```rust
1440 /// # use winnow::prelude::*;
1441 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed, IResult};
1442 /// # use winnow::ascii::digit1;
1443 /// # use winnow::prelude::*;
1444 /// # use winnow::Partial;
1445 /// use winnow::ascii::escaped;
1446 /// use winnow::token::one_of;
1447 ///
1448 /// fn esc(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
1449 /// escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s)
1450 /// }
1451 ///
1452 /// assert_eq!(esc(Partial::new("123;")), Ok((Partial::new(";"), "123")));
1453 /// assert_eq!(esc(Partial::new("12\\\"34;")), Ok((Partial::new(";"), "12\\\"34")));
1454 /// ```
1455 #[inline(always)]
escaped<'a, I: 'a, Error, F, G, O1, O2>( mut normal: F, control_char: char, mut escapable: G, ) -> impl Parser<I, <I as Stream>::Slice, Error> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar + Clone, F: Parser<I, O1, Error>, G: Parser<I, O2, Error>, Error: ParserError<I>,1456 pub fn escaped<'a, I: 'a, Error, F, G, O1, O2>(
1457 mut normal: F,
1458 control_char: char,
1459 mut escapable: G,
1460 ) -> impl Parser<I, <I as Stream>::Slice, Error>
1461 where
1462 I: StreamIsPartial,
1463 I: Stream,
1464 <I as Stream>::Token: AsChar + Clone,
1465 F: Parser<I, O1, Error>,
1466 G: Parser<I, O2, Error>,
1467 Error: ParserError<I>,
1468 {
1469 trace("escaped", move |input: &mut I| {
1470 if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
1471 streaming_escaped_internal(input, &mut normal, control_char, &mut escapable)
1472 } else {
1473 complete_escaped_internal(input, &mut normal, control_char, &mut escapable)
1474 }
1475 })
1476 }
1477
streaming_escaped_internal<I, Error, F, G, O1, O2>( input: &mut I, normal: &mut F, control_char: char, escapable: &mut G, ) -> PResult<<I as Stream>::Slice, Error> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: AsChar + Clone, F: Parser<I, O1, Error>, G: Parser<I, O2, Error>, Error: ParserError<I>,1478 fn streaming_escaped_internal<I, Error, F, G, O1, O2>(
1479 input: &mut I,
1480 normal: &mut F,
1481 control_char: char,
1482 escapable: &mut G,
1483 ) -> PResult<<I as Stream>::Slice, Error>
1484 where
1485 I: StreamIsPartial,
1486 I: Stream,
1487 <I as Stream>::Token: AsChar + Clone,
1488 F: Parser<I, O1, Error>,
1489 G: Parser<I, O2, Error>,
1490 Error: ParserError<I>,
1491 {
1492 let start = input.checkpoint();
1493
1494 while input.eof_offset() > 0 {
1495 let current_len = input.eof_offset();
1496
1497 match opt(normal.by_ref()).parse_next(input)? {
1498 Some(_) => {
1499 if input.eof_offset() == current_len {
1500 let offset = input.offset_from(&start);
1501 input.reset(start);
1502 return Ok(input.next_slice(offset));
1503 }
1504 }
1505 None => {
1506 if opt(control_char).parse_next(input)?.is_some() {
1507 let _ = escapable.parse_next(input)?;
1508 } else {
1509 let offset = input.offset_from(&start);
1510 input.reset(start);
1511 return Ok(input.next_slice(offset));
1512 }
1513 }
1514 }
1515 }
1516
1517 Err(ErrMode::Incomplete(Needed::Unknown))
1518 }
1519
complete_escaped_internal<'a, I: 'a, Error, F, G, O1, O2>( input: &mut I, normal: &mut F, control_char: char, escapable: &mut G, ) -> PResult<<I as Stream>::Slice, Error> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: crate::stream::AsChar + Clone, F: Parser<I, O1, Error>, G: Parser<I, O2, Error>, Error: ParserError<I>,1520 fn complete_escaped_internal<'a, I: 'a, Error, F, G, O1, O2>(
1521 input: &mut I,
1522 normal: &mut F,
1523 control_char: char,
1524 escapable: &mut G,
1525 ) -> PResult<<I as Stream>::Slice, Error>
1526 where
1527 I: StreamIsPartial,
1528 I: Stream,
1529 <I as Stream>::Token: crate::stream::AsChar + Clone,
1530 F: Parser<I, O1, Error>,
1531 G: Parser<I, O2, Error>,
1532 Error: ParserError<I>,
1533 {
1534 let start = input.checkpoint();
1535
1536 while input.eof_offset() > 0 {
1537 let current_len = input.eof_offset();
1538
1539 match opt(normal.by_ref()).parse_next(input)? {
1540 Some(_) => {
1541 if input.eof_offset() == current_len {
1542 let offset = input.offset_from(&start);
1543 input.reset(start);
1544 return Ok(input.next_slice(offset));
1545 }
1546 }
1547 None => {
1548 if opt(control_char).parse_next(input)?.is_some() {
1549 let _ = escapable.parse_next(input)?;
1550 } else {
1551 let offset = input.offset_from(&start);
1552 input.reset(start);
1553 return Ok(input.next_slice(offset));
1554 }
1555 }
1556 }
1557 }
1558
1559 input.reset(start);
1560 Ok(input.finish())
1561 }
1562
1563 /// Matches a byte string with escaped characters.
1564 ///
1565 /// * The first argument matches the normal characters (it must not match the control character)
1566 /// * The second argument is the control character (like `\` in most languages)
1567 /// * The third argument matches the escaped characters and transforms them
1568 ///
1569 /// As an example, the chain `abc\tdef` could be `abc def` (it also consumes the control character)
1570 ///
1571 /// # Example
1572 ///
1573 /// ```rust
1574 /// # use winnow::prelude::*;
1575 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
1576 /// # use std::str::from_utf8;
1577 /// use winnow::token::tag;
1578 /// use winnow::ascii::escaped_transform;
1579 /// use winnow::ascii::alpha1;
1580 /// use winnow::combinator::alt;
1581 ///
1582 /// fn parser<'s>(input: &mut &'s str) -> PResult<String, InputError<&'s str>> {
1583 /// escaped_transform(
1584 /// alpha1,
1585 /// '\\',
1586 /// alt((
1587 /// "\\".value("\\"),
1588 /// "\"".value("\""),
1589 /// "n".value("\n"),
1590 /// ))
1591 /// ).parse_next(input)
1592 /// }
1593 ///
1594 /// assert_eq!(parser.parse_peek("ab\\\"cd"), Ok(("", String::from("ab\"cd"))));
1595 /// assert_eq!(parser.parse_peek("ab\\ncd"), Ok(("", String::from("ab\ncd"))));
1596 /// ```
1597 ///
1598 /// ```
1599 /// # use winnow::prelude::*;
1600 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
1601 /// # use std::str::from_utf8;
1602 /// # use winnow::Partial;
1603 /// use winnow::token::tag;
1604 /// use winnow::ascii::escaped_transform;
1605 /// use winnow::ascii::alpha1;
1606 /// use winnow::combinator::alt;
1607 ///
1608 /// fn parser<'s>(input: &mut Partial<&'s str>) -> PResult<String, InputError<Partial<&'s str>>> {
1609 /// escaped_transform(
1610 /// alpha1,
1611 /// '\\',
1612 /// alt((
1613 /// "\\".value("\\"),
1614 /// "\"".value("\""),
1615 /// "n".value("\n"),
1616 /// ))
1617 /// ).parse_next(input)
1618 /// }
1619 ///
1620 /// assert_eq!(parser.parse_peek(Partial::new("ab\\\"cd\"")), Ok((Partial::new("\""), String::from("ab\"cd"))));
1621 /// ```
1622 #[inline(always)]
escaped_transform<I, Error, F, G, Output>( mut normal: F, control_char: char, mut transform: G, ) -> impl Parser<I, Output, Error> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: crate::stream::AsChar + Clone, Output: crate::stream::Accumulate<<I as Stream>::Slice>, F: Parser<I, <I as Stream>::Slice, Error>, G: Parser<I, <I as Stream>::Slice, Error>, Error: ParserError<I>,1623 pub fn escaped_transform<I, Error, F, G, Output>(
1624 mut normal: F,
1625 control_char: char,
1626 mut transform: G,
1627 ) -> impl Parser<I, Output, Error>
1628 where
1629 I: StreamIsPartial,
1630 I: Stream,
1631 <I as Stream>::Token: crate::stream::AsChar + Clone,
1632 Output: crate::stream::Accumulate<<I as Stream>::Slice>,
1633 F: Parser<I, <I as Stream>::Slice, Error>,
1634 G: Parser<I, <I as Stream>::Slice, Error>,
1635 Error: ParserError<I>,
1636 {
1637 trace("escaped_transform", move |input: &mut I| {
1638 if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
1639 streaming_escaped_transform_internal(input, &mut normal, control_char, &mut transform)
1640 } else {
1641 complete_escaped_transform_internal(input, &mut normal, control_char, &mut transform)
1642 }
1643 })
1644 }
1645
streaming_escaped_transform_internal<I, Error, F, G, Output>( input: &mut I, normal: &mut F, control_char: char, transform: &mut G, ) -> PResult<Output, Error> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: crate::stream::AsChar + Clone, Output: crate::stream::Accumulate<<I as Stream>::Slice>, F: Parser<I, <I as Stream>::Slice, Error>, G: Parser<I, <I as Stream>::Slice, Error>, Error: ParserError<I>,1646 fn streaming_escaped_transform_internal<I, Error, F, G, Output>(
1647 input: &mut I,
1648 normal: &mut F,
1649 control_char: char,
1650 transform: &mut G,
1651 ) -> PResult<Output, Error>
1652 where
1653 I: StreamIsPartial,
1654 I: Stream,
1655 <I as Stream>::Token: crate::stream::AsChar + Clone,
1656 Output: crate::stream::Accumulate<<I as Stream>::Slice>,
1657 F: Parser<I, <I as Stream>::Slice, Error>,
1658 G: Parser<I, <I as Stream>::Slice, Error>,
1659 Error: ParserError<I>,
1660 {
1661 let mut res = Output::initial(Some(input.eof_offset()));
1662
1663 while input.eof_offset() > 0 {
1664 let current_len = input.eof_offset();
1665 match opt(normal.by_ref()).parse_next(input)? {
1666 Some(o) => {
1667 res.accumulate(o);
1668 if input.eof_offset() == current_len {
1669 return Ok(res);
1670 }
1671 }
1672 None => {
1673 if opt(control_char).parse_next(input)?.is_some() {
1674 let o = transform.parse_next(input)?;
1675 res.accumulate(o);
1676 } else {
1677 return Ok(res);
1678 }
1679 }
1680 }
1681 }
1682 Err(ErrMode::Incomplete(Needed::Unknown))
1683 }
1684
complete_escaped_transform_internal<I, Error, F, G, Output>( input: &mut I, normal: &mut F, control_char: char, transform: &mut G, ) -> PResult<Output, Error> where I: StreamIsPartial, I: Stream, <I as Stream>::Token: crate::stream::AsChar + Clone, Output: crate::stream::Accumulate<<I as Stream>::Slice>, F: Parser<I, <I as Stream>::Slice, Error>, G: Parser<I, <I as Stream>::Slice, Error>, Error: ParserError<I>,1685 fn complete_escaped_transform_internal<I, Error, F, G, Output>(
1686 input: &mut I,
1687 normal: &mut F,
1688 control_char: char,
1689 transform: &mut G,
1690 ) -> PResult<Output, Error>
1691 where
1692 I: StreamIsPartial,
1693 I: Stream,
1694 <I as Stream>::Token: crate::stream::AsChar + Clone,
1695 Output: crate::stream::Accumulate<<I as Stream>::Slice>,
1696 F: Parser<I, <I as Stream>::Slice, Error>,
1697 G: Parser<I, <I as Stream>::Slice, Error>,
1698 Error: ParserError<I>,
1699 {
1700 let mut res = Output::initial(Some(input.eof_offset()));
1701
1702 while input.eof_offset() > 0 {
1703 let current_len = input.eof_offset();
1704
1705 match opt(normal.by_ref()).parse_next(input)? {
1706 Some(o) => {
1707 res.accumulate(o);
1708 if input.eof_offset() == current_len {
1709 return Ok(res);
1710 }
1711 }
1712 None => {
1713 if opt(control_char).parse_next(input)?.is_some() {
1714 let o = transform.parse_next(input)?;
1715 res.accumulate(o);
1716 } else {
1717 return Ok(res);
1718 }
1719 }
1720 }
1721 }
1722 Ok(res)
1723 }
1724
1725 mod sealed {
1726 pub struct SealedMarker;
1727 }
1728