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