Diffstat (limited to 'src/protocol/visitor.rs')
-rw-r--r--src/protocol/visitor.rs124
1 files changed, 123 insertions, 1 deletions
diff --git a/src/protocol/visitor.rs b/src/protocol/visitor.rs
index fbcfdd2..6dc58fb 100644
--- a/src/protocol/visitor.rs
+++ b/src/protocol/visitor.rs
@@ -1,4 +1,10 @@
-use crate::Flow;
+use core::ops::ControlFlow;
+
+use crate::{
+ effect::{Effective, EffectiveExt as _, ErasedEffective, Ss},
+ never::Never,
+ Flow, Status,
+};
mod recoverable;
mod request_hint;
@@ -25,6 +31,122 @@ pub enum VisitResult<S> {
Control(Flow),
}
+impl<S> VisitResult<S> {
+ pub fn unit_skipped(self) -> VisitResult<()> {
+ match self {
+ VisitResult::Skipped(_) => VisitResult::Skipped(()),
+ VisitResult::Control(flow) => VisitResult::Control(flow),
+ }
+ }
+
+ pub fn to_status(self) -> Status {
+ match self {
+ VisitResult::Skipped(_) => Status::Ok,
+ VisitResult::Control(flow) => flow.to_status(),
+ }
+ }
+
+ pub fn to_flow(self) -> Option<Flow> {
+ match self {
+ VisitResult::Skipped(_) => None,
+ VisitResult::Control(flow) => Some(flow),
+ }
+ }
+
+ pub fn dont_break_if_skipped(self) -> Option<Status> {
+ match self {
+ VisitResult::Skipped(_) => None,
+ VisitResult::Control(Flow::Continue) => Some(Status::Ok),
+ VisitResult::Control(Flow::Done) => Some(Status::Ok),
+ VisitResult::Control(Flow::Err) => Some(Status::Err),
+ }
+ }
+
+ pub fn break_if_done_or_err(self) -> Option<Status> {
+ match self {
+ VisitResult::Skipped(_) => None,
+ VisitResult::Control(Flow::Continue) => None,
+ VisitResult::Control(Flow::Done) => Some(Status::Ok),
+ VisitResult::Control(Flow::Err) => Some(Status::Err),
+ }
+ }
+
+ pub fn break_if_err(self) -> Option<Status> {
+ match self {
+ VisitResult::Skipped(_) => None,
+ VisitResult::Control(Flow::Continue) => None,
+ VisitResult::Control(Flow::Done) => None,
+ VisitResult::Control(Flow::Err) => Some(Status::Err),
+ }
+ }
+}
+
+pub trait EffectiveVisitExt<'lt>: Effective<'lt> {
+ fn if_skipped<'ctx: 'lt, 'wrap, Ctx: Ss, F: Ss>(
+ self,
+ f: F,
+ ) -> ErasedEffective<'wrap, (Ctx, VisitResult<()>), Self::Effect>
+ where
+ Self: Effective<'lt, Output = (Ctx, VisitResult<()>)>,
+ F: for<'temp> FnOnce(
+ &'temp mut Ctx,
+ )
+ -> ErasedEffective<'temp, VisitResult<()>, Self::Effect, &'ctx ()>,
+ 'lt: 'wrap,
+ {
+ self.r#do(
+ |(ctx, v)| {
+ (
+ ctx,
+ match v {
+ VisitResult::Skipped(()) => ControlFlow::Continue(()),
+ v => ControlFlow::Break(v),
+ },
+ )
+ },
+ |ctx, ()| f(ctx).cast(),
+ |_, v| ControlFlow::<_, Never>::Break(v),
+ |_, _| unreachable!(),
+ |_, _, _: Never| unreachable!(),
+ |ctx, _, v| (ctx, v),
+ )
+ }
+
+ fn if_not_finished<'ctx: 'lt, 'wrap, Ctx: Ss, F: Ss>(
+ self,
+ f: F,
+ ) -> ErasedEffective<'wrap, (Ctx, VisitResult<()>), Self::Effect>
+ where
+ Self: Effective<'lt, Output = (Ctx, VisitResult<()>)>,
+ F: for<'temp> FnOnce(
+ &'temp mut Ctx,
+ )
+ -> ErasedEffective<'temp, VisitResult<()>, Self::Effect, &'ctx ()>,
+ 'lt: 'wrap,
+ {
+ self.r#do(
+ |(ctx, v)| {
+ (
+ ctx,
+ match v {
+ VisitResult::Skipped(()) | VisitResult::Control(Flow::Continue) => {
+ ControlFlow::Continue(())
+ }
+ v => ControlFlow::Break(v),
+ },
+ )
+ },
+ |ctx, ()| f(ctx).cast(),
+ |_, v| ControlFlow::<_, Never>::Break(v),
+ |_, _| unreachable!(),
+ |_, _, _: Never| unreachable!(),
+ |ctx, _, v| (ctx, v),
+ )
+ }
+}
+
+impl<'lt, T: Effective<'lt>> EffectiveVisitExt<'lt> for T {}
+
impl<S> PartialEq for VisitResult<S> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {