use crate::Tokens;
#[derive(Debug)]
pub struct Take<'a, T> {
tokens: &'a mut T,
n: usize,
}
impl<'a, T> Take<'a, T> {
pub(crate) fn new(tokens: &'a mut T, n: usize) -> Self {
Self { tokens, n }
}
}
impl<'a, T> Tokens for Take<'a, T>
where
T: Tokens,
{
type Item = T::Item;
type Location = T::Location;
fn next(&mut self) -> Option<Self::Item> {
if self.n == 0 {
return None;
}
self.n -= 1;
self.tokens.next()
}
fn location(&self) -> Self::Location {
self.tokens.location()
}
fn set_location(&mut self, location: Self::Location) {
self.tokens.set_location(location)
}
fn is_at_location(&self, location: &Self::Location) -> bool {
self.tokens.is_at_location(location)
}
fn parse<Out, Buf>(&mut self) -> Result<Out, <Out as core::str::FromStr>::Err>
where
Out: core::str::FromStr,
Buf: FromIterator<Self::Item> + core::ops::Deref<Target = str>,
{
let res = self.tokens.parse_take::<Out, Buf>(self.n);
if res.is_ok() {
self.n = 0;
}
res
}
fn parse_take_while<Out, Buf, F2>(
&mut self,
mut take_while: F2,
) -> Result<Out, <Out as core::str::FromStr>::Err>
where
Out: core::str::FromStr,
Buf: FromIterator<Self::Item> + core::ops::Deref<Target = str>,
F2: FnMut(&Self::Item) -> bool,
{
let mut n = self.n;
let res = self.tokens.parse_take_while::<Out, Buf, _>(|t| {
let res = n > 0 && take_while(t);
n = n.saturating_sub(1);
res
});
if res.is_ok() {
self.n = n;
}
res
}
fn parse_take<Out, Buf>(&mut self, n: usize) -> Result<Out, <Out as core::str::FromStr>::Err>
where
Out: core::str::FromStr,
Buf: FromIterator<Self::Item> + core::ops::Deref<Target = str>,
{
let min_n = usize::min(self.n, n);
let res = self.tokens.parse_take::<Out, Buf>(min_n);
if res.is_ok() {
self.n -= min_n;
}
res
}
}
#[cfg(test)]
mod test {
use crate::{types::IterTokens, IntoTokens, Tokens};
#[test]
fn test_parse_success() {
let mut toks = "345abc".into_tokens();
let mut tw = toks.take(3);
let n = tw.parse::<u16, String>().unwrap();
assert_eq!(n, 345);
assert_eq!(tw.collect::<String>(), "");
}
#[test]
fn test_parse_failure() {
let mut toks = "345abc".into_tokens();
let mut tw = toks.take(3);
tw.parse::<u8, String>().unwrap_err();
assert_eq!(tw.collect::<String>(), "345");
}
#[test]
fn test_combinator_stacking_optimisations() {
const TOKS: &str = "345abc+";
fn take_while(mut toks: impl Tokens<Item = char>) {
let s: String = toks.take(6).take_while(|t| t.is_numeric()).collect();
assert_eq!(s, "345");
assert_eq!(toks.collect::<String>(), "abc+");
}
fn take(mut toks: impl Tokens<Item = char>) {
let s: String = toks.take(6).take(4).collect();
assert_eq!(s, "345a");
assert_eq!(toks.collect::<String>(), "bc+");
}
take_while(TOKS.into_tokens());
take_while(IterTokens::new(TOKS.chars()));
take(TOKS.into_tokens());
take(IterTokens::new(TOKS.chars()));
}
}