use core::fmt::{Debug, Display};
use effectful::{bound::{IsSend, IsSync}, closure::Capture, effective::Effective, environment::{DynBind, Environment, NativeForm}, higher_ranked::Mut, tri};
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.
pub struct StructBuilder<'ctx, Info, Mode, E: Environment>
where
Info: StructTypeInfo<'ctx, Mode, E>,
{
inner: Inner<'ctx, Info, Mode, E>,
}
unsafe impl<'ctx, Info, Mode, E: Environment> IsSend<E::NeedSend> for StructBuilder<'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, E>,
{}
unsafe impl<'ctx, Info, Mode, E: Environment> IsSync<E::NeedSync> for StructBuilder<'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, E>,
{}
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(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,
}
/// 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: DynBind<E>;
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<Self::T, Self::Error>, 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.
pub struct StructError<'ctx, Info, M, E: Environment>
where
Info: StructTypeInfo<'ctx, M, E>,
{
/// Error from the struct info definition.
error: Info::Error,
}
unsafe impl<'ctx, Info, Mode, E: Environment> IsSend<E::NeedSend> for StructError<'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, E>,
{}
unsafe impl<'ctx, Info, Mode, E: Environment> IsSync<E::NeedSync> for StructError<'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, E>,
{}
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>,
{
type Seed = Info::Seed;
type Error = StructError<'ctx, Info, Mode, E>;
type Value = Info::T;
}
impl<'ctx, Info, Mode: 'ctx, E: Environment> StructBuilder<'ctx, Info, Mode, E>
where
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(Capture(self).fun_once(|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
Info: StructTypeInfo<'ctx, Mode, E>,
for<'b, 'c> TypeName::T<'b, 'c, Info::ValueT, E>: IsSync<E::NeedSend>,
{
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::Value, 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(Capture(()).fun_once(|_, builders, _| Info::from_builders(builders)))
.map(Capture(()).fun_once(|_, result, _| result.map_err(StructError::from_field_err))).cast()
}
Inner::Builders { builders, .. } => {
// Create the value from the builders.
Info::from_builders(builders)
.map(Capture(()).fun_once(|_, 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
Info: StructTypeInfo<'ctx, Mode, E>,
for<'b, 'c> TypeName::T<'b, 'c, Info::ValueT, E>: IsSync<E::NeedSend>,
{
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
E: Environment,
Info: StructTypeInfo<'ctx, Mode, E>,
for<'b, 'c> TypeName::T<'b, 'c, Info::ValueT, E>: IsSync<E::NeedSend>,
Mode: 'ctx,
}
impl<'ctx, Info, Mode: 'ctx, E> RequestHint<'ctx, E> for StructBuilder<'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, E>,
for<'b, 'c> TypeName::T<'b, 'c, Info::ValueT, E>: IsSync<E::NeedSend>,
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(Capture(()).fun_once_hrt::<Mut<_>, _>(|_, (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(Capture(()).fun_once(|_, (_, 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
Info: StructTypeInfo<'ctx, Mode, E>,
E: Environment,
for<'a> 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<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(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
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(Capture(()).fun_once_hrt::<Mut<_>, _>(|_, this, _| this.make_builders().cast()))
.then(Capture(walker).fun_once(|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
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(Capture(()).fun_once_hrt::<Mut<_>, _>(|_, this, _| this.make_builders().cast())).then(Capture(walker).fun_once(|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
Info: StructTypeInfo<'ctx, Mode, 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(Capture(()).fun_once_hrt::<Mut<_>, _>(|_, this, _| this.make_builders().cast()))
.update(Capture(()).fun_once_hrt::<Mut<_>, _>(|_, (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), move |(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.
_ => E::value(Flow::Done).cast(),
}
.map(Into::into)
.cast()
}))
.map(Capture(()).fun_once(|_, (_, x), _| x))
.cast()
}
}
struct FieldVisitor<'a, 'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Environment> {
builders: &'a mut I::Builders,
marker: Option<I::FieldMarker>,
_marker: Marker<E>,
}
unsafe impl<'a, 'ctx, Info, Mode, E: Environment> IsSend<E::NeedSend> for FieldVisitor<'a, 'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, E>,
{}
unsafe impl<'a, 'ctx, Info, Mode, E: Environment> IsSync<E::NeedSync> for FieldVisitor<'a, 'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, 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>,
}
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>,
{
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::ready((self, visitor, walker))
.as_ctx(|(_, visitor, walker)| walker.walk(DynVisitor(visitor)).cast())
.map(|((this, visitor, _), flow)| {
this.marker = visitor.field_marker;
flow.to_continue().into()
})
}
}
struct NameVisitor<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Environment> {
field_marker: Option<I::FieldMarker>,
_marker: Marker<E>,
}
unsafe impl<'ctx, Info, Mode, E: Environment> IsSend<E::NeedSend> for NameVisitor<'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, E>,
{}
unsafe impl<'ctx, Info, Mode, E: Environment> IsSync<E::NeedSync> for NameVisitor<'ctx, Info, Mode, E>
where
Info: StructTypeInfo<'ctx, Mode, 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>,
}
impl<'ctx, I, M, E> Value<'ctx, OwnedStatic<usize>, E> for NameVisitor<'ctx, I, M, E>
where
E: Environment,
I: StructTypeInfo<'ctx, M, E>,
{
fn visit<'a>(
&'a mut self,
OwnedStatic(index): TypeName::T<'a, 'ctx, OwnedStatic<usize>, E>,
) -> NativeForm<'a, VisitResult<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::ready(VisitResult::Control(Flow::Done))
}
}
impl<'ctx, I, M, E> Value<'ctx, TempBorrowedStaticHrt<str>, E> for NameVisitor<'ctx, I, M, E>
where
E: Environment,
I: StructTypeInfo<'ctx, M, E>,
{
fn visit<'a>(
&'a mut self,
TempBorrowedStatic(name): TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>, E>,
) -> NativeForm<'a, VisitResult<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::ready(VisitResult::Control(Flow::Done))
}
}
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>,
{
fn visit<'a>(
&'a mut self,
OwnedStatic(name): TypeName::T<'a, 'ctx, OwnedStatic<&'static str>, E>,
) -> NativeForm<'a, VisitResult<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::ready(VisitResult::Control(Flow::Done))
}
}