Diffstat (limited to 'src/walk.rs')
-rw-r--r--src/walk.rs80
1 files changed, 79 insertions, 1 deletions
diff --git a/src/walk.rs b/src/walk.rs
index f0c5c65..a119965 100644
--- a/src/walk.rs
+++ b/src/walk.rs
@@ -1,5 +1,7 @@
pub mod walkers;
+use core::ops::ControlFlow;
+
use crate::{
effect::{Effect, Future},
protocol::Visitor,
@@ -8,7 +10,12 @@ use crate::{
/// A type that can be walked.
pub trait Walk<'ctx>: WalkerTypes<'ctx> + Sized {
/// The walker for the type.
- type Walker<E: Effect<'ctx>>: Walker<'ctx, Error = Self::Error, Output = Self::Output, Effect = E>;
+ type Walker<E: Effect<'ctx>>: Walker<
+ 'ctx,
+ Error = Self::Error,
+ Output = Self::Output,
+ Effect = E,
+ >;
fn into_walker<E: Effect<'ctx>>(self) -> Self::Walker<E>;
}
@@ -44,3 +51,74 @@ pub trait Walker<'ctx>: WalkerTypes<'ctx> + Send {
where
Self: 'a;
}
+
+pub trait DynWalker<'ctx>: Send {
+ type Effect: Effect<'ctx>;
+
+ fn walk<'a>(
+ &'a mut self,
+ visitor: Visitor<'a, 'ctx>,
+ ) -> Future<'a, 'ctx, ControlFlow<(), ()>, Self::Effect>
+ where
+ Self: 'a;
+}
+
+enum DynWalkerState<'ctx, W: WalkerTypes<'ctx>> {
+ Walking,
+ Pending(W),
+ Done(Result<W::Output, W::Error>),
+}
+
+pub enum DynWalkerError<'ctx, W: WalkerTypes<'ctx>> {
+ NeverWalked(W),
+
+ /// This can only happen if a panic happens furing the walk and is then caught before calling
+ /// finish..
+ WalkNeverFinished,
+
+ Walker(W::Error),
+}
+
+pub struct DynWalkerAdapter<'ctx, W: WalkerTypes<'ctx>> {
+ state: DynWalkerState<'ctx, W>,
+}
+
+impl<'ctx, W: WalkerTypes<'ctx>> DynWalkerAdapter<'ctx, W> {
+ pub fn new(walker: W) -> Self {
+ Self {
+ state: DynWalkerState::Pending(walker),
+ }
+ }
+
+ pub fn finish(self) -> Result<W::Output, DynWalkerError<'ctx, W>> {
+ match self.state {
+ DynWalkerState::Walking => Err(DynWalkerError::WalkNeverFinished),
+ DynWalkerState::Pending(walker) => Err(DynWalkerError::NeverWalked(walker)),
+ DynWalkerState::Done(result) => result.map_err(DynWalkerError::Walker),
+ }
+ }
+}
+
+impl<'ctx, W: Walker<'ctx>> DynWalker<'ctx> for DynWalkerAdapter<'ctx, W> {
+ type Effect = W::Effect;
+
+ fn walk<'a>(
+ &'a mut self,
+ visitor: Visitor<'a, 'ctx>,
+ ) -> Future<'a, 'ctx, ControlFlow<(), ()>, Self::Effect>
+ where
+ Self: 'a,
+ {
+ Self::Effect::wrap(async {
+ if let DynWalkerState::Pending(walker) =
+ core::mem::replace(&mut self.state, DynWalkerState::Walking)
+ {
+ // Walk the walker.
+ self.state = DynWalkerState::Done(walker.walk(visitor).await);
+ } else {
+ // Can't do anything if the walker has already been walked.
+ }
+ ControlFlow::Continue(())
+ })
+ }
+}