winnow/ascii/
mod.rs

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