use core::ops::ControlFlow;
use crate::{
effect::{Effective, ErasedEffective, Ss},
never::Never,
Flow, Status,
};
mod recoverable;
mod request_hint;
mod sequence;
mod tag;
mod value;
pub use recoverable::*;
pub use request_hint::*;
pub use sequence::*;
pub use tag::*;
pub use value::*;
#[derive(Copy, Clone)]
#[must_use]
pub enum VisitResult<S> {
/// The protocol was not used.
///
/// This either means the visitor doesn't support the protocol at all, or
/// it didn't want to use the protocol right now.
Skipped(S),
/// How control flow should proceed.
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 fn map_skipped<R, F>(self, f: F) -> VisitResult<R>
where
F: FnOnce(S) -> R,
{
match self {
VisitResult::Skipped(s) => VisitResult::Skipped(f(s)),
VisitResult::Control(flow) => VisitResult::Control(flow),
}
}
}
pub trait EffectiveVisitExt<'lt>: Effective<'lt> {
fn if_skipped<'ctx, 'wrap, Ctx, F>(
self,
f: F,
) -> ErasedEffective<'wrap, (Ctx, VisitResult<()>), Self::Effect>
where
Self: Effective<'lt, Output = (Ctx, VisitResult<()>)>,
F: 'wrap
+ Ss
+ for<'temp> FnOnce(
&'temp mut Ctx,
)
-> ErasedEffective<'temp, VisitResult<()>, Self::Effect, &'ctx ()>,
Ctx: Ss,
'ctx: 'lt,
'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, 'wrap, Ctx, F>(
self,
f: F,
) -> ErasedEffective<'wrap, (Ctx, VisitResult<()>), Self::Effect>
where
Self: Effective<'lt, Output = (Ctx, VisitResult<()>)>,
F: 'wrap
+ for<'temp> FnOnce(
&'temp mut Ctx,
)
-> ErasedEffective<'temp, VisitResult<()>, Self::Effect, &'ctx ()>,
'ctx: 'lt,
Ctx: Ss,
F: Ss,
'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) {
(Self::Skipped(_), Self::Skipped(_)) => true,
(Self::Control(l0), Self::Control(r0)) => l0 == r0,
_ => false,
}
}
}
impl<S> core::fmt::Debug for VisitResult<S> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Skipped(_) => f.debug_tuple("Skipped").finish(),
Self::Control(arg0) => f.debug_tuple("Control").field(arg0).finish(),
}
}
}
impl<S> From<Flow> for VisitResult<S> {
fn from(value: Flow) -> Self {
Self::Control(value)
}
}