struct to struct via macro
| -rw-r--r-- | src/build.rs | 4 | ||||
| -rw-r--r-- | src/build/builders/core/struct.rs | 43 | ||||
| -rw-r--r-- | src/effect.rs | 3 | ||||
| -rw-r--r-- | src/lib.rs | 9 | ||||
| -rw-r--r-- | src/macros.rs | 2 | ||||
| -rw-r--r-- | src/macros/build.rs | 127 | ||||
| -rw-r--r-- | src/protocol/visitor/tag.rs | 1 | ||||
| -rw-r--r-- | src/transform.rs | 150 | ||||
| -rw-r--r-- | src/walk.rs | 4 | ||||
| -rw-r--r-- | src/walk/walkers/core/struct.rs | 1 | ||||
| -rw-r--r-- | tests/builder_struct.rs | 39 | ||||
| -rw-r--r-- | tests/protocol_visitor_value.rs | 15 | ||||
| -rw-r--r-- | tests/protocol_walker_hint.rs | 5 | ||||
| -rw-r--r-- | tests/walker_struct.rs | 14 |
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, @@ -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. |