pipe_chain/
take.rs

1use crate::{Incomplete, Pipe, Repetition};
2use fatal_error::FatalError;
3use std::error::Error as StdError;
4
5/// Implemented by [TakeAtom::Atom] to have an atom length
6pub trait LenBytes {
7    /// number of bytes
8    fn len_bytes(&self) -> usize;
9}
10
11/// Implemented by types usable with [take_while]
12pub trait TakeAtom {
13    /// a single element
14    type Atom: LenBytes;
15    /// a container of [TakeAtom::Atom]
16    type Container;
17
18    /// Gets the next [TakeAtom::Atom] and it's index in the container
19    fn next(&mut self) -> Option<(usize, Self::Atom)>;
20
21    /// returns a tuple containing `(remaining, consumed)`
22    ///  where `consumed` contains all of the items returned by [TakeAtom::next]
23    fn split_at(self, index: usize) -> (Self::Container, Self::Container);
24}
25
26/// Returns the longest input that matches the predicate
27///
28/// If quantity condition is not attained an error is returned
29#[inline]
30pub fn take_while<I, E>(
31    mut f: impl FnMut(I::Atom) -> bool,
32    qty: impl TryInto<Repetition, Error = impl StdError>,
33) -> impl Pipe<I, (I::Container,), E, I::Container>
34where
35    I: TakeAtom,
36    Incomplete: Into<E>,
37{
38    let qty = qty.try_into().unwrap();
39    #[inline]
40    move |mut input: I| {
41        let mut c = 0;
42        let mut idx = 0;
43        let mut err = None;
44        let mut all = false;
45        while !qty.is_max(c) {
46            if let Some((i, x)) = input.next() {
47                let end = i + x.len_bytes();
48                if !f(x) {
49                    err = Some(Incomplete::Unknown.into());
50                    break;
51                }
52                idx = end;
53                c += 1;
54            } else {
55                all = true;
56                break;
57            }
58        }
59        qty.map_err(c, |_, _, _| {
60            if all {
61                FatalError::Error(Incomplete::Unknown.into())
62            } else {
63                FatalError::Error(err.unwrap())
64            }
65        })?;
66        let (r, x) = input.split_at(idx);
67        Ok((r, (x,)))
68    }
69}
70
71/// creates a pipe that takes `qty` of [TakeAtom]
72///
73/// examples see: [sentences][crate::str::sentences], [words][crate::str::words]
74pub fn take_atom<A: TakeAtom, E, E2>(
75    qty: impl TryInto<Repetition, Error = E>,
76) -> Result<impl Pipe<A, (Vec<A::Atom>,), E2, A::Container>, E>
77where
78    Incomplete: Into<E2>,
79{
80    let qty = qty.try_into()?;
81    Ok(move |mut input: A| {
82        let mut r = Vec::new();
83        let mut last = 0;
84        for x in 0.. {
85            match input.next() {
86                Some((i, o)) => {
87                    last = i + o.len_bytes();
88                    r.push(o);
89                }
90                None => {
91                    if qty.needs_more(x) {
92                        return Err(FatalError::Error(Incomplete::Unknown.into()));
93                    } else {
94                        break;
95                    }
96                }
97            }
98            if qty.is_max(x) {
99                break;
100            }
101        }
102        Ok((input.split_at(last).0, (r,)))
103    })
104}