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(name:"crlf", parser:"\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 traceimpl Parser::Slice, …>(name:"till_line_ending", parser: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: ::Slice = take_till(range:0.., ('\r', '\n')).parse_next(input)?;
169 if input.compare("\r") == CompareResult::Ok {
170 let comp: CompareResult = 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, kind: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(name:"line_ending", parser: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(name:"newline", parser:'\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(name:"tab", parser:'\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(name:"alpha0", parser:take_while(range:0.., list: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(name:"alpha1", parser:take_while(range:1.., list: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(name:"digit0", parser:take_while(range:0.., list: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(name:"digit1", parser:take_while(range:1.., list: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(name:"hex_digit0", parser:take_while(range:0.., list: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(name:"hex_digit1", parser:take_while(range:1.., list: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(name:"oct_digit0", parser:take_while(range:0.., list: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(name:"oct_digit0", parser:take_while(range:1.., list: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(name:"alphanumeric0", parser:take_while(range:0.., list: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(name:"alphanumeric1", parser:take_while(range:1.., list: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(name:"space0", parser:take_while(range:0.., list: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(name:"space1", parser:take_while(range:1.., list: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(name:"multispace0", parser:take_while(range: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(name:"multispace1", parser:take_while(range: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 traceimpl Parser(name:"float", parser:move |input: &mut I| {
1360 let s: ::Slice = recognize_float_or_exceptions(input)?;
1361 s.parse_slice()
1362 .ok_or_else(|| ErrMode::from_error_kind(input, kind: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 altimpl Parser::Slice, …>((
1381 recognize_float,
1382 crate::token::tag_no_case(tag:"nan"),
1383 (
1384 opt(one_of(['+', '-'])),
1385 crate::token::tag_no_case(tag:"infinity"),
1386 )
1387 .recognize(),
1388 (opt(one_of(['+', '-'])), crate::token::tag_no_case(tag:"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(parser: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(name:"escaped", parser: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(name:"escaped_transform", parser: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 = Output::initial(capacity:Some(input.eof_offset()));
1662
1663 while input.eof_offset() > 0 {
1664 let current_len: usize = input.eof_offset();
1665 match opt(normal.by_ref()).parse_next(input)? {
1666 Some(o: ::Slice) => {
1667 res.accumulate(acc: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: ::Slice = transform.parse_next(input)?;
1675 res.accumulate(acc: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}
1728