pub mod walkers;
use core::fmt::Debug;
use effectful::{
effective::{Canonical, Effective}, environment::Environment, DynBind, SendSync
};
use crate::{protocol::DynVisitor, Flow};
/// A type that can be walked.
pub trait Walk<'ctx, M, E: Environment>: Sized {
/// The walker for the type.
type Walker: Walker<'ctx, E>;
#[must_use]
fn into_walker<'e>(self) -> Canonical<'e, Self::Walker, E>
where
Self: 'e;
}
/// Walker for a type.
///
/// The `'ctx` lifetime is some lifetime that is longer than `Self`.
/// Data from the value may borrow using `'ctx`.
///
/// The way to use a walker is as follows.
/// - Call [From::from()] with a value to be walked to make a walker.
/// - Call [Self::walk()] to walk the value. Data will be sent to the provided
/// visitor.
pub trait Walker<'ctx, E: Environment>: DynBind<E> {
type Error: DynBind<E> + Debug;
/// An arbitrary type the walker is left with after walking.
///
/// Its recommended that this is `Self` if the walker is repeatable.
type Output: DynBind<E>;
/// Walk the value.
///
/// The walker should send data to the `visitor` as it walks the value.
fn walk<'visitor: 'effect, 'lt: 'effect, 'effect>(
self,
visitor: DynVisitor<'visitor, 'lt, 'ctx, E>,
) -> Canonical<'effect, Result<Self::Output, Self::Error>, E>
where
Self: 'effect;
}
pub trait WalkerObjSafe<'lt, 'ctx: 'lt, E: Environment>: DynBind<E> + 'lt {
fn walk<'a: 'c, 'b: 'c, 'd: 'b, 'c>(
&'a mut self,
visitor: DynVisitor<'b, 'd, 'ctx, E>,
) -> Canonical<'c, Flow, E>
where
'ctx: 'd,
Self: 'a;
}
pub type DynWalkerObjSafe<'a, 'lt, 'ctx, E> = &'a mut (dyn WalkerObjSafe<'lt, 'ctx, E> + 'lt);
#[derive(SendSync)]
enum DynWalkerState<'ctx, W: Walker<'ctx, E>, E: Environment> {
Walking,
Pending(W),
Done(W::Output),
Err(W::Error),
}
pub enum DynWalkerError<'ctx, W: Walker<'ctx, E>, E: Environment> {
NeverWalked(W),
/// This can only happen if a panic happens furing the walk and is then caught before calling
/// finish..
WalkNeverFinished,
Walker(W::Error),
WasWalked(W::Output),
}
#[derive(SendSync)]
pub struct DynWalkerAdapter<'ctx, W: Walker<'ctx, E>, E: Environment> {
state: DynWalkerState<'ctx, W, E>,
}
impl<'ctx, W: Walker<'ctx, E>, E: Environment> DynWalkerAdapter<'ctx, W, E> {
#[inline(always)]
pub fn new(walker: W) -> Self {
Self {
state: DynWalkerState::Pending(walker),
}
}
#[inline(always)]
pub fn finish(self) -> Result<W::Output, DynWalkerError<'ctx, W, E>> {
match self.state {
DynWalkerState::Walking => Err(DynWalkerError::WalkNeverFinished),
DynWalkerState::Pending(walker) => Err(DynWalkerError::NeverWalked(walker)),
DynWalkerState::Done(value) => Ok(value),
DynWalkerState::Err(err) => Err(DynWalkerError::Walker(err)),
}
}
#[inline(always)]
pub fn into_inner(self) -> Result<W, DynWalkerError<'ctx, W, E>> {
match self.state {
DynWalkerState::Walking => Err(DynWalkerError::WalkNeverFinished),
DynWalkerState::Pending(walker) => Ok(walker),
DynWalkerState::Done(value) => Err(DynWalkerError::WasWalked(value)),
DynWalkerState::Err(err) => Err(DynWalkerError::Walker(err)),
}
}
}
impl<'lt, 'ctx: 'lt, W: Walker<'ctx, E> + 'lt, E: Environment> WalkerObjSafe<'lt, 'ctx, E>
for DynWalkerAdapter<'ctx, W, E>
where
Self: DynBind<E>,
{
#[inline(always)]
fn walk<'a: 'c, 'b: 'c, 'd: 'b, 'c>(
&'a mut self,
visitor: DynVisitor<'b, 'd, 'ctx, E>,
) -> Canonical<'c, Flow, E>
where
Self: 'a,
{
if let DynWalkerState::Pending(walker) =
core::mem::replace(&mut self.state, DynWalkerState::Walking)
{
E::value((self, visitor))
.update_map(walker, |walker, (this, visitor)| {
// Walk the walker.
walker
.walk(visitor.cast())
.map(this, |this, value| match value {
Ok(value) => {
this.state = DynWalkerState::Done(value);
Flow::Done
}
Err(err) => {
this.state = DynWalkerState::Err(err);
// Signal that control flow should stop as soon as possible as we
// are in an error state.
Flow::Err
}
})
.cast()
})
.map((), |_, (_, value)| value)
.cast()
} else {
// Can't do anything if the walker has already been walked.
E::value(Flow::Done).cast()
}
}
}