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