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