use crate::{
action::{Action, Input, Output},
combinator::{provided::create_recur, Combinator},
instant::Instant,
};
use core::fmt;
use std::{cell::OnceCell, rc::Rc};
create_recur!([u8]);
#[allow(clippy::type_complexity)]
pub fn recur<Value>() -> (
impl Fn() -> Combinator<Recur<(), (), Value>>,
RecurSetter<(), (), Value>,
) {
let inner = Rc::new(OnceCell::new());
let setter = RecurSetter::new(inner.clone());
let getter = move || Combinator::new(Recur::new(inner.clone()));
(getter, setter)
}
#[allow(clippy::type_complexity)]
pub unsafe fn recur_unchecked<Value>() -> (
impl Fn() -> Combinator<RecurUnchecked<(), (), Value>>,
RecurSetter<(), (), Value>,
) {
let inner = Rc::new(OnceCell::new());
let setter = RecurSetter::new(inner.clone());
let getter = move || Combinator::new(RecurUnchecked::new(inner.clone()));
(getter, setter)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{combinator::bytes::eat, digest::Digest, instant::Instant};
use std::{ops::RangeFrom, slice::SliceIndex};
fn helper<Text: ?Sized + Digest>(
action: impl Action<Text = Text, State = (), Heap = (), Value = ()>,
input: &Text,
digested: Option<usize>,
) where
RangeFrom<usize>: SliceIndex<Text, Output = Text>,
{
assert_eq!(
action
.exec(Input {
instant: &Instant::new(input),
state: &mut (),
heap: &mut ()
})
.map(|o| o.digested),
digested
)
}
#[test]
fn test_recur() {
let (value, value_setter) = recur();
let array = || eat(b'[') + (value() * ..).sep(b',') + b']';
value_setter.boxed(array() | b'a');
helper(value(), b"a", Some(1));
helper(value(), b"[]", Some(2));
helper(value(), b"[a]", Some(3));
helper(value(), b"[[]]", Some(4));
helper(value(), b"[a,a]", Some(5));
helper(value(), b"[[],[]]", Some(7));
helper(value(), b"[[a],[]]", Some(8));
let _ = value().clone();
assert_eq!(format!("{:?}", value().action), "Recur");
}
#[test]
#[should_panic]
fn test_recur_panic() {
let (value, _) = recur::<()>();
value().exec(Input {
instant: &Instant::new(b"a"),
state: &mut (),
heap: &mut (),
});
}
#[test]
fn test_recur_unchecked() {
let (value, value_setter) = unsafe { recur_unchecked() };
let array = || eat(b'[') + (value() * ..).sep(b',') + b']';
value_setter.boxed(array() | b'a');
helper(value(), b"a", Some(1));
helper(value(), b"[]", Some(2));
helper(value(), b"[a]", Some(3));
helper(value(), b"[[]]", Some(4));
helper(value(), b"[a,a]", Some(5));
helper(value(), b"[[],[]]", Some(7));
helper(value(), b"[[a],[]]", Some(8));
let _ = value().clone();
assert_eq!(format!("{:?}", value().action), "RecurUnchecked");
}
#[test]
#[should_panic]
fn test_recur_unchecked_panic() {
let (value, _) = unsafe { recur_unchecked::<()>() };
value().exec(Input {
instant: &Instant::new(b"a"),
state: &mut (),
heap: &mut (),
});
}
}