use core::any::TypeId; use effectful::{ bound::SsBound, effective::{Canonical, Effective}, environment::Environment, higher_ranked::Rank1, tri, DynBind, SendSync, }; use crate::{ any::type_name, hkt::Marker, protocol::{walker::hint::HintMeta, DynVisitor}, symbol::Symbol, walk::{DynWalkerAdapter, DynWalkerError, DynWalkerObjSafe}, Walker, }; use super::VisitResult; pub mod tags { use super::*; pub type Struct = TagConst<{ Symbol::new("Struct").to_int() }>; pub type Map = TagConst<{ Symbol::new("Map").to_int() }>; pub type Variant = TagConst<{ Symbol::new("Variant").to_int() }>; pub type Key = TagConst<{ Symbol::new("Key").to_int() }>; pub type Value = TagConst<{ Symbol::new("Value").to_int() }>; pub type FieldNames = TagConst<{ Symbol::new("Field Names").to_int() }>; pub type TypeName = TagConst<{ Symbol::new("Type Name").to_int() }>; pub type TypeId = TagConst<{ Symbol::new("Type ID").to_int() }>; } pub trait TagKind: Copy + DynBind + 'static { fn symbol(&self) -> Symbol; } pub trait ConstTagKind: TagKind { const NEW: Self; } impl ConstTagKind for TagConst { const NEW: Self = TagConst; } #[derive(Copy, Clone, SendSync)] pub struct TagConst; #[derive(Copy, Clone, SendSync)] pub struct TagDyn(pub Symbol); impl TagKind for TagConst { fn symbol(&self) -> Symbol { Symbol::from_int(SYMBOL) } } impl TagConst { pub const VALUE: Symbol = Symbol::from_int(SYMBOL); } impl TagKind for TagDyn { fn symbol(&self) -> Symbol { self.0 } } pub trait Tag<'src, K: TagKind, E: Environment>: DynBind { fn visit<'r>( &'r mut self, kind: K, walker: DynWalkerObjSafe<'r, 'src, E>, ) -> Canonical<'r, VisitResult, E>; } impl<'u, 'src, K: TagKind, E> type_name::Lower<'u, 'src, &'u &'src ()> for dyn Tag<'static, K, E> where E: Environment, { type Lowered = dyn Tag<'src, K, E> + 'u; } impl<'u, 'src, K: TagKind, E> type_name::Raise<'u, 'src, &'u &'src ()> for dyn Tag<'src, K, E> + 'u where E: Environment, { type Raised = dyn Tag<'static, K, E>; } impl, E: Environment> HintMeta for dyn Tag<'static, K, E> { type Known = Rank1; type Hint = Rank1>; } impl, E: Environment> effectful::environment::InEnvironment for dyn Tag<'static, K, E> { type Env = E; } #[derive(SendSync)] pub struct TagKnown { pub kind_available: Option, } #[derive(SendSync)] pub struct TagHint { pub kind: K, } #[derive(Debug, PartialEq, Clone, Copy, SendSync)] pub enum TagErrorKind { NeverWalked, /// This can only happen if a panic happens during the walk and is then caught before calling /// finish. WalkNeverFinished, Walker(E), SkippedWasWalked, } #[derive(Debug, PartialEq, Copy, Clone, SendSync)] #[allow(unused)] pub struct TagError { symbol: Symbol, err: TagErrorKind, } impl TagError { fn new, Env: SsBound>(tag: K, err: E) -> Self { Self { symbol: tag.symbol(), err: TagErrorKind::Walker(err), } } fn never_walked, Env: SsBound>(tag: K) -> Self { Self { symbol: tag.symbol(), err: TagErrorKind::NeverWalked, } } fn walk_never_finished, Env: SsBound>(tag: K) -> Self { Self { symbol: tag.symbol(), err: TagErrorKind::WalkNeverFinished, } } fn was_walked, Env: SsBound>(tag: K) -> Self { Self { symbol: tag.symbol(), err: TagErrorKind::SkippedWasWalked, } } } #[inline(always)] pub fn visit_tag<'r, 'src, K: TagKind, E: Environment, W: crate::Walker<'src, E> + 'r>( kind: K, visitor: DynVisitor<'r, 'src, E>, walker: W, ) -> Canonical<'r, Result, TagError>, E> { // Wrap the walker to allow it to be passed to a dyn walker argument. let walker = DynWalkerAdapter::new(walker); // Give everything to the effective chain to be used as context. E::value((kind, visitor, walker)) .update_map((), |_, (kind, visitor, walker)| { // Try to visit the tag kind as given. tri!(visitor.upcast_mut::>()) .visit(*kind, walker) .map((), |_, x| VisitResult::to_flow(x)) .cast() }) .or_else_update((), |_, (kind, visitor, walker), _| { // If the tag type is already dynamic we don't need to try visiting again. if TypeId::of::() == TypeId::of::() { return E::value(None).cast(); } // Visit using the dynamic tag, but with the same symbol. tri!(visitor.upcast_mut::>()) .visit(TagDyn(kind.symbol()), walker) .map((), |_, x| VisitResult::to_flow(x)) .cast() }) .map((), |_, ((kind, _, walker), result)| match result { // The visit was performed so return the control flow. Some(flow) => Ok(VisitResult::Control(flow)), // The visit was not performed. // We need to give back the walker as we got it. None => match walker.into_inner() { Ok(walker) => Ok(VisitResult::Skipped(walker)), Err(err) => Err(map_walker_err(kind, err)), }, }) .cast() } fn map_walker_err<'ctx, K: TagKind, W: Walker<'ctx, E>, E: Environment>( kind: K, err: DynWalkerError<'ctx, W, E>, ) -> TagError { match err { DynWalkerError::Walker(err) => TagError::new(kind, err), DynWalkerError::NeverWalked(_) => TagError::never_walked(kind), DynWalkerError::WalkNeverFinished => TagError::walk_never_finished(kind), DynWalkerError::WasWalked(_) => TagError::was_walked(kind), } }