use core::fmt::{Debug, Display};
use effectful::{
bound::{Dynamic, IsSend, IsSync},
effective::Effective,
environment::{DynBind, Environment, NativeForm},
higher_ranked::Mut,
tri, SendSync,
};
use crate::{
any::{OwnedStatic, TempBorrowedStatic, TempBorrowedStaticHrt, TypeName},
any_trait,
hkt::Marker,
protocol::{
visitor::{
tags, DynSequenceScope, EffectiveVisitExt as _, RequestHint, RequestHintProto,
Sequence, SequenceHint, SequenceProto, Tag, TagConst, TagHint, TagProto, Value,
ValueProto, VisitResult,
},
walker::hint::hint_protocol,
AsVisitor, DynVisitor, DynWalker,
},
Builder, BuilderTypes, DynWalkerObjSafe, Flow,
};
use super::NoopVisitor;
/// A builder for a struct.
#[derive(SendSync)]
pub struct StructBuilder<'ctx, Info, Mode, E: Environment>
where
Info: StructTypeInfo<'ctx, Mode, E>,
{
inner: Inner<'ctx, Info, Mode, E>,
}
#[derive(SendSync)]
enum Inner<'ctx, Info, Mode, E: Environment>
where
Info: StructTypeInfo<'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<Info::T>),
}
/// 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<'ctx, Mode: 'ctx, E: Environment>: 'static {
/// A struct of builders for each field.
type Builders: DynBind<E>;
/// The seed value needed to make the builders.
type Seed: DynBind<E>;
/// An enum of the fields.
///
/// These markers act in place of the field names.
type FieldMarker: DynBind<E> + Copy + Display;
/// The error type that can be generated while building the struct.
type Error: DynBind<E> + Debug + Display;
/// The kind of type for enabling the direct value protocol.
type ValueT: TypeName::MemberType<E>;
/// 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) -> NativeForm<'a, Self::Builders, E>;
/// Finish building the struct value.
fn from_builders<'a>(
builders: Self::Builders,
) -> NativeForm<'a, Result<Dynamic<Self::T>, Self::Error>, E>
where
Dynamic<Self::T>: DynBind<E>;
/// Get the visitor for a field.
///
/// This is how [`StructBuilder`] picks a field builder to use.
fn as_visitor<'a>(
marker: Self::FieldMarker,
builders: &'a mut Self::Builders,
) -> DynVisitor<'a, 'ctx, E>;
/// 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, E>) -> Self::T;
}
/// Error that [`StructBuilder`] returns.
#[derive(SendSync)]
pub struct StructError<'ctx, Info, M, E: Environment>
where
Info: StructTypeInfo<'ctx, M, E>,
{
/// Error from the struct info definition.
error: Info::Error,
}
impl<'ctx, Info, Mode, E: Environment> StructError<'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, E>,
{
fn from_field_err(error: Info::Error) -> Self {
Self { error }
}
}
impl<'ctx, Info, Mode, E: Environment> Debug for StructError<'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, E>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("StructError").field(&self.error).finish()
}
}
impl<'ctx, Info, Mode, E: Environment> Display for StructError<'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, E>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Display::fmt(&self.error, f)
}
}
impl<'ctx, Info, Mode, E: Environment> BuilderTypes<E> for StructBuilder<'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, E>,
Dynamic<Info::T>: DynBind<E>,
{
type Seed = Info::Seed;
type Error = StructError<'ctx, Info, Mode, E>;
type Output = Dynamic<Info::T>;
type Value = Info::T;
fn unwrap_output(output: Self::Output) -> Self::Value {
output.0
}
}
impl<'ctx, Info, Mode: 'ctx, E: Environment> StructBuilder<'ctx, Info, Mode, E>
where
Self: DynBind<E>,
Info: StructTypeInfo<'ctx, Mode, E>,
{
fn make_builders<'e>(&'e mut self) -> NativeForm<'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<'ctx, Info, Mode: 'ctx, E: Environment> Builder<'ctx, E> for StructBuilder<'ctx, Info, Mode, E>
where
Self: DynBind<E>,
Info: StructTypeInfo<'ctx, Mode, E>,
Dynamic<Info::T>: DynBind<E>,
Info: StructTypeInfo<'ctx, Mode, E>,
Dynamic<OwnedStatic<usize>>: DynBind<E>,
for<'a> Dynamic<&'a Info::T>: DynBind<E>,
for<'b, 'c> Dynamic<TypeName::T<'b, 'c, Info::ValueT, E>>: DynBind<E>,
Dynamic<OwnedStatic<&'static str>>: DynBind<E>,
for<'b, 'c> Dynamic<&'b TypeName::T<'b, 'c, Info::ValueT, E>>: DynBind<E>,
for<'b> Dynamic<TempBorrowedStatic<'b, str>>: DynBind<E>,
{
fn from_seed<'a>(seed: Self::Seed) -> NativeForm<'a, Self, E>
where
Self: 'a,
{
E::value(Self {
inner: Inner::Seed(seed),
})
.cast()
}
fn build<'a>(self) -> NativeForm<'a, Result<Self::Output, Self::Error>, 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<'ctx, Info, Mode: 'ctx, E: Environment> AsVisitor<'ctx, E>
for StructBuilder<'ctx, Info, Mode, E>
where
Self: DynBind<E>,
Info: StructTypeInfo<'ctx, Mode, E>,
Dynamic<Info::T>: DynBind<E>,
Info: StructTypeInfo<'ctx, Mode, E>,
Dynamic<OwnedStatic<&'static str>>: DynBind<E>,
for<'a> Dynamic<&'a Info::T>: DynBind<E>,
Dynamic<OwnedStatic<usize>>: DynBind<E>,
for<'b, 'c> Dynamic<TypeName::T<'b, 'c, Info::ValueT, E>>: DynBind<E>,
for<'b> Dynamic<TempBorrowedStatic<'b, str>>: DynBind<E>,
for<'b, 'c> Dynamic<&'b TypeName::T<'b, 'c, Info::ValueT, E>>: DynBind<E>,
{
fn as_visitor<'a>(&'a mut self) -> DynVisitor<'a, 'ctx, E>
where
'ctx: 'a,
{
DynVisitor(self)
}
}
any_trait! {
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
for<'a> Dynamic<&'a Info::T>: DynBind<E>,
E: Environment,
Self: DynBind<E>,
Info: StructTypeInfo<'ctx, Mode, E>,
Dynamic<Info::T>: DynBind<E>,
Dynamic<OwnedStatic<&'static str>>: DynBind<E>,
for<'b, 'c> Dynamic<TypeName::T<'b, 'c, Info::ValueT, E>>: DynBind<E>,
for<'b, 'c> Dynamic<&'b TypeName::T<'b, 'c, Info::ValueT, E>>: DynBind<E>,
for<'b> Dynamic<TempBorrowedStatic<'b, str>>: DynBind<E>,
Dynamic<OwnedStatic<usize>>: DynBind<E>,
Dynamic<Info::T>: DynBind<E>,
Mode: 'ctx,
}
impl<'ctx, Info, Mode: 'ctx, E> RequestHint<'ctx, E> for StructBuilder<'ctx, Info, Mode, E>
where
Self: DynBind<E>,
Info: StructTypeInfo<'ctx, Mode, E>,
Dynamic<Info::T>: DynBind<E>,
for<'a> Dynamic<&'a Info::T>: DynBind<E>,
for<'b, 'c> Dynamic<TypeName::T<'b, 'c, Info::ValueT, E>>: DynBind<E>,
for<'b, 'c> Dynamic<&'b TypeName::T<'b, 'c, Info::ValueT, E>>: DynBind<E>,
Dynamic<OwnedStatic<usize>>: DynBind<E>,
for<'b> Dynamic<TempBorrowedStatic<'b, str>>: DynBind<E>,
Dynamic<OwnedStatic<&'static str>>: DynBind<E>,
E: Environment,
{
#[inline(always)]
fn request_hint<'this: 'e, 'walker: 'e, 'e>(
&'this mut self,
walker: DynWalker<'walker, 'ctx, E>,
) -> NativeForm<'e, VisitResult, E>
where
'ctx: 'this + 'walker,
{
E::value((self, walker))
.update((), |_, (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()
})
.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()
})
.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()
})
.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()
})
.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<'ctx, Info, Mode, E> Value<'ctx, Info::ValueT, E> for StructBuilder<'ctx, Info, Mode, E>
where
Self: DynBind<E>,
Info: StructTypeInfo<'ctx, Mode, E>,
E: Environment,
for<'a> Dynamic<TypeName::T<'a, 'ctx, Info::ValueT, E>>: DynBind<E>,
{
fn visit<'a>(
&'a mut self,
value: TypeName::T<'a, 'ctx, Info::ValueT, E>,
) -> NativeForm<'a, VisitResult<Dynamic<TypeName::T<'a, 'ctx, Info::ValueT, E>>>, E>
where
TypeName::T<'a, 'ctx, Info::ValueT, E>: Sized,
'ctx: 'a,
{
// 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<'ctx, Info, Mode: 'ctx, E> Tag<'ctx, tags::Struct, E> for StructBuilder<'ctx, Info, Mode, E>
where
Self: DynBind<E>,
Info: StructTypeInfo<'ctx, Mode, E>,
E: Environment,
{
fn visit<'this: 'e, 'walker: 'e, 'e>(
&'this mut self,
_kind: tags::Struct,
walker: DynWalkerObjSafe<'walker, 'ctx, E>,
) -> NativeForm<'e, VisitResult, E> {
// If this protocol is used then we need to create the builders.
E::value(self)
.update((), |_, 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<'ctx, Info, Mode: 'ctx, E> Tag<'ctx, tags::Map, E> for StructBuilder<'ctx, Info, Mode, E>
where
Self: DynBind<E>,
Info: StructTypeInfo<'ctx, Mode, E>,
E: Environment,
{
fn visit<'this: 'e, 'walker: 'e, 'e>(
&'this mut self,
_kind: tags::Map,
walker: DynWalkerObjSafe<'walker, 'ctx, E>,
) -> NativeForm<'e, VisitResult, E> {
// If this protocol is used then we need to create the builders.
E::value(self)
.update((), |_, 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<'ctx, Info, Mode: 'ctx, E> Sequence<'ctx, E> for StructBuilder<'ctx, Info, Mode, E>
where
Self: DynBind<E>,
Info: StructTypeInfo<'ctx, Mode, E>,
for<'b> Dynamic<TempBorrowedStatic<'b, str>>: DynBind<E>,
Dynamic<OwnedStatic<&'static str>>: DynBind<E>,
Dynamic<OwnedStatic<usize>>: DynBind<E>,
E: Environment,
{
fn visit<'a: 'c, 'b: 'c, 'c>(
&'a mut self,
scope: DynSequenceScope<'b, 'ctx, E>,
) -> NativeForm<'c, VisitResult, E>
where
'ctx: 'a + 'b + 'c,
{
// If this protocol is used then we need to create the builders.
E::value(self)
.update((), |_, this| this.make_builders().cast())
.update(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()
})
.map((), |_, (_, _, 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::<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::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()
})
.map((), |_, (_, _, 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, 'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Environment> {
builders: &'a mut I::Builders,
marker: Option<I::FieldMarker>,
_marker: Marker<E>,
}
any_trait! {
impl['ctx, 'a, I, M][E] FieldVisitor<'a, 'ctx, I, M, E> = [
TagProto<tags::Key, E>,
] 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<OwnedStatic<usize>>: DynBind<E>,
Dynamic<OwnedStatic<&'static str>>: DynBind<E>,
for<'b> Dynamic<TempBorrowedStatic<'b, str>>: DynBind<E>,
}
impl<'d, 'ctx, I, M, E> Tag<'ctx, tags::Key, E> for FieldVisitor<'d, 'ctx, I, M, E>
where
E: Environment,
I: StructTypeInfo<'ctx, M, E>,
Dynamic<OwnedStatic<usize>>: DynBind<E>,
Dynamic<OwnedStatic<&'static str>>: DynBind<E>,
for<'a> Dynamic<TempBorrowedStatic<'a, str>>: DynBind<E>,
{
fn visit<'a: 'c, 'b: 'c, 'c>(
&'a mut self,
_key: tags::Key,
walker: DynWalkerObjSafe<'b, 'ctx, E>,
) -> NativeForm<'c, VisitResult, E> {
let visitor = NameVisitor::<I, M, E> {
field_marker: None,
_marker: Default::default(),
};
E::value((self, visitor, walker))
.update((), |_, (_, 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<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Environment> {
field_marker: Option<I::FieldMarker>,
_marker: Marker<E>,
}
any_trait! {
impl['ctx, I, M][E] NameVisitor<'ctx, I, M, E> = [
ValueProto<OwnedStatic<usize>, E>,
ValueProto<TempBorrowedStaticHrt<str>, E>,
ValueProto<OwnedStatic<&'static str>, E>,
] where
E: Environment,
I: StructTypeInfo<'ctx, M, E>,
Dynamic<OwnedStatic<usize>>: DynBind<E>,
Dynamic<OwnedStatic<&'static str>>: DynBind<E>,
for<'a> Dynamic<TempBorrowedStatic<'a, str>>: DynBind<E>,
}
impl<'ctx, I, M, E> Value<'ctx, OwnedStatic<usize>, E> for NameVisitor<'ctx, I, M, E>
where
E: Environment,
I: StructTypeInfo<'ctx, M, E>,
Dynamic<OwnedStatic<usize>>: DynBind<E>,
{
fn visit<'a>(
&'a mut self,
OwnedStatic(index): TypeName::T<'a, 'ctx, OwnedStatic<usize>, E>,
) -> NativeForm<'a, VisitResult<Dynamic<TypeName::T<'a, 'ctx, OwnedStatic<usize>, E>>>, E>
where
TypeName::T<'a, 'ctx, OwnedStatic<usize>, E>: Sized,
'ctx: 'a,
{
self.field_marker = I::marker_from_index(index);
E::value(VisitResult::Control(Flow::Done)).cast()
}
}
impl<'ctx, I, M, E> Value<'ctx, TempBorrowedStaticHrt<str>, E> for NameVisitor<'ctx, I, M, E>
where
E: Environment,
I: StructTypeInfo<'ctx, M, E>,
for<'a> Dynamic<TempBorrowedStatic<'a, str>>: DynBind<E>,
{
fn visit<'a>(
&'a mut self,
TempBorrowedStatic(name): TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>, E>,
) -> NativeForm<'a, VisitResult<Dynamic<TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>, E>>>, E>
where
TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>, E>: Sized,
'ctx: 'a,
{
self.field_marker = I::marker_from_name(name);
E::value(VisitResult::Control(Flow::Done)).cast()
}
}
impl<'ctx, I, M, E> Value<'ctx, OwnedStatic<&'static str>, E> for NameVisitor<'ctx, I, M, E>
where
E: Environment,
I: StructTypeInfo<'ctx, M, E>,
Dynamic<OwnedStatic<&'static str>>: DynBind<E>,
{
fn visit<'a>(
&'a mut self,
OwnedStatic(name): TypeName::T<'a, 'ctx, OwnedStatic<&'static str>, E>,
) -> NativeForm<'a, VisitResult<Dynamic<TypeName::T<'a, 'ctx, OwnedStatic<&'static str>, E>>>, E>
where
TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>, E>: Sized,
'ctx: 'a,
{
self.field_marker = I::marker_from_name(name);
E::value(VisitResult::Control(Flow::Done)).cast()
}
}