binator_utils/
lib.rs

1#![doc = include_str!("../readme.md")]
2#![cfg_attr(not(test), no_std)]
3#![feature(trait_alias)]
4#![feature(try_trait_v2)]
5#![warn(missing_docs)]
6#![deny(clippy::default_numeric_fallback)]
7
8//! Utils combinator
9//!
10//! [Utils] trait contain everything you want to know
11
12use core::{
13  fmt::{
14    Debug,
15    Display,
16  },
17  ops::{
18    BitOr,
19    FromResidual,
20    Try,
21  },
22};
23
24use binator_core::{
25  Contexting,
26  Parse,
27  Parsed,
28  Streaming,
29};
30
31mod span;
32pub use span::*;
33mod opt;
34pub use opt::*;
35mod and;
36pub use and::*;
37mod and_then;
38pub use and_then::*;
39mod and_drop;
40pub use and_drop::*;
41mod drop_and;
42pub use drop_and::*;
43mod or;
44pub use or::*;
45mod not;
46pub use not::*;
47mod peek;
48pub use peek::*;
49mod map;
50pub use map::*;
51mod to;
52pub use to::*;
53mod try_map;
54pub use try_map::*;
55mod drop;
56pub use drop::*;
57// mod drop_last;
58// pub use drop_last::*;
59// mod drop_first;
60// pub use drop_first::*;
61
62mod filter;
63pub use filter::*;
64mod filter_map;
65pub use filter_map::*;
66
67mod fold_bounds;
68pub use fold_bounds::*;
69mod try_fold_bounds;
70pub use try_fold_bounds::*;
71mod try_fold_iter;
72pub use try_fold_iter::*;
73mod fold_until;
74pub use fold_until::*;
75mod try_fold_until;
76pub use try_fold_until::*;
77mod fill;
78pub use fill::*;
79
80mod enumerate;
81pub use enumerate::*;
82mod limit;
83pub use limit::*;
84
85mod add_atom;
86pub use add_atom::*;
87
88/// Atom for most utils combinator
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum UtilsAtom<Stream> {
91  /// When combinator like fold didn't reach the minimun number of Token asked
92  MinNotReach {
93    /// The number of Token found
94    i: usize,
95    /// The number of Token requested
96    min: usize,
97  },
98  /// When combinator like fold_until fail before until is reached
99  UntilNotReach,
100  //  IterEndNotReach,
101  /// When max combinator reached the max allowed.
102  // Stand alone ?
103  Max(usize),
104  /// When filter combinator return failure if filter refuse the Token
105  Filter,
106  /// When Span combinator call diff from stream but it's return Error.
107  /// If you encounter this, it's either mean the two stream are not the same or
108  /// you rewind the stream to a previous point of original stream
109  // missing success token
110  Diff {
111    /// The original steam
112    stream: Stream,
113    /// The stream returned by the success parser
114    stream_success: Stream,
115  },
116}
117
118impl<Stream> Display for UtilsAtom<Stream> {
119  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
120    match self {
121      UtilsAtom::MinNotReach { i, min } => write!(f, "MinNotReach: {} < {}", i, min),
122      UtilsAtom::UntilNotReach => write!(f, "UntilNotReach"),
123      //      UtilsAtom::IterEndNotReach => write!(f, "IterEndNotReach"),
124      UtilsAtom::Max(n) => write!(f, "Max {}", n),
125      UtilsAtom::Filter { .. } => write!(f, "Filter"),
126      UtilsAtom::Diff { .. } => write!(f, "Diff"),
127    }
128  }
129}
130
131/// Extend Parse trait with combinator
132///
133/// Combinator about branching: [Utils::and], [Utils::or].
134///
135/// Combinator about looping: [Utils::fold_bounds] (and rest of fold family),
136/// [Utils::fill].
137///
138/// Combinator often used: [Utils::opt], [Utils::map], [Utils::span].
139pub trait Utils<Stream, Context>: Sized + Parse<Stream, Context>
140where
141  Stream: Streaming,
142{
143  /// and_then will call the underline parser, if successful it will call the
144  /// function in parameter and give it the produced Token. The function must
145  /// return a new parser that will be called to producted the Token returned
146  /// by and_then parser.
147  fn and_then<OtherParser, F>(self, f: F) -> AndThen<Self, F>
148  where
149    OtherParser: Parse<Stream, Context>,
150    F: Fn(Self::Token) -> OtherParser,
151  {
152    and_then(self, f)
153  }
154
155  /// and combinator will call the underline parser, and if successful the
156  /// parser given in parameter. If the second parser is also successful, and
157  /// combinator will return a tuple that contain the two Token producted.
158  fn and<OtherParser, OtherToken>(self, other: OtherParser) -> And<Self, OtherParser>
159  where
160    OtherParser: Parse<Stream, Context, Token = OtherToken>,
161  {
162    and(self, other)
163  }
164
165  /// Same than and combinator but it will drop the second Token instead,
166  /// returning only the first Token from the inner parser.
167  fn and_drop<OtherParser, OtherToken>(self, other: OtherParser) -> AndDrop<Self, OtherParser>
168  where
169    OtherParser: Parse<Stream, Context, Token = OtherToken>,
170  {
171    and_drop(self, other)
172  }
173
174  /// Same than and combinator but it will drop the underline Token instead,
175  /// returning only the second Token from the parser in parameter.
176  fn drop_and<OtherParser, OtherToken>(self, other: OtherParser) -> DropAnd<Self, OtherParser>
177  where
178    OtherParser: Parse<Stream, Context, Token = OtherToken>,
179  {
180    drop_and(self, other)
181  }
182
183  /// Call the underline parser but drop the Token if sucessful. This can be
184  /// considered as a shortcut of the toilet closure: `.map(|_| ())`.
185  fn drop(self) -> Drop<Self> {
186    drop(self)
187  }
188
189  /// Will call the underline parser N times to fill an array of size N and
190  /// return [Token; N] if successfull
191  fn fill<const N: usize>(self) -> Fill<Self, N>
192  where
193    Context: Contexting<UtilsAtom<Stream>>,
194  {
195    fill(self)
196  }
197
198  // consider removing too specific, how to replace ?
199  /// This combinator take a Iterator, it will call the inner parser
200  /// as many time the iterator lenght. The Item produced and the Token
201  /// produced will be given to the function F along with the accumulator
202  /// produced by Init function. The Init and F function can use return any
203  /// type that implement Try. The Token produced is the last accumulator value.
204  fn try_fold_iter<IntoIter, Init, Acc, Ret, F>(
205    self, iter: IntoIter, init: Init, f: F,
206  ) -> TryFoldIter<Self, IntoIter, Init, F>
207  where
208    Context: Contexting<UtilsAtom<Stream>>,
209    IntoIter: IntoIterator + Clone,
210    Init: Fn() -> Ret,
211    F: Fn(Acc, Self::Token, IntoIter::Item) -> Ret,
212    Ret: Try<Output = Acc>,
213    Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
214  {
215    try_fold_iter(self, iter, init, f)
216  }
217
218  /// This Combinator will call the inner parser as long as the until
219  /// parser is not successful, each Token produced will be feed to F
220  /// function along with the accumulator.
221  ///
222  /// The Token produced by fold_until is a tuple of the last
223  /// value of the accumulator and the Token from until parser.
224  fn fold_until<TokenUntil, Acc, Until, Init, F>(
225    self, until: Until, init: Init, f: F,
226  ) -> FoldUntil<Self, Until, Init, F>
227  where
228    Context: Contexting<UtilsAtom<Stream>>,
229    Until: Parse<Stream, Context, Token = TokenUntil>,
230    Init: FnMut() -> Acc,
231    F: FnMut(Acc, Self::Token) -> Acc,
232  {
233    fold_until(self, until, init, f)
234  }
235
236  /// The same then fold_until but can be used with type that implement Try
237  fn try_fold_until<TokenUntil, Acc, Parser, Until, Init, Ret, F>(
238    self, until: Until, init: Init, f: F,
239  ) -> TryFoldUntil<Self, Until, Init, F>
240  where
241    Context: Contexting<UtilsAtom<Stream>>,
242    Until: Parse<Stream, Context, Token = TokenUntil>,
243    Init: Fn() -> Ret,
244    F: Fn(Acc, Self::Token) -> Ret,
245    Ret: Try<Output = Acc>,
246    Parsed<(Acc, Until::Token), Stream, Context>: FromResidual<Ret::Residual>,
247  {
248    try_fold_until(self, until, init, f)
249  }
250
251  /// Main fold combinator, the bahavior depend on the Bounds argument.
252  /// This combinator is implemented for Range and usize. The number of
253  /// iteration depend of the type and the value used for the Bounds argument.
254  ///
255  /// | Type                      | Value | Min | Max |
256  /// |:--------------------------|:------|:----|:----|
257  /// | `Range<usize>`            | 2..4  | 2   | 4   |
258  /// | `RangeInclusive<usize>`   | 2..=4 | 2   | 5   |
259  /// | `RangeFrom<usize>`        | 4..   | 4   | ∞   |
260  /// | `RangeTo<usize>`          | ..4   | 0   | 4   |
261  /// | `RangeToInclusive<usize>` | ..=4  | 0   | 5   |
262  /// | `RangeFull`               | ..    | 0   | ∞   |
263  /// | `usize`                   | 4     | 4   | 4   |
264  ///
265  /// If the minimun value is not respected, it will return an Failure. Then
266  /// until the inner parser return a Failure or the maximun value is reach it
267  /// will continue iterate. Then it will return a Success with the last value
268  /// of the Accumulator This offer a great number of possibility for your
269  /// parser with only one combinator.
270  fn fold_bounds<Bounds, Acc, Init, F>(
271    self, bounds: Bounds, init: Init, f: F,
272  ) -> FoldBounds<Self, Bounds, Init, F>
273  where
274    Context: Contexting<UtilsAtom<Stream>>,
275    Init: FnMut() -> Acc,
276    F: FnMut(Acc, Self::Token) -> Acc,
277    Bounds: FoldBoundsParse,
278    Acc: Debug,
279  {
280    fold_bounds(self, bounds, init, f)
281  }
282
283  /// Same than fold_bounds but F and Acc can return type that implement Try
284  fn try_fold_bounds<Bounds, Acc, Init, Ret, F>(
285    self, bounds: Bounds, init: Init, f: F,
286  ) -> TryFoldBounds<Self, Bounds, Init, F>
287  where
288    Context: Contexting<UtilsAtom<Stream>>,
289    Init: Fn() -> Ret,
290    F: Fn(Acc, Self::Token) -> Ret,
291    Ret: Try<Output = Acc>,
292    Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
293    Bounds: TryFoldBoundsParse,
294    Acc: Debug,
295  {
296    try_fold_bounds(self, bounds, init, f)
297  }
298
299  /// if the underline parser is not successful it will add call
300  /// F and add the Atom provided to the Context
301  fn add_atom<F, Atom>(self, f: F) -> AddAtom<Self, F>
302  where
303    F: Fn() -> Atom,
304    Context: Contexting<Atom>,
305  {
306    add_atom(self, f)
307  }
308
309  /// If the underline parser is successful it will call F
310  /// with the Token produced and change return a new Success with
311  /// the Token returned by F.
312  fn map<F, OtherToken>(self, f: F) -> Map<Self, F>
313  where
314    F: Fn(Self::Token) -> OtherToken,
315  {
316    map(self, f)
317  }
318
319  /// Only allow Success path if F return true
320  fn filter<F>(self, f: F) -> Filter<Self, F>
321  where
322    F: Fn(&Self::Token) -> bool,
323    Context: Contexting<UtilsAtom<Stream>>,
324  {
325    filter(self, f)
326  }
327
328  /// Merge of map and filter combinator, only return Success if
329  /// F return Some.
330  fn filter_map<F, OtherToken>(self, f: F) -> FilterMap<Self, F>
331  where
332    F: Fn(Self::Token) -> Option<OtherToken>,
333    OtherToken: Clone,
334    Context: Contexting<UtilsAtom<Stream>>,
335  {
336    filter_map(self, f)
337  }
338
339  /// If underline parse is successful it will drop the Token and replace it
340  /// with the token in argument. This is mostly a inconditional .map()
341  /// usefull to avoid the closure. (Can be used in Slice parser where map is
342  /// not)
343  fn to<OtherToken>(self, t: OtherToken) -> To<Self, OtherToken>
344  where
345    OtherToken: Clone,
346  {
347    to(self, t)
348  }
349
350  /// Evil Combinator, it will reverse Success in Failure and
351  /// Failure in Success but will not touch Error. This Combinator
352  /// should probably never be used.
353  fn not(self) -> Not<Self>
354  where
355    Stream: Clone,
356    Context: Contexting<NotAtom<Self::Token, Stream>>,
357  {
358    not(self)
359  }
360
361  /// Make a parser optional allowing failure. Return Some(Token)
362  /// in case of Success of underline parser and None in case of Failure.
363  /// This parser can't fail.
364  fn opt(self) -> Optional<Self>
365  where
366    Stream: Clone,
367  {
368    opt(self)
369  }
370
371  /// Very much like Iterator .enumerate(). It will add a counter to every
372  /// Token produced. Return a tuple `(counter, Token)`.
373  fn enumerate(self) -> Enumerate<Self> {
374    enumerate(self)
375  }
376
377  // evil ? should we remove ?
378  /// Very much like Iterator .take(), it will only allow n call to inner parser
379  /// before returning Failure. This parser use a state be aware that it must be
380  /// recreate to be reset.
381  fn limit(self, n: usize) -> Limit<Self>
382  where
383    Context: Contexting<UtilsAtom<Stream>>,
384  {
385    limit(self, n)
386  }
387
388  /// Allow branching, or will call the inner parser and if not sucessful
389  /// it will call the second parser. or can be chained many time allowing
390  /// multiple branch.
391  fn or<OtherParser>(self, b: OtherParser) -> Or<Self, OtherParser>
392  where
393    Stream: Clone,
394    OtherParser: Parse<Stream, Context>,
395    Context: BitOr,
396  {
397    or(self, b)
398  }
399
400  /// peek allow to not consume the Stream but return the Token it would have
401  /// produced. It should be used very often since you would parse more than
402  /// once the same input.
403  fn peek(self) -> Peek<Self>
404  where
405    Stream: Clone,
406  {
407    peek(self)
408  }
409
410  /// span allow to save the Stream consumed by the underline parser in a form
411  /// of a Span. This is very usefull for error or to avoid fully tokenize an
412  /// input. Be aware that Span can contains lifetime since it's linked to
413  /// Stream implementation. For example, an Stream of slice u8 will contains
414  /// a lifetime.
415  fn span(self) -> Span<Self>
416  where
417    Context: Contexting<UtilsAtom<Stream>>,
418  {
419    span(self)
420  }
421
422  /// Same than .filter_map() but expect an Atom in case of Failure.
423  fn try_map<OtherToken, F, Ret>(self, f: F) -> TryMap<Self, F>
424  where
425    F: Fn(Self::Token) -> Ret,
426    Ret: Try<Output = OtherToken>,
427    Parsed<OtherToken, Stream, Context>: FromResidual<Ret::Residual>,
428  {
429    try_map(self, f)
430  }
431}
432
433impl<T, Stream, Context> Utils<Stream, Context> for T
434where
435  Stream: Streaming,
436  Self: Parse<Stream, Context>,
437{
438}