Skip to main content

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                // SAFETY: Only 7-bit ASCII characters are parsed
1096                let s = unsafe { core::str::from_utf8_unchecked(s) };
1097                Output::try_from_dec_uint(s)
1098            })
1099            .parse_next(input)
1100    })
1101    .parse_next(input)
1102}
1103
1104/// Metadata for parsing unsigned integers, see [`dec_uint`]
1105pub trait Uint: Sized {
1106    #[doc(hidden)]
1107    fn try_from_dec_uint(slice: &str) -> Option<Self>;
1108}
1109
1110impl Uint for u8 {
1111    fn try_from_dec_uint(slice: &str) -> Option<Self> {
1112        slice.parse().ok()
1113    }
1114}
1115
1116impl Uint for u16 {
1117    fn try_from_dec_uint(slice: &str) -> Option<Self> {
1118        slice.parse().ok()
1119    }
1120}
1121
1122impl Uint for u32 {
1123    fn try_from_dec_uint(slice: &str) -> Option<Self> {
1124        slice.parse().ok()
1125    }
1126}
1127
1128impl Uint for u64 {
1129    fn try_from_dec_uint(slice: &str) -> Option<Self> {
1130        slice.parse().ok()
1131    }
1132}
1133
1134impl Uint for u128 {
1135    fn try_from_dec_uint(slice: &str) -> Option<Self> {
1136        slice.parse().ok()
1137    }
1138}
1139
1140impl Uint for usize {
1141    fn try_from_dec_uint(slice: &str) -> Option<Self> {
1142        slice.parse().ok()
1143    }
1144}
1145
1146/// Decode a decimal signed integer (e.g. [`i32`])
1147///
1148/// *Complete version*: can parse until the end of input.
1149///
1150/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
1151///
1152/// # Effective Signature
1153///
1154/// Assuming you are parsing a `&str` [Stream] into an `i32`:
1155/// ```rust
1156/// # use winnow::prelude::*;;
1157/// pub fn dec_int(input: &mut &str) -> ModalResult<i32>
1158/// # {
1159/// #     winnow::ascii::dec_int.parse_next(input)
1160/// # }
1161/// ```
1162#[doc(alias = "i8")]
1163#[doc(alias = "i16")]
1164#[doc(alias = "i32")]
1165#[doc(alias = "i64")]
1166#[doc(alias = "i128")]
1167pub fn dec_int<Input, Output, Error>(input: &mut Input) -> Result<Output, Error>
1168where
1169    Input: StreamIsPartial + Stream,
1170    <Input as Stream>::Slice: AsBStr,
1171    <Input as Stream>::Token: AsChar + Clone,
1172    Output: Int,
1173    Error: ParserError<Input>,
1174{
1175    trace("dec_int", move |input: &mut Input| {
1176        let sign = opt(dispatch! {any.map(AsChar::as_char);
1177            '+' => empty.value(true),
1178            '-' => empty.value(false),
1179            _ => fail,
1180        });
1181        alt(((sign, one_of('1'..='9'), digit0).void(), one_of('0').void()))
1182            .take()
1183            .verify_map(|s: <Input as Stream>::Slice| {
1184                let s = s.as_bstr();
1185                // SAFETY: Only 7-bit ASCII characters are parsed
1186                let s = unsafe { core::str::from_utf8_unchecked(s) };
1187                Output::try_from_dec_int(s)
1188            })
1189            .parse_next(input)
1190    })
1191    .parse_next(input)
1192}
1193
1194/// Metadata for parsing signed integers, see [`dec_int`]
1195pub trait Int: Sized {
1196    #[doc(hidden)]
1197    fn try_from_dec_int(slice: &str) -> Option<Self>;
1198}
1199
1200impl Int for i8 {
1201    fn try_from_dec_int(slice: &str) -> Option<Self> {
1202        slice.parse().ok()
1203    }
1204}
1205
1206impl Int for i16 {
1207    fn try_from_dec_int(slice: &str) -> Option<Self> {
1208        slice.parse().ok()
1209    }
1210}
1211
1212impl Int for i32 {
1213    fn try_from_dec_int(slice: &str) -> Option<Self> {
1214        slice.parse().ok()
1215    }
1216}
1217
1218impl Int for i64 {
1219    fn try_from_dec_int(slice: &str) -> Option<Self> {
1220        slice.parse().ok()
1221    }
1222}
1223
1224impl Int for i128 {
1225    fn try_from_dec_int(slice: &str) -> Option<Self> {
1226        slice.parse().ok()
1227    }
1228}
1229
1230impl Int for isize {
1231    fn try_from_dec_int(slice: &str) -> Option<Self> {
1232        slice.parse().ok()
1233    }
1234}
1235
1236/// Decode a variable-width hexadecimal integer (e.g. [`u32`])
1237///
1238/// *Complete version*: Will parse until the end of input if it has fewer characters than the type
1239/// supports.
1240///
1241/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if end-of-input
1242/// is hit before a hard boundary (non-hex character, more characters than supported).
1243///
1244/// # Effective Signature
1245///
1246/// Assuming you are parsing a `&str` [Stream] into a `u32`:
1247/// ```rust
1248/// # use winnow::prelude::*;;
1249/// pub fn hex_uint(input: &mut &str) -> ModalResult<u32>
1250/// # {
1251/// #     winnow::ascii::hex_uint.parse_next(input)
1252/// # }
1253/// ```
1254///
1255/// # Example
1256///
1257/// ```rust
1258/// # use winnow::prelude::*;
1259/// use winnow::ascii::hex_uint;
1260///
1261/// fn parser<'s>(s: &mut &'s [u8]) -> ModalResult<u32> {
1262///   hex_uint(s)
1263/// }
1264///
1265/// assert_eq!(parser.parse_peek(&b"01AE"[..]), Ok((&b""[..], 0x01AE)));
1266/// assert_eq!(parser.parse_peek(&b"abc"[..]), Ok((&b""[..], 0x0ABC)));
1267/// assert!(parser.parse_peek(&b"ggg"[..]).is_err());
1268/// ```
1269///
1270/// ```rust
1271/// # use winnow::prelude::*;
1272/// # use winnow::{error::ErrMode, error::Needed};
1273/// # use winnow::Partial;
1274/// use winnow::ascii::hex_uint;
1275///
1276/// fn parser<'s>(s: &mut Partial<&'s [u8]>) -> ModalResult<u32> {
1277///   hex_uint(s)
1278/// }
1279///
1280/// assert_eq!(parser.parse_peek(Partial::new(&b"01AE;"[..])), Ok((Partial::new(&b";"[..]), 0x01AE)));
1281/// assert_eq!(parser.parse_peek(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
1282/// assert!(parser.parse_peek(Partial::new(&b"ggg"[..])).is_err());
1283/// ```
1284#[inline]
1285pub fn hex_uint<Input, Output, Error>(input: &mut Input) -> Result<Output, Error>
1286where
1287    Input: StreamIsPartial + Stream,
1288    <Input as Stream>::Token: AsChar,
1289    <Input as Stream>::Slice: AsBStr,
1290    Output: HexUint,
1291    Error: ParserError<Input>,
1292{
1293    trace("hex_uint", move |input: &mut Input| {
1294        let invalid_offset = input
1295            .offset_for(|c| !c.is_hex_digit())
1296            .unwrap_or_else(|| input.eof_offset());
1297        let max_nibbles = Output::max_nibbles(sealed::SealedMarker);
1298        let max_offset = input.offset_at(max_nibbles);
1299        let offset = match max_offset {
1300            Ok(max_offset) => {
1301                if max_offset < invalid_offset {
1302                    // Overflow
1303                    return Err(ParserError::from_input(input));
1304                } else {
1305                    invalid_offset
1306                }
1307            }
1308            Err(_) => {
1309                if <Input as StreamIsPartial>::is_partial_supported()
1310                    && input.is_partial()
1311                    && invalid_offset == input.eof_offset()
1312                {
1313                    // Only the next byte is guaranteed required
1314                    return Err(ParserError::incomplete(input, Needed::new(1)));
1315                } else {
1316                    invalid_offset
1317                }
1318            }
1319        };
1320        if offset == 0 {
1321            // Must be at least one digit
1322            return Err(ParserError::from_input(input));
1323        }
1324        let parsed = input.next_slice(offset);
1325
1326        let mut res = Output::default();
1327        for &c in parsed.as_bstr() {
1328            let nibble = match c {
1329                b'0'..=b'9' => c - b'0',
1330                b'a'..=b'f' => c - b'a' + 10,
1331                b'A'..=b'F' => c - b'A' + 10,
1332                _ => unreachable!(),
1333            };
1334            let nibble = Output::from(nibble);
1335            res = (res << Output::from(4)) + nibble;
1336        }
1337
1338        Ok(res)
1339    })
1340    .parse_next(input)
1341}
1342
1343/// Metadata for parsing hex numbers, see [`hex_uint`]
1344pub trait HexUint:
1345    Default + Shl<Self, Output = Self> + Add<Self, Output = Self> + From<u8>
1346{
1347    #[doc(hidden)]
1348    fn max_nibbles(_: sealed::SealedMarker) -> usize;
1349}
1350
1351impl HexUint for u8 {
1352    #[inline(always)]
1353    fn max_nibbles(_: sealed::SealedMarker) -> usize {
1354        2
1355    }
1356}
1357
1358impl HexUint for u16 {
1359    #[inline(always)]
1360    fn max_nibbles(_: sealed::SealedMarker) -> usize {
1361        4
1362    }
1363}
1364
1365impl HexUint for u32 {
1366    #[inline(always)]
1367    fn max_nibbles(_: sealed::SealedMarker) -> usize {
1368        8
1369    }
1370}
1371
1372impl HexUint for u64 {
1373    #[inline(always)]
1374    fn max_nibbles(_: sealed::SealedMarker) -> usize {
1375        16
1376    }
1377}
1378
1379impl HexUint for u128 {
1380    #[inline(always)]
1381    fn max_nibbles(_: sealed::SealedMarker) -> usize {
1382        32
1383    }
1384}
1385
1386/// Recognizes floating point number in text format and returns a [`f32`] or [`f64`].
1387///
1388/// *Complete version*: Can parse until the end of input.
1389///
1390/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
1391///
1392/// # Effective Signature
1393///
1394/// Assuming you are parsing a `&str` [Stream] into an `f64`:
1395/// ```rust
1396/// # use winnow::prelude::*;;
1397/// pub fn float(input: &mut &str) -> ModalResult<f64>
1398/// # {
1399/// #     winnow::ascii::float.parse_next(input)
1400/// # }
1401/// ```
1402///
1403/// # Example
1404///
1405/// ```rust
1406/// # use winnow::prelude::*;
1407/// # use winnow::error::Needed::Size;
1408/// use winnow::ascii::float;
1409///
1410/// fn parser<'s>(s: &mut &'s str) -> ModalResult<f64> {
1411///   float(s)
1412/// }
1413///
1414/// assert_eq!(parser.parse_peek("11e-1"), Ok(("", 1.1)));
1415/// assert_eq!(parser.parse_peek("123E-02"), Ok(("", 1.23)));
1416/// assert_eq!(parser.parse_peek("123K-01"), Ok(("K-01", 123.0)));
1417/// assert!(parser.parse_peek("abc").is_err());
1418/// ```
1419///
1420/// ```rust
1421/// # use winnow::prelude::*;
1422/// # use winnow::{error::ErrMode, error::Needed};
1423/// # use winnow::error::Needed::Size;
1424/// # use winnow::Partial;
1425/// use winnow::ascii::float;
1426///
1427/// fn parser<'s>(s: &mut Partial<&'s str>) -> ModalResult<f64> {
1428///   float(s)
1429/// }
1430///
1431/// assert_eq!(parser.parse_peek(Partial::new("11e-1 ")), Ok((Partial::new(" "), 1.1)));
1432/// assert_eq!(parser.parse_peek(Partial::new("11e-1")), Err(ErrMode::Incomplete(Needed::new(1))));
1433/// assert_eq!(parser.parse_peek(Partial::new("123E-02")), Err(ErrMode::Incomplete(Needed::new(1))));
1434/// assert_eq!(parser.parse_peek(Partial::new("123K-01")), Ok((Partial::new("K-01"), 123.0)));
1435/// assert!(parser.parse_peek(Partial::new("abc")).is_err());
1436/// ```
1437#[inline(always)]
1438#[doc(alias = "f32")]
1439#[doc(alias = "double")]
1440#[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
1441pub fn float<Input, Output, Error>(input: &mut Input) -> Result<Output, Error>
1442where
1443    Input: StreamIsPartial + Stream + Compare<Caseless<&'static str>> + Compare<char> + AsBStr,
1444    <Input as Stream>::Slice: ParseSlice<Output>,
1445    <Input as Stream>::Token: AsChar + Clone,
1446    <Input as Stream>::IterOffsets: Clone,
1447    Error: ParserError<Input>,
1448{
1449    trace("float", move |input: &mut Input| {
1450        let s = take_float_or_exceptions(input)?;
1451        s.parse_slice()
1452            .ok_or_else(|| ParserError::from_input(input))
1453    })
1454    .parse_next(input)
1455}
1456
1457#[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
1458fn take_float_or_exceptions<I, E: ParserError<I>>(input: &mut I) -> Result<<I as Stream>::Slice, E>
1459where
1460    I: StreamIsPartial,
1461    I: Stream,
1462    I: Compare<Caseless<&'static str>>,
1463    I: Compare<char>,
1464    <I as Stream>::Token: AsChar + Clone,
1465    <I as Stream>::IterOffsets: Clone,
1466    I: AsBStr,
1467{
1468    dispatch! {opt(peek(any).map(AsChar::as_char));
1469        Some('N') | Some('n') => Caseless("nan").void(),
1470        Some('+') | Some('-') => (any, take_unsigned_float_or_exceptions).void(),
1471        _ => take_unsigned_float_or_exceptions,
1472    }
1473    .take()
1474    .parse_next(input)
1475}
1476
1477#[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
1478fn take_unsigned_float_or_exceptions<I, E: ParserError<I>>(input: &mut I) -> Result<(), E>
1479where
1480    I: StreamIsPartial,
1481    I: Stream,
1482    I: Compare<Caseless<&'static str>>,
1483    I: Compare<char>,
1484    <I as Stream>::Token: AsChar + Clone,
1485    <I as Stream>::IterOffsets: Clone,
1486    I: AsBStr,
1487{
1488    dispatch! {opt(peek(any).map(AsChar::as_char));
1489        Some('I') | Some('i') => (Caseless("inf"), opt(Caseless("inity"))).void(),
1490        Some('.') => ('.', digit1, take_exp).void(),
1491        _ => (digit1, opt(('.', opt(digit1))), take_exp).void(),
1492    }
1493    .parse_next(input)
1494}
1495
1496#[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
1497fn take_exp<I, E: ParserError<I>>(input: &mut I) -> Result<(), E>
1498where
1499    I: StreamIsPartial,
1500    I: Stream,
1501    I: Compare<char>,
1502    <I as Stream>::Token: AsChar + Clone,
1503    <I as Stream>::IterOffsets: Clone,
1504    I: AsBStr,
1505{
1506    dispatch! {opt(peek(any).map(AsChar::as_char));
1507        Some('E') | Some('e') => (one_of(['e', 'E']), opt(one_of(['+', '-'])), digit1).void(),
1508        _ => empty,
1509    }
1510    .parse_next(input)
1511}
1512
1513/// Recognize the input slice with escaped characters.
1514///
1515/// Arguments:
1516/// - `normal`: unescapeable characters
1517///   - Must not include `control`
1518/// - `control_char`: e.g. `\` for strings in most languages
1519/// - `escape`: parse and transform the escaped character
1520///
1521/// Parsing ends when:
1522/// - `alt(normal, control_char)` [`Backtrack`s][crate::error::ErrMode::Backtrack]
1523/// - `normal` doesn't advance the input stream
1524/// - *(complete)* input stream is exhausted
1525///
1526/// See also [`escaped`]
1527///
1528/// <div class="warning">
1529///
1530/// **Warning:** If the `normal` parser passed to `take_escaped` accepts empty inputs
1531/// (like `alpha0` or `digit0`), `take_escaped` will return an error,
1532/// to prevent going into an infinite loop.
1533///
1534/// </div>
1535///
1536///
1537/// # Example
1538///
1539/// ```rust
1540/// # use winnow::prelude::*;
1541/// # use winnow::ascii::digit1;
1542/// # use winnow::prelude::*;
1543/// use winnow::ascii::take_escaped;
1544/// use winnow::token::one_of;
1545///
1546/// fn esc<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
1547///   take_escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_next(input)
1548/// }
1549///
1550/// assert_eq!(esc.parse_peek("123;"), Ok((";", "123")));
1551/// assert_eq!(esc.parse_peek(r#"12\"34;"#), Ok((";", r#"12\"34"#)));
1552/// ```
1553///
1554/// ```rust
1555/// # use winnow::prelude::*;
1556/// # use winnow::{error::ErrMode, error::Needed};
1557/// # use winnow::ascii::digit1;
1558/// # use winnow::prelude::*;
1559/// # use winnow::Partial;
1560/// use winnow::ascii::take_escaped;
1561/// use winnow::token::one_of;
1562///
1563/// fn esc<'i>(input: &mut Partial<&'i str>) -> ModalResult<&'i str> {
1564///   take_escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_next(input)
1565/// }
1566///
1567/// assert_eq!(esc.parse_peek(Partial::new("123;")), Ok((Partial::new(";"), "123")));
1568/// assert_eq!(esc.parse_peek(Partial::new("12\\\"34;")), Ok((Partial::new(";"), "12\\\"34")));
1569/// ```
1570#[inline(always)]
1571pub fn take_escaped<
1572    Input,
1573    Error,
1574    Normal,
1575    ControlChar,
1576    Escapable,
1577    NormalOutput,
1578    ControlCharOutput,
1579    EscapableOutput,
1580>(
1581    mut normal: Normal,
1582    mut control_char: ControlChar,
1583    mut escapable: Escapable,
1584) -> impl Parser<Input, <Input as Stream>::Slice, Error>
1585where
1586    Input: StreamIsPartial + Stream,
1587    Normal: Parser<Input, NormalOutput, Error>,
1588    ControlChar: Parser<Input, ControlCharOutput, ()>,
1589    Escapable: Parser<Input, EscapableOutput, Error>,
1590    Error: ParserError<Input>,
1591{
1592    trace("take_escaped", move |input: &mut Input| {
1593        if <Input as StreamIsPartial>::is_partial_supported() && input.is_partial() {
1594            escaped_internal::<_, _, _, _, _, _, _, _, true>(
1595                input,
1596                &mut normal,
1597                &mut control_char,
1598                &mut escapable,
1599            )
1600        } else {
1601            escaped_internal::<_, _, _, _, _, _, _, _, false>(
1602                input,
1603                &mut normal,
1604                &mut control_char,
1605                &mut escapable,
1606            )
1607        }
1608    })
1609}
1610
1611fn escaped_internal<I, Error, F, ControlChar, G, O1, O2, O3, const PARTIAL: bool>(
1612    input: &mut I,
1613    normal: &mut F,
1614    control_char: &mut ControlChar,
1615    escapable: &mut G,
1616) -> Result<<I as Stream>::Slice, Error>
1617where
1618    I: StreamIsPartial,
1619    I: Stream,
1620    F: Parser<I, O1, Error>,
1621    ControlChar: Parser<I, O3, ()>,
1622    G: Parser<I, O2, Error>,
1623    Error: ParserError<I>,
1624{
1625    let start = input.checkpoint();
1626
1627    while input.eof_offset() > 0 {
1628        let current_len = input.eof_offset();
1629
1630        match opt(normal.by_ref()).parse_next(input)? {
1631            Some(_) => {
1632                // infinite loop check: the parser must always consume
1633                if input.eof_offset() == current_len {
1634                    return Err(ParserError::assert(
1635                        input,
1636                        "`take_escaped` parsers must always consume",
1637                    ));
1638                }
1639            }
1640            None => {
1641                if control_char.by_ref().parse_next(input).is_ok() {
1642                    let _ = escapable.parse_next(input)?;
1643                } else {
1644                    let offset = input.offset_from(&start);
1645                    input.reset(&start);
1646                    return Ok(input.next_slice(offset));
1647                }
1648            }
1649        }
1650    }
1651
1652    if PARTIAL && input.is_partial() {
1653        Err(ParserError::incomplete(input, Needed::Unknown))
1654    } else {
1655        input.reset(&start);
1656        Ok(input.finish())
1657    }
1658}
1659
1660/// Parse escaped characters, unescaping them
1661///
1662/// Arguments:
1663/// - `normal`: unescapeable characters
1664///   - Must not include `control`
1665/// - `control_char`: e.g. `\` for strings in most languages
1666/// - `escape`: parse and transform the escaped character
1667///
1668/// Parsing ends when:
1669/// - `alt(normal, control_char)` [`Backtrack`s][crate::error::ErrMode::Backtrack]
1670/// - `normal` doesn't advance the input stream
1671/// - *(complete)* input stream is exhausted
1672///
1673/// <div class="warning">
1674///
1675/// **Warning:** If the `normal` parser passed to `escaped` accepts empty inputs
1676/// (like `alpha0` or `digit0`), `escaped` will return an error,
1677/// to prevent going into an infinite loop.
1678///
1679/// </div>
1680///
1681/// # Example
1682///
1683/// ```rust
1684/// # #[cfg(feature = "std")] {
1685/// # use winnow::prelude::*;
1686/// # use std::str::from_utf8;
1687/// use winnow::token::literal;
1688/// use winnow::ascii::escaped;
1689/// use winnow::ascii::alpha1;
1690/// use winnow::combinator::alt;
1691///
1692/// fn parser<'s>(input: &mut &'s str) -> ModalResult<String> {
1693///   escaped(
1694///     alpha1,
1695///     '\\',
1696///     alt((
1697///       "\\".value("\\"),
1698///       "\"".value("\""),
1699///       "n".value("\n"),
1700///     ))
1701///   ).parse_next(input)
1702/// }
1703///
1704/// assert_eq!(parser.parse_peek("ab\\\"cd"), Ok(("", String::from("ab\"cd"))));
1705/// assert_eq!(parser.parse_peek("ab\\ncd"), Ok(("", String::from("ab\ncd"))));
1706/// # }
1707/// ```
1708///
1709/// ```rust
1710/// # #[cfg(feature = "std")] {
1711/// # use winnow::prelude::*;
1712/// # use winnow::{error::ErrMode, error::Needed};
1713/// # use std::str::from_utf8;
1714/// # use winnow::Partial;
1715/// use winnow::token::literal;
1716/// use winnow::ascii::escaped;
1717/// use winnow::ascii::alpha1;
1718/// use winnow::combinator::alt;
1719///
1720/// fn parser<'s>(input: &mut Partial<&'s str>) -> ModalResult<String> {
1721///   escaped(
1722///     alpha1,
1723///     '\\',
1724///     alt((
1725///       "\\".value("\\"),
1726///       "\"".value("\""),
1727///       "n".value("\n"),
1728///     ))
1729///   ).parse_next(input)
1730/// }
1731///
1732/// assert_eq!(parser.parse_peek(Partial::new("ab\\\"cd\"")), Ok((Partial::new("\""), String::from("ab\"cd"))));
1733/// # }
1734/// ```
1735#[inline(always)]
1736pub fn escaped<
1737    Input,
1738    Error,
1739    Normal,
1740    ControlChar,
1741    Escape,
1742    NormalOutput,
1743    ControlCharOutput,
1744    EscapeOutput,
1745    Output,
1746>(
1747    mut normal: Normal,
1748    mut control_char: ControlChar,
1749    mut escape: Escape,
1750) -> impl Parser<Input, Output, Error>
1751where
1752    Input: StreamIsPartial + Stream,
1753    Normal: Parser<Input, NormalOutput, Error>,
1754    ControlChar: Parser<Input, ControlCharOutput, ()>,
1755    Escape: Parser<Input, EscapeOutput, Error>,
1756    Output: crate::stream::Accumulate<NormalOutput>,
1757    Output: crate::stream::Accumulate<EscapeOutput>,
1758    Error: ParserError<Input>,
1759{
1760    trace("escaped", move |input: &mut Input| {
1761        if <Input as StreamIsPartial>::is_partial_supported() && input.is_partial() {
1762            escaped_transform_internal::<_, _, _, _, _, _, _, _, _, true>(
1763                input,
1764                &mut normal,
1765                &mut control_char,
1766                &mut escape,
1767            )
1768        } else {
1769            escaped_transform_internal::<_, _, _, _, _, _, _, _, _, false>(
1770                input,
1771                &mut normal,
1772                &mut control_char,
1773                &mut escape,
1774            )
1775        }
1776    })
1777}
1778
1779fn escaped_transform_internal<
1780    I,
1781    Error,
1782    F,
1783    NormalOutput,
1784    ControlChar,
1785    ControlCharOutput,
1786    G,
1787    EscapeOutput,
1788    Output,
1789    const PARTIAL: bool,
1790>(
1791    input: &mut I,
1792    normal: &mut F,
1793    control_char: &mut ControlChar,
1794    transform: &mut G,
1795) -> Result<Output, Error>
1796where
1797    I: StreamIsPartial,
1798    I: Stream,
1799    Output: crate::stream::Accumulate<NormalOutput>,
1800    Output: crate::stream::Accumulate<EscapeOutput>,
1801    F: Parser<I, NormalOutput, Error>,
1802    ControlChar: Parser<I, ControlCharOutput, ()>,
1803    G: Parser<I, EscapeOutput, Error>,
1804    Error: ParserError<I>,
1805{
1806    let mut res =
1807        <Output as crate::stream::Accumulate<NormalOutput>>::initial(Some(input.eof_offset()));
1808
1809    while input.eof_offset() > 0 {
1810        let current_len = input.eof_offset();
1811
1812        match opt(normal.by_ref()).parse_next(input)? {
1813            Some(o) => {
1814                res.accumulate(o);
1815                // infinite loop check: the parser must always consume
1816                if input.eof_offset() == current_len {
1817                    return Err(ParserError::assert(
1818                        input,
1819                        "`escaped` parsers must always consume",
1820                    ));
1821                }
1822            }
1823            None => {
1824                if control_char.by_ref().parse_next(input).is_ok() {
1825                    let o = transform.parse_next(input)?;
1826                    res.accumulate(o);
1827                } else {
1828                    return Ok(res);
1829                }
1830            }
1831        }
1832    }
1833
1834    if PARTIAL && input.is_partial() {
1835        Err(ParserError::incomplete(input, Needed::Unknown))
1836    } else {
1837        Ok(res)
1838    }
1839}
1840
1841mod sealed {
1842    #[allow(unnameable_types)]
1843    pub struct SealedMarker;
1844}