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