Diffstat (limited to 'src/build/builders/core/struct.rs')
-rw-r--r--src/build/builders/core/struct.rs488
1 files changed, 365 insertions, 123 deletions
diff --git a/src/build/builders/core/struct.rs b/src/build/builders/core/struct.rs
index b8ca483..aa5215e 100644
--- a/src/build/builders/core/struct.rs
+++ b/src/build/builders/core/struct.rs
@@ -1,115 +1,222 @@
-use core::ops::ControlFlow;
+use core::fmt::{Debug, Display};
use crate::{
- any::{OwnedStatic, TempBorrowedStatic, TempBorrowedStaticHrt, TypeName},
+ any::{AnyTrait, OwnedStatic, StaticType, TempBorrowedStatic, TempBorrowedStaticHrt, TypeName},
any_trait,
effect::{
- Effect, EffectExt as _, Effective, EffectiveExt as _, ErasedEffective, ReadyExt as _,
+ Effect, EffectExt as _, Effective, EffectiveExt as _, ErasedEffective, ReadyExt as _, Ss,
},
hkt::Marker,
protocol::{
visitor::{
- tags, DynSequenceScope, Sequence, SequenceProto, Tag, TagProto, Value, ValueProto,
- VisitResult,
+ tags, DynSequenceScope, EffectiveVisitExt as _, RequestHint, RequestHintProto,
+ Sequence, SequenceHint, SequenceProto, Tag, TagConst, TagHint, TagProto, Value,
+ ValueProto, VisitResult,
},
- DynVisitor,
+ walker::{self, hint::hint_protocol},
+ DynVisitor, DynWalker,
},
tri, Builder, BuilderTypes, DynWalkerObjSafe, Flow,
};
use super::NoopVisitor;
-enum StructMode {
- /// A tuple-like struct uses the order of the sequence.
+/// A builder for a struct.
+pub struct StructBuilder<'ctx, Info, Mode, E: Effect>
+where
+ Info: StructTypeInfo<'ctx, Mode>,
+{
+ inner: Inner<'ctx, Info, Mode, E>,
+}
+
+enum Inner<'ctx, Info, Mode, E: Effect>
+where
+ Info: StructTypeInfo<'ctx, Mode>,
+{
+ Temp,
+ Seed(Info::Seed),
+ Builders {
+ /// The builders for all the struct's fields.
+ builders: Info::Builders<E>,
+
+ /// The kind of struct the builder is expecting.
+ kind: StructKind,
+ },
+ Value(Info::T),
+}
+
+/// Structs are either tuple-like or map-like.
+enum StructKind {
+ /// A tuple-like struct uses the order of the fields.
Tuple,
/// A map-like struct uses field names.
Map,
}
-pub struct StructBuilder<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> {
- builders: I::Builders,
- mode: StructMode,
- _generics: Marker<E>,
-}
+/// 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<'ctx, Mode>: 'static {
+ /// A struct of builders for each field.
+ type Builders<E: Effect>: Ss;
-pub trait StructTypeInfo<'ctx, M, E: Effect>: 'static {
- type Builders: Send + Sync;
+ /// The seed value needed to make the builders.
+ type Seed: Ss;
- type Seed: Send + Sync;
+ /// An enum of the fields.
+ ///
+ /// These markers act in place of the field names.
+ type FieldMarker: Ss + Copy + Display;
- type FieldMarker: Send + Sync + Copy + core::fmt::Debug;
+ /// The error type that can be generated while building the struct.
+ type Error: Ss + Debug + Display;
- type Error: Send + Sync + core::fmt::Debug;
+ /// The kind of type for enabling the direct value protocol.
+ type ValueT: TypeName::MemberType;
- type T: Send + Sync;
+ /// The struct type this info is for.
+ type T: Ss;
- fn new_builders<'a>(seed: Self::Seed) -> ErasedEffective<'a, Self::Builders, E>;
+ const FIELD_COUNT: usize;
- fn from_builders<'a>(
- builders: Self::Builders,
+ /// Create a set of builders from a seed value.
+ fn new_builders<'a, E: Effect>(seed: Self::Seed) -> ErasedEffective<'a, Self::Builders<E>, E>;
+
+ /// Finish building the struct value.
+ fn from_builders<'a, E: Effect>(
+ builders: Self::Builders<E>,
) -> ErasedEffective<'a, Result<Self::T, Self::Error>, E>;
- fn as_visitor<'a>(
+ /// Get the visitor for a field.
+ ///
+ /// This is how [`StructBuilder`] picks a field builder to use.
+ fn as_visitor<'a, E: Effect>(
marker: Self::FieldMarker,
- builders: &'a mut Self::Builders,
+ builders: &'a mut Self::Builders<E>,
) -> DynVisitor<'a, 'ctx>;
+ /// 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<Self::FieldMarker>;
+
+ /// Get a field marker from a field name.
fn marker_from_name(name: &str) -> Option<Self::FieldMarker>;
+
+ /// Get the value from the value protocol.
+ fn from_value<'a>(value: TypeName::T<'a, 'ctx, Self::ValueT>) -> Self::T;
}
-pub struct StructError<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> {
- error: I::Error,
+/// Error that [`StructBuilder`] returns.
+pub struct StructError<'ctx, Info, M>
+where
+ Info: StructTypeInfo<'ctx, M>,
+{
+ /// Error from the struct info definition.
+ error: Info::Error,
+}
+
+impl<'ctx, Info, Mode> StructError<'ctx, Info, Mode>
+where
+ Info: StructTypeInfo<'ctx, Mode>,
+{
+ fn from_field_err(error: Info::Error) -> Self {
+ Self { error }
+ }
}
-impl<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> core::fmt::Debug
- for StructError<'ctx, I, M, E>
+impl<'ctx, Info, Mode> Debug for StructError<'ctx, Info, Mode>
+where
+ Info: StructTypeInfo<'ctx, Mode>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- f.debug_struct("StructError")
- .field("error", &self.error)
- .finish()
+ f.debug_tuple("StructError").field(&self.error).finish()
}
}
-impl<'ctx, I, M, E: Effect> BuilderTypes for StructBuilder<'ctx, I, M, E>
+impl<'ctx, Info, Mode> Display for StructError<'ctx, Info, Mode>
where
- I: StructTypeInfo<'ctx, M, E>,
+ Info: StructTypeInfo<'ctx, Mode>,
{
- type Seed = I::Seed;
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ Display::fmt(&self.error, f)
+ }
+}
- type Error = StructError<'ctx, I, M, E>;
+impl<'ctx, Info, Mode, E: Effect> BuilderTypes for StructBuilder<'ctx, Info, Mode, E>
+where
+ Info: StructTypeInfo<'ctx, Mode>,
+{
+ type Seed = Info::Seed;
+
+ type Error = StructError<'ctx, Info, Mode>;
- type Value = I::T;
+ type Value = Info::T;
}
-impl<'ctx, I, M, E> Builder<'ctx, E> for StructBuilder<'ctx, I, M, E>
+impl<'ctx, Info, Mode: 'ctx, E: Effect> StructBuilder<'ctx, Info, Mode, E>
where
- I: StructTypeInfo<'ctx, M, E>,
- E: Effect,
+ Info: StructTypeInfo<'ctx, Mode>,
+{
+ fn into_builders<'e>(&mut self) -> ErasedEffective<'e, (), E>
+ where
+ 'ctx: 'e,
+ {
+ match core::mem::replace(&mut self.inner, Inner::Temp) {
+ Inner::Seed(seed) => Info::new_builders::<E>(seed).map(|builders| {
+ self.inner = Inner::Builders {
+ builders,
+ kind: StructKind::Tuple,
+ };
+ }),
+ inner => {
+ self.inner = inner;
+
+ ().ready()
+ }
+ }
+ }
+}
+
+impl<'ctx, Info, Mode: 'ctx, E: Effect> Builder<'ctx, E> for StructBuilder<'ctx, Info, Mode, E>
+where
+ Info: StructTypeInfo<'ctx, Mode>,
{
- #[inline(always)]
fn from_seed<'a>(seed: Self::Seed) -> ErasedEffective<'a, Self, E>
where
Self: 'a,
{
- I::new_builders(seed).map(|builders| {
- Self {
- builders,
- // Start in tuple mode until a struct or map tag is visited.
- mode: StructMode::Tuple,
- _generics: Default::default(),
- }
- })
+ Self {
+ inner: Inner::Seed(seed),
+ }
+ .ready()
}
fn build<'a>(self) -> ErasedEffective<'a, Result<Self::Value, Self::Error>, E>
where
Self: 'a,
{
- I::from_builders(self.builders)
- .map(|builders| builders.map_err(|err| StructError { error: err }))
+ match self.inner {
+ Inner::Temp => unreachable!(),
+ Inner::Seed(seed) => {
+ // We may be able to make a value from just the seed.
+ Info::new_builders::<E>(seed)
+ .then(|builders| Info::from_builders(builders))
+ .map(|result| result.map_err(StructError::from_field_err))
+ }
+ Inner::Builders { builders, .. } => {
+ // Create the value from the builders.
+ Info::from_builders(builders)
+ .map(|result| result.map_err(StructError::from_field_err))
+ }
+ // Use the value as is.
+ Inner::Value(value) => Ok(value).ready(),
+ }
}
fn as_visitor(&mut self) -> DynVisitor<'_, 'ctx> {
@@ -118,98 +225,237 @@ where
}
any_trait! {
- impl['ctx, I, M, E] StructBuilder<'ctx, I, M, E> = [
+ impl['ctx, Info, Mode, E] StructBuilder<'ctx, Info, Mode, E> = [
+ RequestHintProto<E>,
+ ValueProto<Info::ValueT, E>,
+ TagProto<tags::Struct, E>,
TagProto<tags::Map, E>,
SequenceProto<E>
] where
E: Effect,
- I: StructTypeInfo<'ctx, M, E>,
+ Info: StructTypeInfo<'ctx, Mode>,
+ Mode: 'ctx,
}
-impl<'ctx, I, M, E> Tag<'ctx, tags::Map, E> for StructBuilder<'ctx, I, M, E>
+impl<'ctx, Info, Mode: 'ctx, E> RequestHint<'ctx, E> for StructBuilder<'ctx, Info, Mode, E>
where
- I: StructTypeInfo<'ctx, M, E>,
+ Info: StructTypeInfo<'ctx, Mode>,
E: Effect,
{
#[inline(always)]
- fn visit<'a: 'c, 'b: 'c, 'c>(
+ fn request_hint<'a>(
&'a mut self,
- _kind: tags::Map,
- walker: DynWalkerObjSafe<'b, 'ctx, E>,
- ) -> ErasedEffective<'c, VisitResult<DynWalkerObjSafe<'b, 'ctx, E>>, E> {
- // This signals to go into map mode for the sequence.
- self.mode = StructMode::Map;
-
- E::ready(NoopVisitor::new()).as_ctx_map(|noop| {
- walker
- .walk(DynVisitor(noop))
- .map(|x| x.to_continue().into())
- .cast()
+ walker: DynWalker<'a, 'ctx>,
+ ) -> ErasedEffective<'a, VisitResult<DynWalker<'a, 'ctx>>, E> {
+ E::as_ctx((self, walker), |(this, walker)| {
+ // Start with a hint to use the value protocol to directly transfer the
+ // struct value.
+ hint_protocol::<ValueProto<Info::ValueT, E>, _>(walker.cast(), *this, ()).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::<TagProto<tags::Struct, E>, _>(
+ walker.cast(),
+ *this,
+ TagHint { kind: TagConst },
+ )
+ .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::<TagProto<tags::Map, E>, _>(
+ walker.cast(),
+ *this,
+ TagHint { kind: TagConst },
+ )
+ .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::<SequenceProto<E>, _>(
+ walker.cast(),
+ *this,
+ SequenceHint {
+ len: (Info::FIELD_COUNT, Some(Info::FIELD_COUNT)),
+ },
+ )
+ .cast()
+ })
+ .map(|((_, walker), result)| match result {
+ VisitResult::Skipped(()) => VisitResult::Skipped(walker),
+ VisitResult::Control(flow) => VisitResult::Control(flow),
})
}
}
-// A struct is a sequence of field values.
-impl<'ctx, I, M, E> Sequence<'ctx, E> for StructBuilder<'ctx, I, M, E>
+/// Allows for a walker to directly give the struct value.
+///
+/// This skips needing to go through each field individually.
+impl<'ctx, Info, Mode, E> Value<'ctx, Info::ValueT, E> for StructBuilder<'ctx, Info, Mode, E>
where
- I: StructTypeInfo<'ctx, M, E>,
+ Info: StructTypeInfo<'ctx, Mode>,
E: Effect,
{
- #[inline(always)]
- fn visit<'a: 'c, 'b: 'c, 'c>(
+ fn visit<'a>(
&'a mut self,
- scope: DynSequenceScope<'b, 'ctx, E>,
- ) -> ErasedEffective<'c, VisitResult<DynSequenceScope<'b, 'ctx, E>>, E>
+ value: TypeName::T<'a, 'ctx, Info::ValueT>,
+ ) -> ErasedEffective<'a, VisitResult<TypeName::T<'a, 'ctx, Info::ValueT>>, E>
where
- 'ctx: 'c,
+ TypeName::T<'a, 'ctx, Info::ValueT>: Send + Sized,
+ 'ctx: 'a,
{
- match self.mode {
- StructMode::Tuple => {
- // Tuple-like is based on the index of the field.
- let mut index = 0;
+ // Get the value from what we got from the walker.
+ self.inner = Inner::Value(Info::from_value(value));
- // Loop through all the fields getting a value for each one.
- E::repeat_map((self, scope), |(this, scope)| {
- // Get the marker for the field at this index.
- let marker = tri!(I::marker_from_index(index));
+ // Since we have the struct value we are done.
+ E::ready(Flow::Done.into())
+ }
+}
- // Move to the next field for the next iteration.
- index += 1;
+/// Allows for the walker to use field names.
+///
+/// By default [`StructBuilder`] expects a tuple-like struct.
+impl<'ctx, Info, Mode: 'ctx, E> Tag<'ctx, tags::Struct, E> for StructBuilder<'ctx, Info, Mode, E>
+where
+ Info: StructTypeInfo<'ctx, Mode>,
+ E: Effect,
+{
+ fn visit<'this: 'e, 'walker: 'e, 'e>(
+ &'this mut self,
+ _kind: tags::Struct,
+ walker: DynWalkerObjSafe<'walker, 'ctx, E>,
+ ) -> ErasedEffective<'e, VisitResult<DynWalkerObjSafe<'walker, 'ctx, E>>, E> {
+ // If this protocol is used then we need to create the builders.
+ E::as_ctx(self, |this| this.into_builders().cast()).then(|(this, _)| {
+ match &mut this.inner {
+ Inner::Builders { kind, .. } => {
+ // This signals to go into map mode for the sequence.
+ *kind = StructKind::Map;
+ }
+ _ => {}
+ }
- // Select the visitor for this field.
- let visitor = I::as_visitor(marker, &mut this.builders);
+ // Walk the walker so nothing complains.
+ NoopVisitor::walk_dyn(walker).cast()
+ })
+ }
+}
- // Visit the next item in the sequence.
- scope.next(visitor).map(Flow::to_control_flow).cast()
- })
+/// Allows for the walker to use field names.
+///
+/// By default [`StructBuilder`] expects a tuple-like struct.
+impl<'ctx, Info, Mode: 'ctx, E> Tag<'ctx, tags::Map, E> for StructBuilder<'ctx, Info, Mode, E>
+where
+ Info: StructTypeInfo<'ctx, Mode>,
+ E: Effect,
+{
+ fn visit<'this: 'e, 'walker: 'e, 'e>(
+ &'this mut self,
+ _kind: tags::Map,
+ walker: DynWalkerObjSafe<'walker, 'ctx, E>,
+ ) -> ErasedEffective<'e, VisitResult<DynWalkerObjSafe<'walker, 'ctx, E>>, E> {
+ // If this protocol is used then we need to create the builders.
+ E::as_ctx(self, |this| this.into_builders().cast()).then(|(this, _)| {
+ match &mut this.inner {
+ Inner::Builders { kind, .. } => {
+ // This signals to go into map mode for the sequence.
+ *kind = StructKind::Map;
+ }
+ _ => {}
}
- StructMode::Map => {
- // A visitor that knows how to use field names.
- let visitor = FieldVisitor::<I, M, E> {
- builders: &mut self.builders,
- marker: None,
- _marker: Default::default(),
- };
- // Loop through all the elements in the sequence.
- // Each key value pair will be mapped to a field.
- E::repeat_map((visitor, scope), |(visitor, scope)| {
- E::as_ctx_map((scope, visitor), |(scope, visitor)| {
- // Visit the next element of the sequence.
- // When there are no more items in the sequence then the loop ends.
- scope.next(DynVisitor(*visitor)).cast()
- })
- .map(Flow::to_control_flow)
- .cast()
- })
- }
- }
- .map(Into::into)
+ // 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<'ctx, Info, Mode: 'ctx, E> Sequence<'ctx, E> for StructBuilder<'ctx, Info, Mode, E>
+where
+ Info: StructTypeInfo<'ctx, Mode>,
+ E: Effect,
+{
+ fn visit<'a: 'c, 'b: 'c, 'c>(
+ &'a mut self,
+ scope: DynSequenceScope<'b, 'ctx, E>,
+ ) -> ErasedEffective<'c, VisitResult<DynSequenceScope<'b, 'ctx, E>>, E>
+ where
+ 'ctx: 'a + 'b + 'c,
+ {
+ // If this protocol is used then we need to create the builders.
+ E::as_ctx(self, |this| this.into_builders().cast())
+ .as_ctx(|(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 mut index = 0;
+
+ // Loop through all the fields getting a value for each one.
+ E::repeat_map((scope, builders), |(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(Flow::to_control_flow).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::<Info, Mode, E> {
+ builders,
+ marker: None,
+ _marker: Default::default(),
+ };
+
+ // Loop through all the elements in the sequence.
+ // Each key value pair will be mapped to a field.
+ E::repeat_map((visitor, scope), |(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(Flow::to_control_flow)
+ .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.
+ _ => Flow::Done.ready(),
+ }
+ .map(Into::into)
+ .cast()
+ })
+ .remove_ctx()
}
}
-struct FieldVisitor<'a, 'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> {
- builders: &'a mut I::Builders,
+struct FieldVisitor<'a, 'ctx, I: StructTypeInfo<'ctx, M>, M, E: Effect> {
+ builders: &'a mut I::Builders<E>,
marker: Option<I::FieldMarker>,
_marker: Marker<E>,
}
@@ -228,15 +474,14 @@ any_trait! {
})
} where
E: Effect,
- I: StructTypeInfo<'ctx, M, E>,
+ I: StructTypeInfo<'ctx, M>,
}
impl<'d, 'ctx, I, M, E> Tag<'ctx, tags::Key, E> for FieldVisitor<'d, 'ctx, I, M, E>
where
E: Effect,
- I: StructTypeInfo<'ctx, M, E>,
+ I: StructTypeInfo<'ctx, M>,
{
- #[inline(always)]
fn visit<'a: 'c, 'b: 'c, 'c>(
&'a mut self,
_key: tags::Key,
@@ -256,7 +501,7 @@ where
}
}
-struct NameVisitor<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> {
+struct NameVisitor<'ctx, I: StructTypeInfo<'ctx, M>, M, E: Effect> {
field_marker: Option<I::FieldMarker>,
_marker: Marker<E>,
}
@@ -268,15 +513,14 @@ any_trait! {
ValueProto<OwnedStatic<&'static str>, E>,
] where
E: Effect,
- I: StructTypeInfo<'ctx, M, E>,
+ I: StructTypeInfo<'ctx, M>,
}
impl<'ctx, I, M, E> Value<'ctx, OwnedStatic<usize>, E> for NameVisitor<'ctx, I, M, E>
where
E: Effect,
- I: StructTypeInfo<'ctx, M, E>,
+ I: StructTypeInfo<'ctx, M>,
{
- #[inline(always)]
fn visit<'a>(
&'a mut self,
OwnedStatic(index): TypeName::T<'a, 'ctx, OwnedStatic<usize>>,
@@ -294,9 +538,8 @@ where
impl<'ctx, I, M, E> Value<'ctx, TempBorrowedStaticHrt<str>, E> for NameVisitor<'ctx, I, M, E>
where
E: Effect,
- I: StructTypeInfo<'ctx, M, E>,
+ I: StructTypeInfo<'ctx, M>,
{
- #[inline(always)]
fn visit<'a>(
&'a mut self,
TempBorrowedStatic(name): TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>>,
@@ -314,9 +557,8 @@ where
impl<'ctx, I, M, E> Value<'ctx, OwnedStatic<&'static str>, E> for NameVisitor<'ctx, I, M, E>
where
E: Effect,
- I: StructTypeInfo<'ctx, M, E>,
+ I: StructTypeInfo<'ctx, M>,
{
- #[inline(always)]
fn visit<'a>(
&'a mut self,
OwnedStatic(name): TypeName::T<'a, 'ctx, OwnedStatic<&'static str>>,