use core::fmt::{Debug, Display}; use crate::hkt::BorrowsCtx; use effectful::{ bound::Dynamic, effective::{Effective, Canonical}, environment::{Environment}, tri, SendSync, DynBind }; use crate::{ trait_by_id, any::{type_name, AnyTrait, OwnedStatic, TempBorrowedStatic}, build::BuilderTypes, hkt::Marker, protocol::{ visitor::{ tags, DynSequenceScope, EffectiveVisitExt as _, RequestHint, Sequence, SequenceHint, Tag, TagConst, TagHint, Value, VisitResult, }, walker::hint::hint_protocol, AsVisitor, DynVisitor, DynWalker, }, walk::DynWalkerObjSafe, Builder, Flow, }; use super::NoopVisitor; /// A builder for a struct. #[derive(SendSync)] pub struct StructBuilder<'lt, 'ctx, Info, Mode, E: Environment> where Info: StructTypeInfo<'lt, 'ctx, Mode, E>, { inner: Inner<'lt, 'ctx, Info, Mode, E>, } #[derive(SendSync)] enum Inner<'lt, 'ctx, Info, Mode, E: Environment> where Info: StructTypeInfo<'lt, 'ctx, Mode, E>, { Temp, Seed(Info::Seed), Builders { /// The builders for all the struct's fields. builders: Info::Builders, /// The kind of struct the builder is expecting. kind: StructKind, }, Value(Dynamic), } /// Structs are either tuple-like or map-like. #[derive(SendSync)] enum StructKind { /// A tuple-like struct uses the order of the fields. Tuple, /// A map-like struct uses field names. Map, } /// Information about how a struct type interacts with the [`StructBuilder`]. /// /// This trait is *not* implemented by the struct type itself to allow many /// of these to exist per struct. /// /// The `Mode` generic allows implementations to change depending on the mode the user gives. /// It is not used by the trait directly. pub trait StructTypeInfo<'lt, 'ctx, Mode: 'ctx, E: Environment>: 'lt { /// A struct of builders for each field. type Builders: DynBind; /// The seed value needed to make the builders. type Seed: DynBind; /// An enum of the fields. /// /// These markers act in place of the field names. type FieldMarker: DynBind + Copy + Display; /// The error type that can be generated while building the struct. type Error: DynBind + Debug + Display; /// The kind of type for enabling the direct value protocol. type ValueT: type_name::Static; /// The struct type this info is for. type T; const FIELD_COUNT: usize; /// Create a set of builders from a seed value. fn new_builders<'a>(seed: Self::Seed) -> Canonical<'a, Self::Builders, E>; /// Finish building the struct value. fn from_builders<'a>( builders: Self::Builders, ) -> Canonical<'a, Result, Self::Error>, E> where Dynamic: DynBind; /// Get the visitor for a field. /// /// This is how [`StructBuilder`] picks a field builder to use. fn as_visitor<'a, 'b>( marker: Self::FieldMarker, builders: &'a mut Self::Builders, ) -> DynVisitor<'a, 'b, 'ctx, E> where 'lt: 'b; /// Get a field marker from the index of the field. /// /// This should be the definition order as seen in the Rust source code. fn marker_from_index(index: usize) -> Option; /// Get a field marker from a field name. fn marker_from_name(name: &str) -> Option; /// Get the value from the value protocol. fn from_value<'a>(value: type_name::Lowered<'a, 'ctx, Self::ValueT>) -> Self::T; } /// Error that [`StructBuilder`] returns. #[derive(SendSync)] pub struct StructError<'lt, 'ctx, Info, M, E: Environment> where Info: StructTypeInfo<'lt, 'ctx, M, E>, { /// Error from the struct info definition. error: Info::Error, } impl<'lt, 'ctx, Info, Mode, E: Environment> StructError<'lt, 'ctx, Info, Mode, E> where Info: StructTypeInfo<'lt, 'ctx, Mode, E>, { fn from_field_err(error: Info::Error) -> Self { Self { error } } } impl<'lt, 'ctx, Info, Mode, E: Environment> Debug for StructError<'lt, 'ctx, Info, Mode, E> where Info: StructTypeInfo<'lt, 'ctx, Mode, E>, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_tuple("StructError").field(&self.error).finish() } } impl<'lt, 'ctx, Info, Mode, E: Environment> Display for StructError<'lt, 'ctx, Info, Mode, E> where Info: StructTypeInfo<'lt, 'ctx, Mode, E>, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { Display::fmt(&self.error, f) } } impl<'lt, 'ctx, Info, Mode, E: Environment> BuilderTypes for StructBuilder<'lt, 'ctx, Info, Mode, E> where Info: StructTypeInfo<'lt, 'ctx, Mode, E>, Dynamic: DynBind, { type Seed = Info::Seed; type Error = StructError<'lt, 'ctx, Info, Mode, E>; type Output = Dynamic; type Value = Info::T; fn unwrap_output(output: Self::Output) -> Self::Value { output.0 } } impl<'lt, 'ctx, Info, Mode: 'ctx, E: Environment> StructBuilder<'lt, 'ctx, Info, Mode, E> where Self: DynBind, Info: StructTypeInfo<'lt, 'ctx, Mode, E>, { fn make_builders<'e>(&'e mut self) -> Canonical<'e, (), E> where 'ctx: 'e, { match core::mem::replace(&mut self.inner, Inner::Temp) { Inner::Seed(seed) => Info::new_builders(seed) .map(self, |this, builders| { this.inner = Inner::Builders { builders, kind: StructKind::Tuple, }; }) .cast(), inner => { self.inner = inner; E::value(()).cast() } } } } impl<'lt, 'ctx, Info, Mode: 'ctx, E: Environment> Builder<'lt, 'ctx, E> for StructBuilder<'lt, 'ctx, Info, Mode, E> where Self: DynBind, Info: StructTypeInfo<'lt, 'ctx, Mode, E>, Dynamic: DynBind, Dynamic>: DynBind, Dynamic>: DynBind, for<'a> Dynamic<&'a Info::T>: DynBind, for<'b, 'c> Dynamic>: DynBind, Dynamic>: DynBind, for<'b, 'c> Dynamic<&'b type_name::Lowered<'b, 'c, Info::ValueT>>: DynBind, for<'b> Dynamic>: DynBind, { fn from_seed<'a>(seed: Self::Seed) -> Canonical<'a, Self, E> where Self: 'a, { E::value(Self { inner: Inner::Seed(seed), }) .cast() } fn build<'a>(self) -> Canonical<'a, Result, E> where Self: 'a, { match self.inner { Inner::Temp => unreachable!(), Inner::Seed(seed) => { // We may be able to make a value from just the seed. Info::new_builders(seed) .then((), |_, builders| Info::from_builders(builders)) .map((), |_, result| result.map_err(StructError::from_field_err)) .cast() } Inner::Builders { builders, .. } => { // Create the value from the builders. Info::from_builders(builders) .map((), |_, result| result.map_err(StructError::from_field_err)) .cast() } // Use the value as is. Inner::Value(value) => E::value(Ok(value)).cast(), } } } impl<'lt, 'ctx, Info, Mode: 'ctx, E: Environment> AsVisitor<'lt, 'ctx, E> for StructBuilder<'lt, 'ctx, Info, Mode, E> where Self: DynBind, Info: StructTypeInfo<'lt, 'ctx, Mode, E>, Dynamic: DynBind, Dynamic>: DynBind, for<'a> Dynamic<&'a Info::T>: DynBind, Dynamic>: DynBind, Dynamic>: DynBind, for<'b, 'c> Dynamic>: DynBind, for<'b> Dynamic>: DynBind, for<'b, 'c> Dynamic<&'b type_name::Lowered<'b, 'c, Info::ValueT>>: DynBind, { fn as_visitor(&mut self) -> DynVisitor<'_, 'lt, 'ctx, E> { DynVisitor(self) } } impl<'lt, 'ctx: 'lt, Info, Mode: 'lt, E: Environment> AnyTrait<'lt, 'ctx> for StructBuilder<'lt, 'ctx, Info, Mode, E> where for<'a> Dynamic<&'a Info::T>: DynBind, E: Environment, Self: DynBind, Info: StructTypeInfo<'lt, 'ctx, Mode, E>, Dynamic: DynBind, Dynamic>: DynBind, Dynamic>: DynBind, for<'b, 'c> Dynamic>: DynBind, for<'b, 'c> Dynamic<&'b type_name::Lowered<'b, 'c, Info::ValueT>>: DynBind, for<'b> Dynamic>: DynBind, Dynamic>: DynBind, Dynamic: DynBind, Mode: 'ctx, { fn upcast_by_id_mut<'a>( &'a mut self, id: crate::any::WithLtTypeId<'lt, 'ctx>, ) -> Option> where 'lt: 'a, { dbg!(&id); trait_by_id!(&mut self, id, { type Impls = (dyn Tag<'ctx, tags::Map, E>, dyn Sequence<'ctx, E>); }); None } } // any_trait! { // impl['ctx, Info, Mode][E] StructBuilder<'ctx, Info, Mode, E> = [ // RequestHintProto, // ValueProto, // TagProto, // TagProto, // SequenceProto // ] where // for<'a> Dynamic<&'a Info::T>: DynBind, // E: Environment, // Self: DynBind, // Info: StructTypeInfo<'ctx, Mode, E>, // Dynamic: DynBind, // Dynamic>: DynBind, // Dynamic>: DynBind, // for<'b, 'c> Dynamic>: DynBind, // for<'b, 'c> Dynamic<&'b TypeName::T<'b, 'c, Info::ValueT, E>>: DynBind, // for<'b> Dynamic>: DynBind, // Dynamic>: DynBind, // Dynamic: DynBind, // Mode: 'ctx, // } impl<'lt, 'ctx: 'lt, Info, Mode: 'ctx, E> RequestHint<'ctx, E> for StructBuilder<'lt, 'ctx, Info, Mode, E> where Self: DynBind, Info: StructTypeInfo<'lt, 'ctx, Mode, E>, Dynamic: DynBind, for<'a> Dynamic<&'a Info::T>: DynBind, for<'b, 'c> Dynamic>: DynBind, for<'b, 'c> Dynamic<&'b type_name::Lowered<'b, 'c, Info::ValueT>>: DynBind, Dynamic>: DynBind, for<'b> Dynamic>: DynBind, Dynamic>: DynBind, Dynamic>: DynBind, E: Environment, { #[inline(always)] fn request_hint<'this: 'e, 'walker: 'e, 'd: 'e, 'e>( &'this mut self, walker: DynWalker<'walker, 'd, 'ctx, E>, ) -> Canonical<'e, VisitResult, E> where 'ctx: 'this + 'walker + 'd, { E::value((self, walker)) .update_map((), |_, (this, walker)| { // Start with a hint to use the value protocol to directly transfer the // struct value. hint_protocol::, _, _>(walker.cast(), *this, ()) .cast() }) .cast::<()>() .if_not_finished((), |_, (this, walker)| { // Next hint that the struct protocol should be used to switch into // map-like if the walker supports it. hint_protocol::, _, _>( walker.cast(), *this, TagHint { kind: TagConst }, ) .cast() }) .cast::<()>() .if_skipped((), |_, (this, walker)| { // If the struct hint didn't work, // then hint that the map protocol should be used to switch into // map-like if the walker supports it. hint_protocol::, _, _>( walker.cast(), *this, TagHint { kind: TagConst }, ) .cast() }) .cast::<()>() .if_not_finished((), |_, (this, walker)| { // Lastly hint to use a sequence to get the field values. // We hint with the exact number of fields we are expecting. hint_protocol::, _, _>( walker.cast(), *this, SequenceHint { len: (Info::FIELD_COUNT, Some(Info::FIELD_COUNT)), }, ) .cast() }) .cast::<()>() .map((), |_, (_, x)| x) .cast() } } /// Allows for a walker to directly give the struct value. /// /// This skips needing to go through each field individually. impl<'lt, 'ctx, Info, Mode, E> Value<'ctx, Info::ValueT, E> for StructBuilder<'lt, 'ctx, Info, Mode, E> where Self: DynBind, Info: StructTypeInfo<'lt, 'ctx, Mode, E>, E: Environment, { fn visit<'this: 'value, 'value: 'e, 'e>( &'this mut self, value: type_name::Lowered<'value, 'ctx, Info::ValueT>, ) -> Canonical<'e, VisitResult>>, E> where type_name::Lowered<'value, 'ctx, Info::ValueT>: Sized, Dynamic>: DynBind, 'ctx: 'this + 'value, { // Get the value from what we got from the walker. self.inner = Inner::Value(Dynamic(Info::from_value(value))); // Since we have the struct value we are done. E::value(Flow::Done.into()).cast() } } /// Allows for the walker to use field names. /// /// By default [`StructBuilder`] expects a tuple-like struct. impl<'lt, 'ctx, Info, Mode: 'ctx, E> Tag<'ctx, tags::Struct, E> for StructBuilder<'lt, 'ctx, Info, Mode, E> where Self: DynBind, Info: StructTypeInfo<'lt, 'ctx, Mode, E>, E: Environment, { fn visit<'this: 'e, 'walker: 'e, 'd: 'e, 'e>( &'this mut self, _kind: tags::Struct, walker: DynWalkerObjSafe<'walker, 'd, 'ctx, E>, ) -> Canonical<'e, VisitResult, E> where 'ctx: 'd { // If this protocol is used then we need to create the builders. E::value(self) .update_map((), |_, this| this.make_builders().cast()) .then(walker, |walker, (this, _)| { if let Inner::Builders { kind, .. } = &mut this.inner { // This signals to go into map mode for the sequence. *kind = StructKind::Map; } // Walk the walker so nothing complains. NoopVisitor::walk_dyn(walker) }) .cast() } } /// Allows for the walker to use field names. /// /// By default [`StructBuilder`] expects a tuple-like struct. impl<'lt, 'ctx, Info, Mode: 'ctx, E> Tag<'ctx, tags::Map, E> for StructBuilder<'lt, 'ctx, Info, Mode, E> where Self: DynBind, Info: StructTypeInfo<'lt, 'ctx, Mode, E>, E: Environment, { fn visit<'this: 'e, 'walker: 'e, 'd: 'e, 'e>( &'this mut self, _kind: tags::Map, walker: DynWalkerObjSafe<'walker, 'd, 'ctx, E>, ) -> Canonical<'e, VisitResult, E> where 'ctx: 'd { // If this protocol is used then we need to create the builders. E::value(self) .update_map((), |_, this| this.make_builders().cast()) .then(walker, |walker, (this, _)| { if let Inner::Builders { kind, .. } = &mut this.inner { // This signals to go into map mode for the sequence. *kind = StructKind::Map; } // Walk the walker so nothing complains. NoopVisitor::walk_dyn(walker) }) .cast() } } /// Main protocol allowing a sequence of field values. /// /// By default this will use each sequence element as a field value. /// /// If the [`tags::Struct`] or [`tags::Map`] tags are used then this will expect /// a sequence of key value pairs. Where the key is the field name. impl<'lt, 'ctx: 'lt, Info, Mode: 'ctx, E> Sequence<'ctx, E> for StructBuilder<'lt, 'ctx, Info, Mode, E> where Self: DynBind, Info: StructTypeInfo<'lt, 'ctx, Mode, E>, for<'b> Dynamic>: DynBind, Dynamic>: DynBind, Dynamic>: DynBind, Dynamic>: DynBind, E: Environment, { fn visit<'a: 'c, 'b: 'c, 'c>( &'a mut self, scope: DynSequenceScope<'b, 'ctx, E>, ) -> Canonical<'c, VisitResult, E> where 'ctx: 'a + 'b + 'c, { // If this protocol is used then we need to create the builders. E::value(self) .update_map((), |_, this| this.make_builders().cast()) .update_map(scope, |scope, (this, _)| { match &mut this.inner { // We should treat the sequence as just values. Inner::Builders { builders, kind: StructKind::Tuple, } => { // Tuple-like is based on the index of the field. let index = 0; // Loop through all the fields getting a value for each one. E::value((scope, builders)) .repeat(index, |index, (scope, builders)| { // Get the marker for the field at this index. let marker = tri!(Info::marker_from_index(*index)); // Move to the next field for the next iteration. *index += 1; // Select the visitor for this field. let visitor = Info::as_visitor(marker, builders); // Visit the next item in the sequence. scope .next(visitor) .map((), |_, x| Flow::to_control_flow(x)) .cast() }, (), | _, _, _, x| x) .cast::<()>() } // We should treat the sequence as key value pairs. Inner::Builders { builders, kind: StructKind::Map, } => { // A visitor that knows how to use field names. let visitor = FieldVisitor:: { builders, marker: None, _marker: Default::default(), _m: BorrowsCtx::NEW }; // Loop through all the elements in the sequence. // Each key value pair will be mapped to a field. E::value((visitor, scope)) .repeat((), |_, (visitor, scope)| { // Visit the next element of the sequence. // When there are no more items in the sequence then the loop ends. scope .next(DynVisitor(visitor)) .map((), |_, x| Flow::to_control_flow(x)) .cast() }, (), |_, _, _, x| x) .cast() } // If we don't have the builders ... we can't do anything. // This would only happen if the walker gives the value directly // then gives a sequence. _ => E::value(Flow::Done).cast(), } .map((), |_, x| Into::into(x)) .cast() }) .map((), |_, (_, x)| x) .cast() } } #[derive(SendSync)] struct FieldVisitor<'a, 'lt, 'ctx, I: StructTypeInfo<'lt, 'ctx, M, E>, M, E: Environment> { builders: &'a mut I::Builders, marker: Option, _marker: Marker, _m: BorrowsCtx<'lt, 'ctx> } impl<'e, 'lt: 'e, 'ctx: 'lt, I: StructTypeInfo<'lt, 'ctx, M, E>, M: 'lt, E: Environment> AnyTrait<'e, 'ctx> for FieldVisitor<'e, 'lt, 'ctx, I, M, E> where E: Environment, I: StructTypeInfo<'lt, 'ctx, M, E>, Dynamic>: DynBind, Dynamic>: DynBind, Dynamic>: DynBind, for<'b> Dynamic>: DynBind, { fn upcast_by_id_mut<'a>( &'a mut self, id: crate::any::WithLtTypeId<'e, 'ctx>, ) -> Option> where 'e: 'a { dbg!(&id); trait_by_id!(&mut self, id, { type Impls = (dyn Tag<'ctx, tags::Key, E>); }); self.marker.and_then(|marker| { let x = I::as_visitor(marker, self.builders).0; x.upcast_by_id_mut(id) }) } } // any_trait! { // impl['ctx, 'a, I, M][E] FieldVisitor<'a, 'ctx, I, M, E> = [ // TagProto, // ] ref { // let (_this, _id); // } else ref { // None // } mut { // let (this, id); // } else mut { // this.marker.and_then(|marker| { // I::as_visitor(marker, this.builders).0.upcast_to_id_mut(id) // }) // } where // E: Environment, // I: StructTypeInfo<'ctx, M, E>, // Dynamic>: DynBind, // Dynamic>: DynBind, // Dynamic>: DynBind, // for<'b> Dynamic>: DynBind, // } impl<'d, 'lt, 'ctx: 'lt, I, M, E> Tag<'ctx, tags::Key, E> for FieldVisitor<'d, 'lt, 'ctx, I, M, E> where E: Environment, I: StructTypeInfo<'lt, 'ctx, M, E>, Dynamic>: DynBind, Dynamic>: DynBind, Dynamic>: DynBind, for<'a> Dynamic>: DynBind, { fn visit<'a: 'c, 'b: 'c, 'e: 'c, 'c>( &'a mut self, _key: tags::Key, walker: DynWalkerObjSafe<'b, 'e, 'ctx, E>, ) -> Canonical<'c, VisitResult, E> where 'ctx: 'e { let visitor = NameVisitor:: { field_marker: None, _marker: Default::default(), _m: BorrowsCtx::NEW, }; E::value((self, visitor, walker)) .update_map((), |_, (_, visitor, walker)| { walker.walk(DynVisitor(visitor)).cast() }) .map((), |_, ((this, visitor, _), flow)| { this.marker = visitor.field_marker; flow.to_continue().into() }) .cast() } } #[derive(SendSync)] struct NameVisitor<'lt, 'ctx, I: StructTypeInfo<'lt, 'ctx, M, E>, M, E: Environment> { field_marker: Option, _marker: Marker, _m: BorrowsCtx<'lt, 'ctx>, } impl<'lt, 'ctx: 'lt, I: StructTypeInfo<'lt, 'ctx, M, E>, M: 'lt, E: Environment> AnyTrait<'lt, 'ctx> for NameVisitor<'lt, 'ctx, I, M, E> where E: Environment, I: StructTypeInfo<'lt, 'ctx, M, E>, Dynamic>: DynBind, Dynamic>: DynBind, Dynamic>: DynBind, for<'a> Dynamic>: DynBind, { } // any_trait! { // impl['ctx, I, M][E] NameVisitor<'ctx, I, M, E> = [ // ValueProto, E>, // ValueProto, E>, // ValueProto, E>, // ValueProto, E>, // ] where // E: Environment, // I: StructTypeInfo<'ctx, M, E>, // Dynamic>: DynBind, // Dynamic>: DynBind, // Dynamic>: DynBind, // for<'a> Dynamic>: DynBind, // } impl<'lt, 'ctx, I, M, E> Value<'ctx, OwnedStatic, E> for NameVisitor<'lt, 'ctx, I, M, E> where E: Environment, I: StructTypeInfo<'lt, 'ctx, M, E>, { fn visit<'this: 'value, 'value: 'e, 'e>( &'this mut self, value: type_name::Lowered<'value, 'ctx, OwnedStatic>, ) -> Canonical<'e, VisitResult>>>, E> where type_name::Lowered<'value, 'ctx, OwnedStatic>: Sized, Dynamic>>: DynBind, 'ctx: 'this + 'value, { self.field_marker = I::marker_from_index(value.0); E::value(VisitResult::Control(Flow::Done)).cast() } } // impl<'ctx, I, M, E> Value<'ctx, TempBorrowedStatic, E> for NameVisitor<'ctx, I, M, E> // where // E: Environment, // I: StructTypeInfo<'ctx, M, E>, // for<'a> Dynamic>: DynBind, // { // fn visit<'a>( // &'a mut self, // TempBorrowedStatic(name): TypeName::T<'a, 'ctx, TempBorrowedStaticHrt, E>, // ) -> Canonical<'a, VisitResult, E>>>, E> // where // TypeName::T<'a, 'ctx, TempBorrowedStaticHrt, E>: Sized, // 'ctx: 'a, // { // self.field_marker = I::marker_from_name(name); // // E::value(VisitResult::Control(Flow::Done)).cast() // } // } impl<'lt, 'ctx, I, M, E> Value<'ctx, OwnedStatic<&'static str>, E> for NameVisitor<'lt, 'ctx, I, M, E> where E: Environment, I: StructTypeInfo<'lt, 'ctx, M, E>, Dynamic>: DynBind, { fn visit<'this: 'value, 'value: 'e, 'e>( &'this mut self, value: type_name::Lowered<'value, 'ctx, OwnedStatic<&'static str>>, ) -> Canonical< 'e, VisitResult>>>, E, > where type_name::Lowered<'value, 'ctx, OwnedStatic<&'static str>>: Sized, Dynamic>>: DynBind, 'ctx: 'this + 'value, { self.field_marker = I::marker_from_name(value.0); E::value(VisitResult::Control(Flow::Done)).cast() } } impl<'lt, 'ctx, I, M, E> Value<'ctx, OwnedStatic, E> for NameVisitor<'lt, 'ctx, I, M, E> where E: Environment, I: StructTypeInfo<'lt, 'ctx, M, E>, Dynamic>: DynBind, { fn visit<'this: 'value, 'value: 'e, 'e>( &'this mut self, value: type_name::Lowered<'value, 'ctx, OwnedStatic>, ) -> Canonical< 'e, VisitResult>>>, E, > where type_name::Lowered<'value, 'ctx, OwnedStatic>: Sized, Dynamic>>: DynBind, 'ctx: 'this + 'value, { self.field_marker = I::marker_from_name(&value.0); E::value(VisitResult::Control(Flow::Done)).cast() } }