struct to struct via macro
Konnor Andrews 2024-04-22
parent 04d3e97 · commit d97966a
-rw-r--r--src/build.rs4
-rw-r--r--src/build/builders/core/struct.rs43
-rw-r--r--src/effect.rs3
-rw-r--r--src/lib.rs9
-rw-r--r--src/macros.rs2
-rw-r--r--src/macros/build.rs127
-rw-r--r--src/protocol/visitor/tag.rs1
-rw-r--r--src/transform.rs150
-rw-r--r--src/walk.rs4
-rw-r--r--src/walk/walkers/core/struct.rs1
-rw-r--r--tests/builder_struct.rs39
-rw-r--r--tests/protocol_visitor_value.rs15
-rw-r--r--tests/protocol_walker_hint.rs5
-rw-r--r--tests/walker_struct.rs14
14 files changed, 318 insertions, 99 deletions
diff --git a/src/build.rs b/src/build.rs
index 716b882..f5db1d2 100644
--- a/src/build.rs
+++ b/src/build.rs
@@ -6,9 +6,9 @@ use crate::{
};
/// A buildable type.
-pub trait Build<'ctx, M, E: Effect>: BuilderTypes<Value = Self> + Send + Sync {
+pub trait Build<'ctx, M, E: Effect>: Send + Sync {
/// The builder that can be used to build a value of `Self`.
- type Builder: Builder<'ctx, E, Seed = Self::Seed, Error = Self::Error, Value = Self>;
+ type Builder: Builder<'ctx, E, Value = Self>;
}
pub trait BuilderTypes {
diff --git a/src/build/builders/core/struct.rs b/src/build/builders/core/struct.rs
index cff1ab3..feada5d 100644
--- a/src/build/builders/core/struct.rs
+++ b/src/build/builders/core/struct.rs
@@ -34,9 +34,9 @@ pub trait StructTypeInfo<'ctx, M, E: Effect>: 'static {
type Seed: Send + Sync;
- type FieldMarker: Send + Sync + Copy;
+ type FieldMarker: Send + Sync + Copy + core::fmt::Debug;
- type Error: Send + Sync;
+ type Error: Send + Sync + core::fmt::Debug;
type T: Send + Sync;
@@ -53,13 +53,27 @@ pub trait StructTypeInfo<'ctx, M, E: Effect>: 'static {
fn marker_from_name(name: &str) -> Option<Self::FieldMarker>;
}
+pub struct StructError<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> {
+ error: I::Error,
+}
+
+impl<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> core::fmt::Debug
+ for StructError<'ctx, I, M, E>
+{
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ f.debug_struct("StructError")
+ .field("error", &self.error)
+ .finish()
+ }
+}
+
impl<'ctx, I, M, E: Effect> BuilderTypes for StructBuilder<'ctx, I, M, E>
where
I: StructTypeInfo<'ctx, M, E>,
{
type Seed = I::Seed;
- type Error = ();
+ type Error = StructError<'ctx, I, M, E>;
type Value = I::T;
}
@@ -90,7 +104,7 @@ where
E::wrap(async {
match I::from_builders(self.builders).await {
Ok(value) => Ok(value),
- Err(_) => todo!(),
+ Err(err) => Err(StructError { error: err }),
}
})
}
@@ -138,6 +152,7 @@ where
I: StructTypeInfo<'ctx, M, E>,
E: Effect,
{
+ #[inline(always)]
fn visit<'a>(
&'a mut self,
scope: DynSequenceScope<'a, 'ctx, E>,
@@ -241,6 +256,7 @@ 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: Effect,
I: StructTypeInfo<'ctx, M, E>,
@@ -283,3 +299,22 @@ where
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: Effect,
+ I: StructTypeInfo<'ctx, M, E>,
+{
+ fn visit<'a>(
+ &'a mut self,
+ OwnedStatic(name): TypeName::T<'a, 'ctx, OwnedStatic<&'static str>>,
+ ) -> Future<'a, VisitResult<TypeName::T<'a, 'ctx, OwnedStatic<&'static str>>>, E>
+ where
+ TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>>: Send + Sized,
+ 'ctx: 'a,
+ {
+ self.field_marker = I::marker_from_name(name);
+
+ E::ready(VisitResult::Control(Flow::Done))
+ }
+}
diff --git a/src/effect.rs b/src/effect.rs
index 920ee0e..3e3b8da 100644
--- a/src/effect.rs
+++ b/src/effect.rs
@@ -155,6 +155,7 @@ higher_ranked_type! {
impl<B: BlockOn> Effect for Blocking<B> {
type Future<T: Send> = core::future::Ready<T>;
+ #[inline(always)]
fn wrap<'a, F>(future: F) -> SendFuture::T<'a, F::Output, Self::Future<F::Output>>
where
F: core::future::Future + Send + 'a,
@@ -163,10 +164,12 @@ impl<B: BlockOn> Effect for Blocking<B> {
core::future::ready(B::block_on(future))
}
+ #[inline(always)]
fn ready<'a, T: Send>(value: T) -> SendFuture::T<'a, T, Self::Future<T>> {
core::future::ready(value)
}
+ #[inline(always)]
fn map<'a, T, U, F>(
future: SendFuture::T<'a, T, Self::Future<T>>,
func: F,
diff --git a/src/lib.rs b/src/lib.rs
index aa77edd..8d86fb2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -88,12 +88,12 @@ macro_rules! Walk {
{
$(#[$($attr:tt)*])*
$vis:vis struct $name:ident {$(
- $field:ident: $type:ty
+ $fvis:vis $field:ident: $type:ty
),* $(,)?}
} => {
const _: () = {
impl<'ctx, M: 'ctx, E: $crate::effect::Effect> $crate::Walk<'ctx, M, E> for &'ctx $name {
- type Walker = $crate::walkers::core::r#struct::StructWalker<'ctx, $name, Info, M, E>;
+ type Walker = $crate::walkers::core::r#struct::StructWalker<'ctx, Info, $crate::walkers::core::r#struct::StaticType, M, E>;
fn into_walker(self) -> Self::Walker {
$crate::walkers::core::r#struct::StructWalker::new(self)
@@ -123,12 +123,13 @@ macro_rules! Walk {
type FieldError = FieldError<'ctx>;
type T = $name;
+ type S = $crate::walkers::core::r#struct::StaticType;
#[allow(unreachable_code, non_snake_case, non_upper_case_globals, non_camel_case_types)]
fn walk_field<'a, E: $crate::effect::Effect>(
index: usize,
value: &'ctx Self::T,
- visitor: $crate::protocol::Visitor<'a, 'ctx>,
+ visitor: $crate::protocol::DynVisitor<'a, 'ctx>,
) -> $crate::effect::Future<'a, Result<$crate::Flow, Self::FieldError>, E> {
mod fields {
enum Fields {$($field),*}
@@ -146,7 +147,7 @@ macro_rules! Walk {
E::map($crate::Walker::<'ctx, E>::walk(walker, visitor), |result| match result {
Ok(_) => {
- Ok(Flow::Continue)
+ Ok($crate::Flow::Continue)
}
Err(err) => {
Err(FieldError(FieldErrorKind::$field(err)))
diff --git a/src/macros.rs b/src/macros.rs
index 8b13789..af0168a 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -1 +1 @@
-
+mod build;
diff --git a/src/macros/build.rs b/src/macros/build.rs
new file mode 100644
index 0000000..9d37bf6
--- /dev/null
+++ b/src/macros/build.rs
@@ -0,0 +1,127 @@
+use crate::{
+ effect::{Blocking, ReadyValue},
+ transform, DefaultMode,
+};
+
+#[macro_export]
+macro_rules! Build {
+ {
+ $(#[$($attr:tt)*])*
+ $vis:vis struct $name:ident {$(
+ $fvis:vis $field:ident: $type:ty
+ ),* $(,)?}
+ } => {
+ const _: () = {
+ impl<'ctx, M, E: $crate::effect::Effect> $crate::Build<'ctx, M, E> for $name {
+ type Builder = $crate::builders::core::r#struct::StructBuilder<'ctx, Info, M, E>;
+ }
+
+ $vis struct Builders<'ctx, M, E: $crate::effect::Effect> {
+ $($field: <$type as $crate::Build<'ctx, M, E>>::Builder),*
+ }
+
+ #[derive(Copy, Clone, Debug)]
+ $vis enum Field {
+ $($field),*
+ }
+
+ mod field_index {
+ enum __Fields {
+ $($field),*
+ }
+
+ $(pub const $field: usize = __Fields::$field as usize;)*
+ }
+
+ #[derive(Debug)]
+ $vis enum Error {
+ $($field(<$type as $crate::BuilderTypes>::Error)),*
+ }
+
+ $vis struct Info;
+
+ impl<'ctx, M, E: $crate::effect::Effect> $crate::builders::core::r#struct::StructTypeInfo<'ctx, M, E> for Info {
+ type Builders = Builders<'ctx, M, E>;
+ type FieldMarker = Field;
+ type T = $name;
+ type Error = Error;
+ type Seed = ($(<$type as $crate::BuilderTypes>::Seed),*);
+
+ fn new_builders<'a>(seed: Self::Seed) -> $crate::effect::Future<'a, Self::Builders, E> {
+ let ($($field),*) = seed;
+
+ E::wrap(async move {
+ Builders {
+ $($field: $crate::Builder::<E>::from_seed($field).await),*
+ }
+ })
+ }
+
+ fn from_builders<'a>(builders: Self::Builders) -> $crate::effect::Future<'a, Result<Self::T, Self::Error>, E> {
+ use $crate::Builder;
+
+ E::wrap(async {
+ Ok($name {
+ $($field: builders.$field.build().await.map_err(Error::$field)?),*
+ })
+ })
+ }
+
+ fn as_visitor<'a>(
+ marker: Self::FieldMarker,
+ builders: &'a mut Self::Builders,
+ ) -> $crate::protocol::DynVisitor<'a, 'ctx> {
+ use $crate::Builder;
+
+ match marker {
+ $(Field::$field => builders.$field.as_visitor()),*
+ }
+ }
+
+ fn marker_from_index(index: usize) -> Option<Self::FieldMarker> {
+ match index {
+ $(field_index::$field => Some(Field::$field),)*
+ _ => None
+ }
+ }
+
+ fn marker_from_name(name: &str) -> Option<Self::FieldMarker> {
+ match name {
+ $(stringify!($field) => Some(Field::$field),)*
+ _ => None
+ }
+ }
+ }
+ };
+ }
+}
+
+#[test]
+fn demo() {
+ use crate::Walk;
+ use macro_rules_attribute::derive;
+
+ #[derive(Build!, Walk!, Debug)]
+ struct X {
+ a: bool,
+ b: bool,
+ }
+
+ #[derive(Build!, Walk!, Debug)]
+ struct Y {
+ b: bool,
+ a: bool,
+ }
+
+ let value = X { a: true, b: false };
+
+ let other = transform::<<Y as crate::Build<'_, DefaultMode, _>>::Builder, _, Blocking>(
+ ((), ()),
+ Walk::<DefaultMode, _>::into_walker(&value),
+ )
+ .value();
+
+ dbg!(other);
+
+ // todo!();
+}
diff --git a/src/protocol/visitor/tag.rs b/src/protocol/visitor/tag.rs
index 806f4aa..e90dea6 100644
--- a/src/protocol/visitor/tag.rs
+++ b/src/protocol/visitor/tag.rs
@@ -164,6 +164,7 @@ impl<E> TagError<E> {
}
}
+#[inline(always)]
pub fn visit_tag<'a, 'ctx: 'a, K: TagKind, E: Effect, W: crate::Walker<'ctx, E> + 'a>(
kind: K,
mut visitor: DynVisitor<'a, 'ctx>,
diff --git a/src/transform.rs b/src/transform.rs
index 81bcf47..9e5a215 100644
--- a/src/transform.rs
+++ b/src/transform.rs
@@ -32,78 +32,78 @@ pub struct Projection<T, B, U, M> {
_marker: Marker<(U, M)>,
}
-#[allow(clippy::type_complexity)]
-impl<T, B, U: Send + Sync, M> Projection<T, B, U, M> {
- pub fn project_ref<'a, E: Effect>(
- &'a self,
- ) -> Future<
- 'a,
- (
- Result<&'a U, B::Error>,
- Result<<&'a T as WalkerTypes>::Output, <&'a T as WalkerTypes>::Error>,
- ),
- E,
- >
- where
- &'a T: Walk<'a, M, E>,
- B: Clone + Builder<'a, E, Value = &'a U>,
- {
- let walker = self.value.into_walker();
- let mut builder = self.builder.clone();
-
- E::wrap(async {
- let result = walker.walk(builder.as_visitor()).await;
-
- (builder.build().await, result)
- })
- }
-
- pub fn project_mut<'a, E: Effect>(
- &'a mut self,
- ) -> Future<
- 'a,
- (
- Result<&'a mut U, B::Error>,
- Result<<&'a mut T as WalkerTypes>::Output, <&'a mut T as WalkerTypes>::Error>,
- ),
- E,
- >
- where
- &'a mut T: Walk<'a, M, E>,
- B: Clone + Builder<'a, E, Value = &'a mut U>,
- {
- let walker = self.value.into_walker();
- let mut builder = self.builder.clone();
-
- E::wrap(async {
- let result = walker.walk(builder.as_visitor()).await;
-
- (builder.build().await, result)
- })
- }
-
- pub fn project<'a, E: Effect>(
- self,
- ) -> Future<
- 'a,
- (
- Result<U, B::Error>,
- Result<<T as WalkerTypes>::Output, <T as WalkerTypes>::Error>,
- ),
- E,
- >
- where
- T: Walk<'a, M, E> + 'a,
- M: 'a,
- B: Clone + Builder<'a, E, Value = U> + 'a,
- {
- let walker = self.value.into_walker();
- let mut builder = self.builder.clone();
-
- E::wrap(async {
- let result = walker.walk(builder.as_visitor()).await;
-
- (builder.build().await, result)
- })
- }
-}
+// #[allow(clippy::type_complexity)]
+// impl<T, B, U: Send + Sync, M> Projection<T, B, U, M> {
+// pub fn project_ref<'a, E: Effect>(
+// &'a self,
+// ) -> Future<
+// 'a,
+// (
+// Result<&'a U, B::Error>,
+// Result<<<&'a T as Walk<'>>::Walker as WalkerTypes>::Output, <&'a T as WalkerTypes>::Error>,
+// ),
+// E,
+// >
+// where
+// &'a T: Walk<'a, M, E>,
+// B: Clone + Builder<'a, E, Value = &'a U>,
+// {
+// let walker = self.value.into_walker();
+// let mut builder = self.builder.clone();
+//
+// E::wrap(async {
+// let result = walker.walk(builder.as_visitor()).await;
+//
+// (builder.build().await, result)
+// })
+// }
+//
+// pub fn project_mut<'a, E: Effect>(
+// &'a mut self,
+// ) -> Future<
+// 'a,
+// (
+// Result<&'a mut U, B::Error>,
+// Result<<&'a mut T as WalkerTypes>::Output, <&'a mut T as WalkerTypes>::Error>,
+// ),
+// E,
+// >
+// where
+// &'a mut T: Walk<'a, M, E>,
+// B: Clone + Builder<'a, E, Value = &'a mut U>,
+// {
+// let walker = self.value.into_walker();
+// let mut builder = self.builder.clone();
+//
+// E::wrap(async {
+// let result = walker.walk(builder.as_visitor()).await;
+//
+// (builder.build().await, result)
+// })
+// }
+//
+// pub fn project<'a, E: Effect>(
+// self,
+// ) -> Future<
+// 'a,
+// (
+// Result<U, B::Error>,
+// Result<<T as WalkerTypes>::Output, <T as WalkerTypes>::Error>,
+// ),
+// E,
+// >
+// where
+// T: Walk<'a, M, E> + 'a,
+// M: 'a,
+// B: Clone + Builder<'a, E, Value = U> + 'a,
+// {
+// let walker = self.value.into_walker();
+// let mut builder = self.builder.clone();
+//
+// E::wrap(async {
+// let result = walker.walk(builder.as_visitor()).await;
+//
+// (builder.build().await, result)
+// })
+// }
+// }
diff --git a/src/walk.rs b/src/walk.rs
index 349a63d..4c2042c 100644
--- a/src/walk.rs
+++ b/src/walk.rs
@@ -7,9 +7,9 @@ use crate::{
};
/// A type that can be walked.
-pub trait Walk<'ctx, M, E: Effect>: WalkerTypes + Sized {
+pub trait Walk<'ctx, M, E: Effect>: Sized {
/// The walker for the type.
- type Walker: Walker<'ctx, E, Error = Self::Error, Output = Self::Output>;
+ type Walker: Walker<'ctx, E>;
#[must_use]
fn into_walker(self) -> Self::Walker;
diff --git a/src/walk/walkers/core/struct.rs b/src/walk/walkers/core/struct.rs
index 9e67382..9e301e8 100644
--- a/src/walk/walkers/core/struct.rs
+++ b/src/walk/walkers/core/struct.rs
@@ -543,6 +543,7 @@ where
I: StructTypeInfo<'ctx, M, S = StaticType>,
I::T: 'static,
{
+ #[inline(always)]
fn new_walk<'a>(&'a mut self, mut visitor: DynVisitor<'a, 'ctx>) -> Future<'a, Status, E> {
// Reset the errors to default state.
self.error = None;
diff --git a/tests/builder_struct.rs b/tests/builder_struct.rs
index af56538..8f85854 100644
--- a/tests/builder_struct.rs
+++ b/tests/builder_struct.rs
@@ -17,10 +17,7 @@ use treaty::{
Build, Builder, DefaultMode, Flow, Walk, Walker,
};
-use crate::common::{
- protocol::sequence::MockSequenceScope,
- walker::MockWalker,
-};
+use crate::common::{protocol::sequence::MockSequenceScope, walker::MockWalker};
mod common;
@@ -75,7 +72,7 @@ struct Fields<'ctx, M, E: Effect> {
b: <bool as Build<'ctx, M, E>>::Builder,
}
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
enum FieldMarker {
A,
B,
@@ -283,3 +280,35 @@ fn from_basic_map_like() {
// The struct is built as the mock walker above makes it.
assert_eq!(builder.build().value().unwrap(), X { a: false, b: true });
}
+
+pub mod demo {
+ use crate::Walk;
+ use macro_rules_attribute::derive;
+ use treaty::{
+ effect::{Blocking, ReadyValue as _},
+ transform, Build, DefaultMode,
+ };
+
+ #[derive(Build!, Walk!, Debug)]
+ pub struct X {
+ pub a: bool,
+ pub b: bool,
+ }
+
+ #[derive(Build!, Walk!, Debug)]
+ pub struct Y {
+ pub b: bool,
+ pub a: bool,
+ }
+
+ #[no_mangle]
+ pub fn ident(x: X) -> Y {
+ let other = transform::<<Y as crate::Build<'_, DefaultMode, _>>::Builder, _, Blocking>(
+ ((), ()),
+ Walk::<DefaultMode, _>::into_walker(&x),
+ )
+ .value();
+
+ other.0.unwrap()
+ }
+}
diff --git a/tests/protocol_visitor_value.rs b/tests/protocol_visitor_value.rs
index 584e87f..91fb280 100644
--- a/tests/protocol_visitor_value.rs
+++ b/tests/protocol_visitor_value.rs
@@ -82,7 +82,10 @@ fn borrowed_value() {
let visitor: &mut dyn Value<BorrowedStaticHrt<String>, Blocking> = &mut mock;
// Visit the borrowed value.
- assert_eq!(visitor.visit(BorrowedStatic(value)).value(), Flow::Done.into());
+ assert_eq!(
+ visitor.visit(BorrowedStatic(value)).value(),
+ Flow::Done.into()
+ );
let visitor: &mut (dyn AnyTrait + Send + Sync) = &mut mock;
assert_eq!(
@@ -126,7 +129,10 @@ fn temp_borrowed_value() {
// Visit the context to show we can shorten the lifetime.
// This would also force the lifetime to be to long if this wasn't the Temp form.
- assert_eq!(visitor.visit(TempBorrowedMutStatic(value)).value(), Flow::Done.into());
+ assert_eq!(
+ visitor.visit(TempBorrowedMutStatic(value)).value(),
+ Flow::Done.into()
+ );
// Temporary scope (smaller than the context we set above).
{
@@ -134,7 +140,10 @@ fn temp_borrowed_value() {
let mut value = String::from("test");
// Visit the temp value.
- assert_eq!(visitor.visit(TempBorrowedMutStatic(&mut value)).value(), Flow::Done.into());
+ assert_eq!(
+ visitor.visit(TempBorrowedMutStatic(&mut value)).value(),
+ Flow::Done.into()
+ );
}
// Force the visitor to outlive the temporary scope.
diff --git a/tests/protocol_walker_hint.rs b/tests/protocol_walker_hint.rs
index 4cb41dd..75c8d94 100644
--- a/tests/protocol_walker_hint.rs
+++ b/tests/protocol_walker_hint.rs
@@ -73,7 +73,10 @@ fn can_get_known_and_hint() {
let mut mock = MockBuilder::<(), (), ()>::new();
// We can call hint to "commit" to the protocol and ask the walker to use it.
- assert_eq!(walker.hint(DynVisitor(&mut mock), Hint(123)).value(), Flow::Done);
+ assert_eq!(
+ walker.hint(DynVisitor(&mut mock), Hint(123)).value(),
+ Flow::Done
+ );
}
}
diff --git a/tests/walker_struct.rs b/tests/walker_struct.rs
index 26f8935..95184a3 100644
--- a/tests/walker_struct.rs
+++ b/tests/walker_struct.rs
@@ -131,7 +131,12 @@ fn sequence_of_field_values() {
Some(Box::new(visitor))
});
- assert_eq!(scope.next(Builder::<Blocking>::as_visitor(&mut visitor)).value(), Flow::Continue);
+ assert_eq!(
+ scope
+ .next(Builder::<Blocking>::as_visitor(&mut visitor))
+ .value(),
+ Flow::Continue
+ );
}
// Get the second field value.
@@ -159,7 +164,12 @@ fn sequence_of_field_values() {
Some(Box::new(visitor))
});
- assert_eq!(scope.next(Builder::<Blocking>::as_visitor(&mut visitor)).value(), Flow::Done);
+ assert_eq!(
+ scope
+ .next(Builder::<Blocking>::as_visitor(&mut visitor))
+ .value(),
+ Flow::Done
+ );
}
// We are done with the sequence of fields.