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