1 | //! Parsers extracting tokens from the stream |
2 | |
3 | #[cfg (test)] |
4 | mod tests; |
5 | |
6 | use crate::error::ErrMode; |
7 | use crate::error::ErrorKind; |
8 | use crate::error::Needed; |
9 | use crate::error::ParseError; |
10 | use crate::lib::std::result::Result::Ok; |
11 | use crate::stream::Range; |
12 | use crate::stream::{ |
13 | split_at_offset1_complete, split_at_offset1_partial, split_at_offset_complete, |
14 | split_at_offset_partial, Compare, CompareResult, ContainsToken, FindSlice, SliceLen, Stream, |
15 | }; |
16 | use crate::stream::{StreamIsPartial, ToUsize}; |
17 | use crate::trace::trace; |
18 | use crate::IResult; |
19 | use crate::Parser; |
20 | |
21 | /// Matches one token |
22 | /// |
23 | /// *Complete version*: Will return an error if there's not enough input data. |
24 | /// |
25 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. |
26 | /// |
27 | /// # Example |
28 | /// |
29 | /// ```rust |
30 | /// # use winnow::{token::any, error::ErrMode, error::{Error, ErrorKind}}; |
31 | /// # use winnow::prelude::*; |
32 | /// fn parser(input: &str) -> IResult<&str, char> { |
33 | /// any.parse_next(input) |
34 | /// } |
35 | /// |
36 | /// assert_eq!(parser("abc" ), Ok(("bc" ,'a' ))); |
37 | /// assert_eq!(parser("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Token)))); |
38 | /// ``` |
39 | /// |
40 | /// ```rust |
41 | /// # use winnow::{token::any, error::ErrMode, error::ErrorKind, error::Error, error::Needed}; |
42 | /// # use winnow::prelude::*; |
43 | /// # use winnow::Partial; |
44 | /// assert_eq!(any::<_, Error<_>>.parse_next(Partial::new("abc" )), Ok((Partial::new("bc" ),'a' ))); |
45 | /// assert_eq!(any::<_, Error<_>>.parse_next(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
46 | /// ``` |
47 | #[inline (always)] |
48 | #[doc (alias = "token" )] |
49 | pub fn any<I, E: ParseError<I>>(input: I) -> IResult<I, <I as Stream>::Token, E> |
50 | where |
51 | I: StreamIsPartial, |
52 | I: Stream, |
53 | { |
54 | traceimpl Parser::Token, …>(name:"any" , parser:move |input: I| { |
55 | if <I as StreamIsPartial>::is_partial_supported() { |
56 | any_::<_, _, true>(input) |
57 | } else { |
58 | any_::<_, _, true>(input) |
59 | } |
60 | }) |
61 | .parse_next(input) |
62 | } |
63 | |
64 | fn any_<I, E: ParseError<I>, const PARTIAL: bool>(input: I) -> IResult<I, <I as Stream>::Token, E> |
65 | where |
66 | I: StreamIsPartial, |
67 | I: Stream, |
68 | { |
69 | input.next_token().ok_or_else(|| { |
70 | if PARTIAL && input.is_partial() { |
71 | ErrMode::Incomplete(Needed::new(1)) |
72 | } else { |
73 | ErrMode::from_error_kind(input, kind:ErrorKind::Token) |
74 | } |
75 | }) |
76 | } |
77 | |
78 | /// Recognizes a literal |
79 | /// |
80 | /// The input data will be compared to the tag combinator's argument and will return the part of |
81 | /// the input that matches the argument |
82 | /// |
83 | /// It will return `Err(ErrMode::Backtrack(Error::new(_, ErrorKind::Tag)))` if the input doesn't match the pattern |
84 | /// |
85 | /// **Note:** [`Parser`][crate::Parser] is implemented for strings and byte strings as a convenience (complete |
86 | /// only) |
87 | /// |
88 | /// # Example |
89 | /// ```rust |
90 | /// # use winnow::prelude::*; |
91 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
92 | /// use winnow::token::tag; |
93 | /// |
94 | /// fn parser(s: &str) -> IResult<&str, &str> { |
95 | /// "Hello" .parse_next(s) |
96 | /// } |
97 | /// |
98 | /// assert_eq!(parser("Hello, World!" ), Ok((", World!" , "Hello" ))); |
99 | /// assert_eq!(parser("Something" ), Err(ErrMode::Backtrack(Error::new("Something" , ErrorKind::Tag)))); |
100 | /// assert_eq!(parser("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Tag)))); |
101 | /// ``` |
102 | /// |
103 | /// ```rust |
104 | /// # use winnow::prelude::*; |
105 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
106 | /// # use winnow::Partial; |
107 | /// use winnow::token::tag; |
108 | /// |
109 | /// fn parser(s: Partial<&str>) -> IResult<Partial<&str>, &str> { |
110 | /// "Hello" .parse_next(s) |
111 | /// } |
112 | /// |
113 | /// assert_eq!(parser(Partial::new("Hello, World!" )), Ok((Partial::new(", World!" ), "Hello" ))); |
114 | /// assert_eq!(parser(Partial::new("Something" )), Err(ErrMode::Backtrack(Error::new(Partial::new("Something" ), ErrorKind::Tag)))); |
115 | /// assert_eq!(parser(Partial::new("S" )), Err(ErrMode::Backtrack(Error::new(Partial::new("S" ), ErrorKind::Tag)))); |
116 | /// assert_eq!(parser(Partial::new("H" )), Err(ErrMode::Incomplete(Needed::new(4)))); |
117 | /// ``` |
118 | #[inline (always)] |
119 | #[doc (alias = "literal" )] |
120 | #[doc (alias = "bytes" )] |
121 | #[doc (alias = "just" )] |
122 | pub fn tag<T, I, Error: ParseError<I>>(tag: T) -> impl Parser<I, <I as Stream>::Slice, Error> |
123 | where |
124 | I: StreamIsPartial, |
125 | I: Stream + Compare<T>, |
126 | T: SliceLen + Clone, |
127 | { |
128 | trace(name:"tag" , parser:move |i: I| { |
129 | let t: T = tag.clone(); |
130 | if <I as StreamIsPartial>::is_partial_supported() { |
131 | tag_::<_, _, _, true>(i, t) |
132 | } else { |
133 | tag_::<_, _, _, false>(i, t) |
134 | } |
135 | }) |
136 | } |
137 | |
138 | fn tag_<T, I, Error: ParseError<I>, const PARTIAL: bool>( |
139 | i: I, |
140 | t: T, |
141 | ) -> IResult<I, <I as Stream>::Slice, Error> |
142 | where |
143 | I: StreamIsPartial, |
144 | I: Stream + Compare<T>, |
145 | T: SliceLen, |
146 | { |
147 | let tag_len: usize = t.slice_len(); |
148 | match i.compare(t) { |
149 | CompareResult::Ok => Ok(i.next_slice(offset:tag_len)), |
150 | CompareResult::Incomplete if PARTIAL && i.is_partial() => { |
151 | Err(ErrMode::Incomplete(Needed::new(tag_len - i.eof_offset()))) |
152 | } |
153 | CompareResult::Incomplete | CompareResult::Error => { |
154 | let e: ErrorKind = ErrorKind::Tag; |
155 | Err(ErrMode::from_error_kind(input:i, kind:e)) |
156 | } |
157 | } |
158 | } |
159 | |
160 | /// Recognizes a case insensitive literal. |
161 | /// |
162 | /// The input data will be compared to the tag combinator's argument and will return the part of |
163 | /// the input that matches the argument with no regard to case. |
164 | /// |
165 | /// It will return `Err(ErrMode::Backtrack(Error::new(_, ErrorKind::Tag)))` if the input doesn't match the pattern. |
166 | /// |
167 | /// # Example |
168 | /// |
169 | /// ```rust |
170 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
171 | /// # use winnow::prelude::*; |
172 | /// use winnow::token::tag_no_case; |
173 | /// |
174 | /// fn parser(s: &str) -> IResult<&str, &str> { |
175 | /// tag_no_case("hello" ).parse_next(s) |
176 | /// } |
177 | /// |
178 | /// assert_eq!(parser("Hello, World!" ), Ok((", World!" , "Hello" ))); |
179 | /// assert_eq!(parser("hello, World!" ), Ok((", World!" , "hello" ))); |
180 | /// assert_eq!(parser("HeLlO, World!" ), Ok((", World!" , "HeLlO" ))); |
181 | /// assert_eq!(parser("Something" ), Err(ErrMode::Backtrack(Error::new("Something" , ErrorKind::Tag)))); |
182 | /// assert_eq!(parser("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Tag)))); |
183 | /// ``` |
184 | /// |
185 | /// ```rust |
186 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
187 | /// # use winnow::prelude::*; |
188 | /// # use winnow::Partial; |
189 | /// use winnow::token::tag_no_case; |
190 | /// |
191 | /// fn parser(s: Partial<&str>) -> IResult<Partial<&str>, &str> { |
192 | /// tag_no_case("hello" ).parse_next(s) |
193 | /// } |
194 | /// |
195 | /// assert_eq!(parser(Partial::new("Hello, World!" )), Ok((Partial::new(", World!" ), "Hello" ))); |
196 | /// assert_eq!(parser(Partial::new("hello, World!" )), Ok((Partial::new(", World!" ), "hello" ))); |
197 | /// assert_eq!(parser(Partial::new("HeLlO, World!" )), Ok((Partial::new(", World!" ), "HeLlO" ))); |
198 | /// assert_eq!(parser(Partial::new("Something" )), Err(ErrMode::Backtrack(Error::new(Partial::new("Something" ), ErrorKind::Tag)))); |
199 | /// assert_eq!(parser(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(5)))); |
200 | /// ``` |
201 | #[inline (always)] |
202 | #[doc (alias = "literal" )] |
203 | #[doc (alias = "bytes" )] |
204 | #[doc (alias = "just" )] |
205 | pub fn tag_no_case<T, I, Error: ParseError<I>>( |
206 | tag: T, |
207 | ) -> impl Parser<I, <I as Stream>::Slice, Error> |
208 | where |
209 | I: StreamIsPartial, |
210 | I: Stream + Compare<T>, |
211 | T: SliceLen + Clone, |
212 | { |
213 | trace(name:"tag_no_case" , parser:move |i: I| { |
214 | let t: T = tag.clone(); |
215 | if <I as StreamIsPartial>::is_partial_supported() { |
216 | tag_no_case_::<_, _, _, true>(i, t) |
217 | } else { |
218 | tag_no_case_::<_, _, _, false>(i, t) |
219 | } |
220 | }) |
221 | } |
222 | |
223 | fn tag_no_case_<T, I, Error: ParseError<I>, const PARTIAL: bool>( |
224 | i: I, |
225 | t: T, |
226 | ) -> IResult<I, <I as Stream>::Slice, Error> |
227 | where |
228 | I: StreamIsPartial, |
229 | I: Stream + Compare<T>, |
230 | T: SliceLen, |
231 | { |
232 | let tag_len: usize = t.slice_len(); |
233 | |
234 | match (i).compare_no_case(t) { |
235 | CompareResult::Ok => Ok(i.next_slice(offset:tag_len)), |
236 | CompareResult::Incomplete if PARTIAL && i.is_partial() => { |
237 | Err(ErrMode::Incomplete(Needed::new(tag_len - i.eof_offset()))) |
238 | } |
239 | CompareResult::Incomplete | CompareResult::Error => { |
240 | let e: ErrorKind = ErrorKind::Tag; |
241 | Err(ErrMode::from_error_kind(input:i, kind:e)) |
242 | } |
243 | } |
244 | } |
245 | |
246 | /// Recognize a token that matches the [pattern][ContainsToken] |
247 | /// |
248 | /// **Note:** [`Parser`][crate::Parser] is implemented as a convenience (complete |
249 | /// only) for |
250 | /// - `u8` |
251 | /// - `char` |
252 | /// |
253 | /// *Complete version*: Will return an error if there's not enough input data. |
254 | /// |
255 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. |
256 | /// |
257 | /// # Example |
258 | /// |
259 | /// ```rust |
260 | /// # use winnow::prelude::*; |
261 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::Error}; |
262 | /// # use winnow::token::one_of; |
263 | /// assert_eq!(one_of::<_, _, Error<_>>("abc" ).parse_next("b" ), Ok(("" , 'b' ))); |
264 | /// assert_eq!(one_of::<_, _, Error<_>>("a" ).parse_next("bc" ), Err(ErrMode::Backtrack(Error::new("bc" , ErrorKind::Verify)))); |
265 | /// assert_eq!(one_of::<_, _, Error<_>>("a" ).parse_next("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Token)))); |
266 | /// |
267 | /// fn parser_fn(i: &str) -> IResult<&str, char> { |
268 | /// one_of(|c| c == 'a' || c == 'b' ).parse_next(i) |
269 | /// } |
270 | /// assert_eq!(parser_fn("abc" ), Ok(("bc" , 'a' ))); |
271 | /// assert_eq!(parser_fn("cd" ), Err(ErrMode::Backtrack(Error::new("cd" , ErrorKind::Verify)))); |
272 | /// assert_eq!(parser_fn("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Token)))); |
273 | /// ``` |
274 | /// |
275 | /// ``` |
276 | /// # use winnow::prelude::*; |
277 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::Error, error::Needed}; |
278 | /// # use winnow::Partial; |
279 | /// # use winnow::token::one_of; |
280 | /// assert_eq!(one_of::<_, _, Error<_>>("abc" ).parse_next(Partial::new("b" )), Ok((Partial::new("" ), 'b' ))); |
281 | /// assert_eq!(one_of::<_, _, Error<_>>("a" ).parse_next(Partial::new("bc" )), Err(ErrMode::Backtrack(Error::new(Partial::new("bc" ), ErrorKind::Verify)))); |
282 | /// assert_eq!(one_of::<_, _, Error<_>>("a" ).parse_next(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
283 | /// |
284 | /// fn parser_fn(i: Partial<&str>) -> IResult<Partial<&str>, char> { |
285 | /// one_of(|c| c == 'a' || c == 'b' ).parse_next(i) |
286 | /// } |
287 | /// assert_eq!(parser_fn(Partial::new("abc" )), Ok((Partial::new("bc" ), 'a' ))); |
288 | /// assert_eq!(parser_fn(Partial::new("cd" )), Err(ErrMode::Backtrack(Error::new(Partial::new("cd" ), ErrorKind::Verify)))); |
289 | /// assert_eq!(parser_fn(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
290 | /// ``` |
291 | #[inline (always)] |
292 | #[doc (alias = "char" )] |
293 | #[doc (alias = "token" )] |
294 | #[doc (alias = "satisfy" )] |
295 | pub fn one_of<I, T, Error: ParseError<I>>(list: T) -> impl Parser<I, <I as Stream>::Token, Error> |
296 | where |
297 | I: StreamIsPartial, |
298 | I: Stream, |
299 | <I as Stream>::Token: Copy, |
300 | T: ContainsToken<<I as Stream>::Token>, |
301 | { |
302 | trace( |
303 | name:"one_of" , |
304 | parser:any.verify(filter:move |t: &<I as Stream>::Token| list.contains_token(*t)), |
305 | ) |
306 | } |
307 | |
308 | /// Recognize a token that does not match the [pattern][ContainsToken] |
309 | /// |
310 | /// *Complete version*: Will return an error if there's not enough input data. |
311 | /// |
312 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. |
313 | /// |
314 | /// # Example |
315 | /// |
316 | /// ```rust |
317 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::Error}; |
318 | /// # use winnow::prelude::*; |
319 | /// # use winnow::token::none_of; |
320 | /// assert_eq!(none_of::<_, _, Error<_>>("abc" ).parse_next("z" ), Ok(("" , 'z' ))); |
321 | /// assert_eq!(none_of::<_, _, Error<_>>("ab" ).parse_next("a" ), Err(ErrMode::Backtrack(Error::new("a" , ErrorKind::Verify)))); |
322 | /// assert_eq!(none_of::<_, _, Error<_>>("a" ).parse_next("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Token)))); |
323 | /// ``` |
324 | /// |
325 | /// ``` |
326 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::Error, error::Needed}; |
327 | /// # use winnow::prelude::*; |
328 | /// # use winnow::Partial; |
329 | /// # use winnow::token::none_of; |
330 | /// assert_eq!(none_of::<_, _, Error<_>>("abc" ).parse_next(Partial::new("z" )), Ok((Partial::new("" ), 'z' ))); |
331 | /// assert_eq!(none_of::<_, _, Error<_>>("ab" ).parse_next(Partial::new("a" )), Err(ErrMode::Backtrack(Error::new(Partial::new("a" ), ErrorKind::Verify)))); |
332 | /// assert_eq!(none_of::<_, _, Error<_>>("a" ).parse_next(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
333 | /// ``` |
334 | #[inline (always)] |
335 | pub fn none_of<I, T, Error: ParseError<I>>(list: T) -> impl Parser<I, <I as Stream>::Token, Error> |
336 | where |
337 | I: StreamIsPartial, |
338 | I: Stream, |
339 | <I as Stream>::Token: Copy, |
340 | T: ContainsToken<<I as Stream>::Token>, |
341 | { |
342 | trace( |
343 | name:"none_of" , |
344 | parser:any.verify(filter:move |t: &<I as Stream>::Token| !list.contains_token(*t)), |
345 | ) |
346 | } |
347 | |
348 | /// Recognize the longest (m <= len <= n) input slice that matches the [pattern][ContainsToken] |
349 | /// |
350 | /// It will return an `ErrMode::Backtrack(Error::new(_, ErrorKind::Slice))` if the pattern wasn't met or is out |
351 | /// of range (m <= len <= n). |
352 | /// |
353 | /// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the pattern reaches the end of the input or is too short. |
354 | /// |
355 | /// To recognize a series of tokens, use [`repeat`][crate::combinator::repeat] to [`Accumulate`][crate::stream::Accumulate] into a `()` and then [`Parser::recognize`][crate::Parser::recognize]. |
356 | /// |
357 | /// # Example |
358 | /// |
359 | /// Zero or more tokens: |
360 | /// ```rust |
361 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::Error, error::Needed}; |
362 | /// # use winnow::prelude::*; |
363 | /// use winnow::token::take_while; |
364 | /// use winnow::stream::AsChar; |
365 | /// |
366 | /// fn alpha(s: &[u8]) -> IResult<&[u8], &[u8]> { |
367 | /// take_while(0.., AsChar::is_alpha).parse_next(s) |
368 | /// } |
369 | /// |
370 | /// assert_eq!(alpha(b"latin123" ), Ok((&b"123" [..], &b"latin" [..]))); |
371 | /// assert_eq!(alpha(b"12345" ), Ok((&b"12345" [..], &b"" [..]))); |
372 | /// assert_eq!(alpha(b"latin" ), Ok((&b"" [..], &b"latin" [..]))); |
373 | /// assert_eq!(alpha(b"" ), Ok((&b"" [..], &b"" [..]))); |
374 | /// ``` |
375 | /// |
376 | /// ```rust |
377 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::Error, error::Needed}; |
378 | /// # use winnow::prelude::*; |
379 | /// # use winnow::Partial; |
380 | /// use winnow::token::take_while; |
381 | /// use winnow::stream::AsChar; |
382 | /// |
383 | /// fn alpha(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { |
384 | /// take_while(0.., AsChar::is_alpha).parse_next(s) |
385 | /// } |
386 | /// |
387 | /// assert_eq!(alpha(Partial::new(b"latin123" )), Ok((Partial::new(&b"123" [..]), &b"latin" [..]))); |
388 | /// assert_eq!(alpha(Partial::new(b"12345" )), Ok((Partial::new(&b"12345" [..]), &b"" [..]))); |
389 | /// assert_eq!(alpha(Partial::new(b"latin" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
390 | /// assert_eq!(alpha(Partial::new(b"" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
391 | /// ``` |
392 | /// |
393 | /// One or more tokens: |
394 | /// ```rust |
395 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
396 | /// # use winnow::prelude::*; |
397 | /// use winnow::token::take_while; |
398 | /// use winnow::stream::AsChar; |
399 | /// |
400 | /// fn alpha(s: &[u8]) -> IResult<&[u8], &[u8]> { |
401 | /// take_while(1.., AsChar::is_alpha).parse_next(s) |
402 | /// } |
403 | /// |
404 | /// assert_eq!(alpha(b"latin123" ), Ok((&b"123" [..], &b"latin" [..]))); |
405 | /// assert_eq!(alpha(b"latin" ), Ok((&b"" [..], &b"latin" [..]))); |
406 | /// assert_eq!(alpha(b"12345" ), Err(ErrMode::Backtrack(Error::new(&b"12345" [..], ErrorKind::Slice)))); |
407 | /// |
408 | /// fn hex(s: &str) -> IResult<&str, &str> { |
409 | /// take_while(1.., "1234567890ABCDEF" ).parse_next(s) |
410 | /// } |
411 | /// |
412 | /// assert_eq!(hex("123 and voila" ), Ok((" and voila" , "123" ))); |
413 | /// assert_eq!(hex("DEADBEEF and others" ), Ok((" and others" , "DEADBEEF" ))); |
414 | /// assert_eq!(hex("BADBABEsomething" ), Ok(("something" , "BADBABE" ))); |
415 | /// assert_eq!(hex("D15EA5E" ), Ok(("" , "D15EA5E" ))); |
416 | /// assert_eq!(hex("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Slice)))); |
417 | /// ``` |
418 | /// |
419 | /// ```rust |
420 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
421 | /// # use winnow::prelude::*; |
422 | /// # use winnow::Partial; |
423 | /// use winnow::token::take_while; |
424 | /// use winnow::stream::AsChar; |
425 | /// |
426 | /// fn alpha(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { |
427 | /// take_while(1.., AsChar::is_alpha).parse_next(s) |
428 | /// } |
429 | /// |
430 | /// assert_eq!(alpha(Partial::new(b"latin123" )), Ok((Partial::new(&b"123" [..]), &b"latin" [..]))); |
431 | /// assert_eq!(alpha(Partial::new(b"latin" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
432 | /// assert_eq!(alpha(Partial::new(b"12345" )), Err(ErrMode::Backtrack(Error::new(Partial::new(&b"12345" [..]), ErrorKind::Slice)))); |
433 | /// |
434 | /// fn hex(s: Partial<&str>) -> IResult<Partial<&str>, &str> { |
435 | /// take_while(1.., "1234567890ABCDEF" ).parse_next(s) |
436 | /// } |
437 | /// |
438 | /// assert_eq!(hex(Partial::new("123 and voila" )), Ok((Partial::new(" and voila" ), "123" ))); |
439 | /// assert_eq!(hex(Partial::new("DEADBEEF and others" )), Ok((Partial::new(" and others" ), "DEADBEEF" ))); |
440 | /// assert_eq!(hex(Partial::new("BADBABEsomething" )), Ok((Partial::new("something" ), "BADBABE" ))); |
441 | /// assert_eq!(hex(Partial::new("D15EA5E" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
442 | /// assert_eq!(hex(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
443 | /// ``` |
444 | /// |
445 | /// Arbitrary amount of tokens: |
446 | /// ```rust |
447 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
448 | /// # use winnow::prelude::*; |
449 | /// use winnow::token::take_while; |
450 | /// use winnow::stream::AsChar; |
451 | /// |
452 | /// fn short_alpha(s: &[u8]) -> IResult<&[u8], &[u8]> { |
453 | /// take_while(3..=6, AsChar::is_alpha).parse_next(s) |
454 | /// } |
455 | /// |
456 | /// assert_eq!(short_alpha(b"latin123" ), Ok((&b"123" [..], &b"latin" [..]))); |
457 | /// assert_eq!(short_alpha(b"lengthy" ), Ok((&b"y" [..], &b"length" [..]))); |
458 | /// assert_eq!(short_alpha(b"latin" ), Ok((&b"" [..], &b"latin" [..]))); |
459 | /// assert_eq!(short_alpha(b"ed" ), Err(ErrMode::Backtrack(Error::new(&b"ed" [..], ErrorKind::Slice)))); |
460 | /// assert_eq!(short_alpha(b"12345" ), Err(ErrMode::Backtrack(Error::new(&b"12345" [..], ErrorKind::Slice)))); |
461 | /// ``` |
462 | /// |
463 | /// ```rust |
464 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
465 | /// # use winnow::prelude::*; |
466 | /// # use winnow::Partial; |
467 | /// use winnow::token::take_while; |
468 | /// use winnow::stream::AsChar; |
469 | /// |
470 | /// fn short_alpha(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { |
471 | /// take_while(3..=6, AsChar::is_alpha).parse_next(s) |
472 | /// } |
473 | /// |
474 | /// assert_eq!(short_alpha(Partial::new(b"latin123" )), Ok((Partial::new(&b"123" [..]), &b"latin" [..]))); |
475 | /// assert_eq!(short_alpha(Partial::new(b"lengthy" )), Ok((Partial::new(&b"y" [..]), &b"length" [..]))); |
476 | /// assert_eq!(short_alpha(Partial::new(b"latin" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
477 | /// assert_eq!(short_alpha(Partial::new(b"ed" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
478 | /// assert_eq!(short_alpha(Partial::new(b"12345" )), Err(ErrMode::Backtrack(Error::new(Partial::new(&b"12345" [..]), ErrorKind::Slice)))); |
479 | /// ``` |
480 | #[inline (always)] |
481 | #[doc (alias = "is_a" )] |
482 | #[doc (alias = "take_while0" )] |
483 | #[doc (alias = "take_while1" )] |
484 | pub fn take_while<T, I, Error: ParseError<I>>( |
485 | range: impl Into<Range>, |
486 | list: T, |
487 | ) -> impl Parser<I, <I as Stream>::Slice, Error> |
488 | where |
489 | I: StreamIsPartial, |
490 | I: Stream, |
491 | T: ContainsToken<<I as Stream>::Token>, |
492 | { |
493 | let Range { |
494 | start_inclusive, |
495 | end_inclusive, |
496 | } = range.into(); |
497 | trace("take_while" , move |i: I| { |
498 | match (start_inclusive, end_inclusive) { |
499 | (0, None) => { |
500 | if <I as StreamIsPartial>::is_partial_supported() { |
501 | take_while0_::<_, _, _, true>(i, &list) |
502 | } else { |
503 | take_while0_::<_, _, _, false>(i, &list) |
504 | } |
505 | } |
506 | (1, None) => { |
507 | if <I as StreamIsPartial>::is_partial_supported() { |
508 | take_while1_::<_, _, _, true>(i, &list) |
509 | } else { |
510 | take_while1_::<_, _, _, false>(i, &list) |
511 | } |
512 | } |
513 | (start, end) => { |
514 | let end = end.unwrap_or(usize::MAX); |
515 | if <I as StreamIsPartial>::is_partial_supported() { |
516 | take_while_m_n_::<_, _, _, true>(i, start, end, &list) |
517 | } else { |
518 | take_while_m_n_::<_, _, _, false>(i, start, end, &list) |
519 | } |
520 | } |
521 | } |
522 | }) |
523 | } |
524 | |
525 | /// Deprecated, see [`take_while`] |
526 | #[deprecated (since = "0.4.6" , note = "Replaced with `take_while`" )] |
527 | #[inline (always)] |
528 | pub fn take_while0<T, I, Error: ParseError<I>>( |
529 | list: T, |
530 | ) -> impl Parser<I, <I as Stream>::Slice, Error> |
531 | where |
532 | I: StreamIsPartial, |
533 | I: Stream, |
534 | T: ContainsToken<<I as Stream>::Token>, |
535 | { |
536 | take_while(range:0.., list) |
537 | } |
538 | |
539 | /// Deprecated, see [`take_while`] |
540 | #[deprecated (since = "0.4.6" , note = "Replaced with `take_while`" )] |
541 | #[inline (always)] |
542 | pub fn take_while1<T, I, Error: ParseError<I>>( |
543 | list: T, |
544 | ) -> impl Parser<I, <I as Stream>::Slice, Error> |
545 | where |
546 | I: StreamIsPartial, |
547 | I: Stream, |
548 | T: ContainsToken<<I as Stream>::Token>, |
549 | { |
550 | take_while(range:1.., list) |
551 | } |
552 | |
553 | fn take_while0_<T, I, Error: ParseError<I>, const PARTIAL: bool>( |
554 | input: I, |
555 | list: &T, |
556 | ) -> IResult<I, <I as Stream>::Slice, Error> |
557 | where |
558 | I: StreamIsPartial, |
559 | I: Stream, |
560 | T: ContainsToken<<I as Stream>::Token>, |
561 | { |
562 | if PARTIAL && input.is_partial() { |
563 | split_at_offset_partial(&input, |c: ::Token| !list.contains_token(c)) |
564 | } else { |
565 | split_at_offset_complete(&input, |c: ::Token| !list.contains_token(c)) |
566 | } |
567 | } |
568 | |
569 | fn take_while1_<T, I, Error: ParseError<I>, const PARTIAL: bool>( |
570 | input: I, |
571 | list: &T, |
572 | ) -> IResult<I, <I as Stream>::Slice, Error> |
573 | where |
574 | I: StreamIsPartial, |
575 | I: Stream, |
576 | T: ContainsToken<<I as Stream>::Token>, |
577 | { |
578 | let e: ErrorKind = ErrorKind::Slice; |
579 | if PARTIAL && input.is_partial() { |
580 | split_at_offset1_partial(&input, |c: ::Token| !list.contains_token(c), e) |
581 | } else { |
582 | split_at_offset1_complete(&input, |c: ::Token| !list.contains_token(c), e) |
583 | } |
584 | } |
585 | |
586 | fn take_while_m_n_<T, I, Error: ParseError<I>, const PARTIAL: bool>( |
587 | input: I, |
588 | m: usize, |
589 | n: usize, |
590 | list: &T, |
591 | ) -> IResult<I, <I as Stream>::Slice, Error> |
592 | where |
593 | I: StreamIsPartial, |
594 | I: Stream, |
595 | T: ContainsToken<<I as Stream>::Token>, |
596 | { |
597 | if n < m { |
598 | return Err(ErrMode::assert(input, "`m` should be <= `n`" )); |
599 | } |
600 | |
601 | let mut final_count = 0; |
602 | for (processed, (offset, token)) in input.iter_offsets().enumerate() { |
603 | if !list.contains_token(token) { |
604 | if processed < m { |
605 | return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); |
606 | } else { |
607 | return Ok(input.next_slice(offset)); |
608 | } |
609 | } else { |
610 | if processed == n { |
611 | return Ok(input.next_slice(offset)); |
612 | } |
613 | final_count = processed + 1; |
614 | } |
615 | } |
616 | if PARTIAL && input.is_partial() { |
617 | if final_count == n { |
618 | Ok(input.next_slice(input.eof_offset())) |
619 | } else { |
620 | let needed = if m > input.eof_offset() { |
621 | m - input.eof_offset() |
622 | } else { |
623 | 1 |
624 | }; |
625 | Err(ErrMode::Incomplete(Needed::new(needed))) |
626 | } |
627 | } else { |
628 | if m <= final_count { |
629 | Ok(input.next_slice(input.eof_offset())) |
630 | } else { |
631 | Err(ErrMode::from_error_kind(input, ErrorKind::Slice)) |
632 | } |
633 | } |
634 | } |
635 | |
636 | /// Recognize the longest input slice (if any) till a [pattern][ContainsToken] is met. |
637 | /// |
638 | /// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the match reaches the |
639 | /// end of input or if there was not match. |
640 | /// |
641 | /// # Example |
642 | /// |
643 | /// ```rust |
644 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::Error, error::Needed}; |
645 | /// # use winnow::prelude::*; |
646 | /// use winnow::token::take_till0; |
647 | /// |
648 | /// fn till_colon(s: &str) -> IResult<&str, &str> { |
649 | /// take_till0(|c| c == ':' ).parse_next(s) |
650 | /// } |
651 | /// |
652 | /// assert_eq!(till_colon("latin:123" ), Ok((":123" , "latin" ))); |
653 | /// assert_eq!(till_colon(":empty matched" ), Ok((":empty matched" , "" ))); //allowed |
654 | /// assert_eq!(till_colon("12345" ), Ok(("" , "12345" ))); |
655 | /// assert_eq!(till_colon("" ), Ok(("" , "" ))); |
656 | /// ``` |
657 | /// |
658 | /// ```rust |
659 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::Error, error::Needed}; |
660 | /// # use winnow::prelude::*; |
661 | /// # use winnow::Partial; |
662 | /// use winnow::token::take_till0; |
663 | /// |
664 | /// fn till_colon(s: Partial<&str>) -> IResult<Partial<&str>, &str> { |
665 | /// take_till0(|c| c == ':' ).parse_next(s) |
666 | /// } |
667 | /// |
668 | /// assert_eq!(till_colon(Partial::new("latin:123" )), Ok((Partial::new(":123" ), "latin" ))); |
669 | /// assert_eq!(till_colon(Partial::new(":empty matched" )), Ok((Partial::new(":empty matched" ), "" ))); //allowed |
670 | /// assert_eq!(till_colon(Partial::new("12345" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
671 | /// assert_eq!(till_colon(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
672 | /// ``` |
673 | #[inline (always)] |
674 | pub fn take_till0<T, I, Error: ParseError<I>>( |
675 | list: T, |
676 | ) -> impl Parser<I, <I as Stream>::Slice, Error> |
677 | where |
678 | I: StreamIsPartial, |
679 | I: Stream, |
680 | T: ContainsToken<<I as Stream>::Token>, |
681 | { |
682 | trace(name:"take_till0" , parser:move |i: I| { |
683 | if <I as StreamIsPartial>::is_partial_supported() && i.is_partial() { |
684 | split_at_offset_partial(&i, |c: ::Token| list.contains_token(c)) |
685 | } else { |
686 | split_at_offset_complete(&i, |c: ::Token| list.contains_token(c)) |
687 | } |
688 | }) |
689 | } |
690 | |
691 | /// Recognize the longest (at least 1) input slice till a [pattern][ContainsToken] is met. |
692 | /// |
693 | /// It will return `Err(ErrMode::Backtrack(Error::new(_, ErrorKind::Slice)))` if the input is empty or the |
694 | /// predicate matches the first input. |
695 | /// |
696 | /// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the match reaches the |
697 | /// end of input or if there was not match. |
698 | /// |
699 | /// # Example |
700 | /// |
701 | /// ```rust |
702 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
703 | /// # use winnow::prelude::*; |
704 | /// use winnow::token::take_till1; |
705 | /// |
706 | /// fn till_colon(s: &str) -> IResult<&str, &str> { |
707 | /// take_till1(|c| c == ':' ).parse_next(s) |
708 | /// } |
709 | /// |
710 | /// assert_eq!(till_colon("latin:123" ), Ok((":123" , "latin" ))); |
711 | /// assert_eq!(till_colon(":empty matched" ), Err(ErrMode::Backtrack(Error::new(":empty matched" , ErrorKind::Slice)))); |
712 | /// assert_eq!(till_colon("12345" ), Ok(("" , "12345" ))); |
713 | /// assert_eq!(till_colon("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Slice)))); |
714 | /// |
715 | /// fn not_space(s: &str) -> IResult<&str, &str> { |
716 | /// take_till1(" \t\r\n" ).parse_next(s) |
717 | /// } |
718 | /// |
719 | /// assert_eq!(not_space("Hello, World!" ), Ok((" World!" , "Hello," ))); |
720 | /// assert_eq!(not_space("Sometimes \t" ), Ok((" \t" , "Sometimes" ))); |
721 | /// assert_eq!(not_space("Nospace" ), Ok(("" , "Nospace" ))); |
722 | /// assert_eq!(not_space("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Slice)))); |
723 | /// ``` |
724 | /// |
725 | /// ```rust |
726 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
727 | /// # use winnow::prelude::*; |
728 | /// # use winnow::Partial; |
729 | /// use winnow::token::take_till1; |
730 | /// |
731 | /// fn till_colon(s: Partial<&str>) -> IResult<Partial<&str>, &str> { |
732 | /// take_till1(|c| c == ':' ).parse_next(s) |
733 | /// } |
734 | /// |
735 | /// assert_eq!(till_colon(Partial::new("latin:123" )), Ok((Partial::new(":123" ), "latin" ))); |
736 | /// assert_eq!(till_colon(Partial::new(":empty matched" )), Err(ErrMode::Backtrack(Error::new(Partial::new(":empty matched" ), ErrorKind::Slice)))); |
737 | /// assert_eq!(till_colon(Partial::new("12345" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
738 | /// assert_eq!(till_colon(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
739 | /// |
740 | /// fn not_space(s: Partial<&str>) -> IResult<Partial<&str>, &str> { |
741 | /// take_till1(" \t\r\n" ).parse_next(s) |
742 | /// } |
743 | /// |
744 | /// assert_eq!(not_space(Partial::new("Hello, World!" )), Ok((Partial::new(" World!" ), "Hello," ))); |
745 | /// assert_eq!(not_space(Partial::new("Sometimes \t" )), Ok((Partial::new(" \t" ), "Sometimes" ))); |
746 | /// assert_eq!(not_space(Partial::new("Nospace" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
747 | /// assert_eq!(not_space(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
748 | /// ``` |
749 | #[inline (always)] |
750 | #[doc (alias = "is_not" )] |
751 | pub fn take_till1<T, I, Error: ParseError<I>>( |
752 | list: T, |
753 | ) -> impl Parser<I, <I as Stream>::Slice, Error> |
754 | where |
755 | I: StreamIsPartial, |
756 | I: Stream, |
757 | T: ContainsToken<<I as Stream>::Token>, |
758 | { |
759 | trace(name:"take_till1" , parser:move |i: I| { |
760 | let e: ErrorKind = ErrorKind::Slice; |
761 | if <I as StreamIsPartial>::is_partial_supported() && i.is_partial() { |
762 | split_at_offset1_partial(&i, |c: ::Token| list.contains_token(c), e) |
763 | } else { |
764 | split_at_offset1_complete(&i, |c: ::Token| list.contains_token(c), e) |
765 | } |
766 | }) |
767 | } |
768 | |
769 | /// Recognize an input slice containing the first N input elements (I[..N]). |
770 | /// |
771 | /// *Complete version*: It will return `Err(ErrMode::Backtrack(Error::new(_, ErrorKind::Slice)))` if the input is shorter than the argument. |
772 | /// |
773 | /// *Partial version*: if the input has less than N elements, `take` will |
774 | /// return a `ErrMode::Incomplete(Needed::new(M))` where M is the number of |
775 | /// additional bytes the parser would need to succeed. |
776 | /// It is well defined for `&[u8]` as the number of elements is the byte size, |
777 | /// but for types like `&str`, we cannot know how many bytes correspond for |
778 | /// the next few chars, so the result will be `ErrMode::Incomplete(Needed::Unknown)` |
779 | /// |
780 | /// # Example |
781 | /// |
782 | /// ```rust |
783 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
784 | /// # use winnow::prelude::*; |
785 | /// use winnow::token::take; |
786 | /// |
787 | /// fn take6(s: &str) -> IResult<&str, &str> { |
788 | /// take(6usize).parse_next(s) |
789 | /// } |
790 | /// |
791 | /// assert_eq!(take6("1234567" ), Ok(("7" , "123456" ))); |
792 | /// assert_eq!(take6("things" ), Ok(("" , "things" ))); |
793 | /// assert_eq!(take6("short" ), Err(ErrMode::Backtrack(Error::new("short" , ErrorKind::Slice)))); |
794 | /// assert_eq!(take6("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Slice)))); |
795 | /// ``` |
796 | /// |
797 | /// The units that are taken will depend on the input type. For example, for a |
798 | /// `&str` it will take a number of `char`'s, whereas for a `&[u8]` it will |
799 | /// take that many `u8`'s: |
800 | /// |
801 | /// ```rust |
802 | /// # use winnow::prelude::*; |
803 | /// use winnow::error::Error; |
804 | /// use winnow::token::take; |
805 | /// |
806 | /// assert_eq!(take::<_, _, Error<_>>(1usize).parse_next("💙" ), Ok(("" , "💙" ))); |
807 | /// assert_eq!(take::<_, _, Error<_>>(1usize).parse_next("💙" .as_bytes()), Ok((b" \x9F\x92\x99" .as_ref(), b" \xF0" .as_ref()))); |
808 | /// ``` |
809 | /// |
810 | /// ```rust |
811 | /// # use winnow::prelude::*; |
812 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::Error, error::Needed}; |
813 | /// # use winnow::Partial; |
814 | /// use winnow::token::take; |
815 | /// |
816 | /// fn take6(s: Partial<&str>) -> IResult<Partial<&str>, &str> { |
817 | /// take(6usize).parse_next(s) |
818 | /// } |
819 | /// |
820 | /// assert_eq!(take6(Partial::new("1234567" )), Ok((Partial::new("7" ), "123456" ))); |
821 | /// assert_eq!(take6(Partial::new("things" )), Ok((Partial::new("" ), "things" ))); |
822 | /// // `Unknown` as we don't know the number of bytes that `count` corresponds to |
823 | /// assert_eq!(take6(Partial::new("short" )), Err(ErrMode::Incomplete(Needed::Unknown))); |
824 | /// ``` |
825 | #[inline (always)] |
826 | pub fn take<C, I, Error: ParseError<I>>(count: C) -> impl Parser<I, <I as Stream>::Slice, Error> |
827 | where |
828 | I: StreamIsPartial, |
829 | I: Stream, |
830 | C: ToUsize, |
831 | { |
832 | let c: usize = count.to_usize(); |
833 | trace(name:"take" , parser:move |i: I| { |
834 | if <I as StreamIsPartial>::is_partial_supported() { |
835 | take_::<_, _, true>(i, c) |
836 | } else { |
837 | take_::<_, _, false>(i, c) |
838 | } |
839 | }) |
840 | } |
841 | |
842 | fn take_<I, Error: ParseError<I>, const PARTIAL: bool>( |
843 | i: I, |
844 | c: usize, |
845 | ) -> IResult<I, <I as Stream>::Slice, Error> |
846 | where |
847 | I: StreamIsPartial, |
848 | I: Stream, |
849 | { |
850 | match i.offset_at(tokens:c) { |
851 | Ok(offset: usize) => Ok(i.next_slice(offset)), |
852 | Err(e: Needed) if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(e)), |
853 | Err(_needed: Needed) => Err(ErrMode::from_error_kind(input:i, kind:ErrorKind::Slice)), |
854 | } |
855 | } |
856 | |
857 | /// Recognize the input slice up to the first occurrence of the literal. |
858 | /// |
859 | /// It doesn't consume the pattern. |
860 | /// |
861 | /// *Complete version*: It will return `Err(ErrMode::Backtrack(Error::new(_, ErrorKind::Slice)))` |
862 | /// if the pattern wasn't met. |
863 | /// |
864 | /// *Partial version*: will return a `ErrMode::Incomplete(Needed::new(N))` if the input doesn't |
865 | /// contain the pattern or if the input is smaller than the pattern. |
866 | /// |
867 | /// # Example |
868 | /// |
869 | /// ```rust |
870 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
871 | /// # use winnow::prelude::*; |
872 | /// use winnow::token::take_until0; |
873 | /// |
874 | /// fn until_eof(s: &str) -> IResult<&str, &str> { |
875 | /// take_until0("eof" ).parse_next(s) |
876 | /// } |
877 | /// |
878 | /// assert_eq!(until_eof("hello, worldeof" ), Ok(("eof" , "hello, world" ))); |
879 | /// assert_eq!(until_eof("hello, world" ), Err(ErrMode::Backtrack(Error::new("hello, world" , ErrorKind::Slice)))); |
880 | /// assert_eq!(until_eof("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Slice)))); |
881 | /// assert_eq!(until_eof("1eof2eof" ), Ok(("eof2eof" , "1" ))); |
882 | /// ``` |
883 | /// |
884 | /// ```rust |
885 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::Error, error::Needed}; |
886 | /// # use winnow::prelude::*; |
887 | /// # use winnow::Partial; |
888 | /// use winnow::token::take_until0; |
889 | /// |
890 | /// fn until_eof(s: Partial<&str>) -> IResult<Partial<&str>, &str> { |
891 | /// take_until0("eof" ).parse_next(s) |
892 | /// } |
893 | /// |
894 | /// assert_eq!(until_eof(Partial::new("hello, worldeof" )), Ok((Partial::new("eof" ), "hello, world" ))); |
895 | /// assert_eq!(until_eof(Partial::new("hello, world" )), Err(ErrMode::Incomplete(Needed::Unknown))); |
896 | /// assert_eq!(until_eof(Partial::new("hello, worldeo" )), Err(ErrMode::Incomplete(Needed::Unknown))); |
897 | /// assert_eq!(until_eof(Partial::new("1eof2eof" )), Ok((Partial::new("eof2eof" ), "1" ))); |
898 | /// ``` |
899 | #[inline (always)] |
900 | pub fn take_until0<T, I, Error: ParseError<I>>( |
901 | tag: T, |
902 | ) -> impl Parser<I, <I as Stream>::Slice, Error> |
903 | where |
904 | I: StreamIsPartial, |
905 | I: Stream + FindSlice<T>, |
906 | T: SliceLen + Clone, |
907 | { |
908 | trace(name:"take_until0" , parser:move |i: I| { |
909 | if <I as StreamIsPartial>::is_partial_supported() { |
910 | take_until0_::<_, _, _, true>(i, t:tag.clone()) |
911 | } else { |
912 | take_until0_::<_, _, _, false>(i, t:tag.clone()) |
913 | } |
914 | }) |
915 | } |
916 | |
917 | fn take_until0_<T, I, Error: ParseError<I>, const PARTIAL: bool>( |
918 | i: I, |
919 | t: T, |
920 | ) -> IResult<I, <I as Stream>::Slice, Error> |
921 | where |
922 | I: StreamIsPartial, |
923 | I: Stream + FindSlice<T>, |
924 | T: SliceLen, |
925 | { |
926 | match i.find_slice(substr:t) { |
927 | Some(offset: usize) => Ok(i.next_slice(offset)), |
928 | None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)), |
929 | None => Err(ErrMode::from_error_kind(input:i, kind:ErrorKind::Slice)), |
930 | } |
931 | } |
932 | |
933 | /// Recognize the non empty input slice up to the first occurrence of the literal. |
934 | /// |
935 | /// It doesn't consume the pattern. |
936 | /// |
937 | /// *Complete version*: It will return `Err(ErrMode::Backtrack(Error::new(_, ErrorKind::Slice)))` |
938 | /// if the pattern wasn't met. |
939 | /// |
940 | /// *Partial version*: will return a `ErrMode::Incomplete(Needed::new(N))` if the input doesn't |
941 | /// contain the pattern or if the input is smaller than the pattern. |
942 | /// |
943 | /// # Example |
944 | /// |
945 | /// ```rust |
946 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
947 | /// # use winnow::prelude::*; |
948 | /// use winnow::token::take_until1; |
949 | /// |
950 | /// fn until_eof(s: &str) -> IResult<&str, &str> { |
951 | /// take_until1("eof" ).parse_next(s) |
952 | /// } |
953 | /// |
954 | /// assert_eq!(until_eof("hello, worldeof" ), Ok(("eof" , "hello, world" ))); |
955 | /// assert_eq!(until_eof("hello, world" ), Err(ErrMode::Backtrack(Error::new("hello, world" , ErrorKind::Slice)))); |
956 | /// assert_eq!(until_eof("" ), Err(ErrMode::Backtrack(Error::new("" , ErrorKind::Slice)))); |
957 | /// assert_eq!(until_eof("1eof2eof" ), Ok(("eof2eof" , "1" ))); |
958 | /// assert_eq!(until_eof("eof" ), Err(ErrMode::Backtrack(Error::new("eof" , ErrorKind::Slice)))); |
959 | /// ``` |
960 | /// |
961 | /// ```rust |
962 | /// # use winnow::{error::ErrMode, error::{Error, ErrorKind}, error::Needed}; |
963 | /// # use winnow::prelude::*; |
964 | /// # use winnow::Partial; |
965 | /// use winnow::token::take_until1; |
966 | /// |
967 | /// fn until_eof(s: Partial<&str>) -> IResult<Partial<&str>, &str> { |
968 | /// take_until1("eof" ).parse_next(s) |
969 | /// } |
970 | /// |
971 | /// assert_eq!(until_eof(Partial::new("hello, worldeof" )), Ok((Partial::new("eof" ), "hello, world" ))); |
972 | /// assert_eq!(until_eof(Partial::new("hello, world" )), Err(ErrMode::Incomplete(Needed::Unknown))); |
973 | /// assert_eq!(until_eof(Partial::new("hello, worldeo" )), Err(ErrMode::Incomplete(Needed::Unknown))); |
974 | /// assert_eq!(until_eof(Partial::new("1eof2eof" )), Ok((Partial::new("eof2eof" ), "1" ))); |
975 | /// assert_eq!(until_eof(Partial::new("eof" )), Err(ErrMode::Backtrack(Error::new(Partial::new("eof" ), ErrorKind::Slice)))); |
976 | /// ``` |
977 | #[inline (always)] |
978 | pub fn take_until1<T, I, Error: ParseError<I>>( |
979 | tag: T, |
980 | ) -> impl Parser<I, <I as Stream>::Slice, Error> |
981 | where |
982 | I: StreamIsPartial, |
983 | I: Stream + FindSlice<T>, |
984 | T: SliceLen + Clone, |
985 | { |
986 | trace(name:"take_until1" , parser:move |i: I| { |
987 | if <I as StreamIsPartial>::is_partial_supported() { |
988 | take_until1_::<_, _, _, true>(i, t:tag.clone()) |
989 | } else { |
990 | take_until1_::<_, _, _, false>(i, t:tag.clone()) |
991 | } |
992 | }) |
993 | } |
994 | |
995 | fn take_until1_<T, I, Error: ParseError<I>, const PARTIAL: bool>( |
996 | i: I, |
997 | t: T, |
998 | ) -> IResult<I, <I as Stream>::Slice, Error> |
999 | where |
1000 | I: StreamIsPartial, |
1001 | I: Stream + FindSlice<T>, |
1002 | T: SliceLen, |
1003 | { |
1004 | match i.find_slice(substr:t) { |
1005 | None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)), |
1006 | None | Some(0) => Err(ErrMode::from_error_kind(input:i, kind:ErrorKind::Slice)), |
1007 | Some(offset: usize) => Ok(i.next_slice(offset)), |
1008 | } |
1009 | } |
1010 | |