Diffstat (limited to 'tests/builder_struct.rs')
| -rw-r--r-- | tests/builder_struct.rs | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/tests/builder_struct.rs b/tests/builder_struct.rs new file mode 100644 index 0000000..af56538 --- /dev/null +++ b/tests/builder_struct.rs @@ -0,0 +1,285 @@ +use treaty::{ + any::{OwnedStatic, TempBorrowedStatic}, + builders::{self, core::r#struct::StructBuilder}, + effect::{Blocking, Effect, Future, ReadyValue}, + protocol::{ + visitor::{tags, visit_sequence, visit_tag, visit_value, TagConst, VisitResult}, + DynVisitor, + }, + transform, + walkers::{ + self, + core::{ + noop::NoopWalker, + r#struct::{StaticType, StructWalker}, + }, + }, + Build, Builder, DefaultMode, Flow, Walk, Walker, +}; + +use crate::common::{ + protocol::sequence::MockSequenceScope, + walker::MockWalker, +}; + +mod common; + +#[derive(Debug, PartialEq)] +struct X { + a: bool, + b: bool, +} + +struct Info; + +impl<'ctx, M> walkers::core::r#struct::StructTypeInfo<'ctx, M> for Info { + const NAME: &'static str = "X"; + + const FIELDS: &'static [&'static str] = &["a", "b"]; + + type FieldError = (); + + type S = StaticType; + + type T = X; + + fn walk_field<'a, E: Effect>( + index: usize, + value: &'ctx Self::T, + visitor: DynVisitor<'a, 'ctx>, + ) -> Future<'a, Result<Flow, Self::FieldError>, E> { + E::wrap(async move { + match index { + 0 => { + let walker = <&bool as Walk<M, E>>::into_walker(&value.a); + + assert_eq!(Walker::<E>::walk(walker, visitor).await, Ok(())); + + Ok(Flow::Continue) + } + 1 => { + let walker = <&bool as Walk<M, E>>::into_walker(&value.b); + + assert_eq!(Walker::<E>::walk(walker, visitor).await, Ok(())); + + Ok(Flow::Continue) + } + _ => Ok(Flow::Done), + } + }) + } +} + +struct Fields<'ctx, M, E: Effect> { + a: <bool as Build<'ctx, M, E>>::Builder, + b: <bool as Build<'ctx, M, E>>::Builder, +} + +#[derive(Copy, Clone)] +enum FieldMarker { + A, + B, +} + +impl<'ctx, M, E: Effect> builders::core::r#struct::StructTypeInfo<'ctx, M, E> for Info { + type Builders = Fields<'ctx, M, E>; + + type FieldMarker = FieldMarker; + + type T = X; + + fn marker_from_index(index: usize) -> Option<Self::FieldMarker> { + match index { + 0 => Some(FieldMarker::A), + 1 => Some(FieldMarker::B), + _ => None, + } + } + + fn marker_from_name(name: &str) -> Option<Self::FieldMarker> { + match name { + "a" => Some(FieldMarker::A), + "b" => Some(FieldMarker::B), + _ => None, + } + } + + type Error = (); + + fn from_builders<'a>(builders: Self::Builders) -> Future<'a, Result<Self::T, Self::Error>, E> { + E::wrap(async { + Ok(X { + a: builders.a.build().await.unwrap(), + b: builders.b.build().await.unwrap(), + }) + }) + } + + fn as_visitor<'a>( + marker: Self::FieldMarker, + builders: &'a mut Self::Builders, + ) -> DynVisitor<'a, 'ctx> { + match marker { + FieldMarker::A => builders.a.as_visitor(), + FieldMarker::B => builders.b.as_visitor(), + } + } + + type Seed = (); + + fn new_builders<'a>(_seed: Self::Seed) -> Future<'a, Self::Builders, E> { + E::wrap(async { + Fields { + a: Builder::<E>::from_seed(()).await, + b: Builder::<E>::from_seed(()).await, + } + }) + } +} + +#[test] +#[ignore] +fn demo() { + let value = X { a: true, b: false }; + + let (other, _) = transform::<StructBuilder<Info, DefaultMode, _>, _, Blocking>( + (), + StructWalker::<Info, _, DefaultMode, _>::new(&value), + ) + .value(); + + assert_eq!(other.unwrap(), value); +} + +#[test] +fn from_basic_tuple_like() { + // A tuple like is just a sequence. + let mut scope = MockSequenceScope::<Blocking>::new(); + + // First field. + scope.expect_next().once().returning(|visitor| { + // Visit a bool value. + // + // The bool visitor should report that it is done. + assert_eq!( + visit_value::<_, Blocking>(visitor, OwnedStatic(true)).value(), + VisitResult::Control(Flow::Done) + ); + + // We have another field. + Flow::Continue + }); + + // Second field. + scope.expect_next().once().returning(|visitor| { + // Visit a bool value. + // + // The bool visitor should report that it is done. + assert_eq!( + visit_value::<_, Blocking>(visitor, OwnedStatic(false)).value(), + VisitResult::Control(Flow::Done) + ); + + // No more fields. + Flow::Done + }); + + let mut builder = StructBuilder::<Info, DefaultMode, Blocking>::from_seed(()).value(); + let visitor = builder.as_visitor(); + + // Visit the sequence of field values. + assert!(matches!( + visit_sequence(visitor, &mut scope).value(), + VisitResult::Control(Flow::Done) + )); + + assert_eq!(builder.build().value().unwrap(), X { a: true, b: false }); +} + +#[test] +fn from_basic_map_like() { + // A map is built from a sequence. + let mut scope = MockSequenceScope::<Blocking>::new(); + + // Here we do the b field first to show a map-like doesn't care about order. + scope.expect_next().once().returning(|mut visitor| { + let mut walker = MockWalker::<(), ()>::new(); + + // We need to give the b field name in the key tag. + walker.expect_walk().once().returning(|visitor| { + assert_eq!( + visit_value::<_, Blocking>(visitor, TempBorrowedStatic("b")).value(), + VisitResult::Control(Flow::Done) + ); + + Ok(()) + }); + + // Tag the value with a key as the field name. + assert_eq!( + visit_tag::<tags::Key, Blocking, _>(TagConst, visitor.cast(), walker).value(), + Ok(VisitResult::Control(Flow::Continue)), + ); + + // Visit the value as normal. + assert_eq!( + visit_value::<_, Blocking>(visitor, OwnedStatic(true)).value(), + VisitResult::Control(Flow::Done) + ); + + // There is another field. + Flow::Continue + }); + + // The other field. + scope.expect_next().once().returning(|mut visitor| { + let mut walker = MockWalker::<(), ()>::new(); + + // Here we do field a. + walker.expect_walk().once().returning(|visitor| { + assert_eq!( + visit_value::<_, Blocking>(visitor, TempBorrowedStatic("a")).value(), + VisitResult::Control(Flow::Done) + ); + + Ok(()) + }); + + // Tag the value with a key. + assert_eq!( + visit_tag::<tags::Key, Blocking, _>(TagConst, visitor.cast(), walker).value(), + Ok(VisitResult::Control(Flow::Continue)), + ); + + // The field value. + assert_eq!( + visit_value::<_, Blocking>(visitor, OwnedStatic(false)).value(), + VisitResult::Control(Flow::Done) + ); + + // The sequence protocol allows for us to wait to decide if there is another item. + Flow::Continue + }); + + // There are no more fields. + scope.expect_next().once().returning(|_visitor| Flow::Done); + + let mut builder = StructBuilder::<Info, DefaultMode, Blocking>::from_seed(()).value(); + let mut visitor = builder.as_visitor(); + + // We need to provide the map tag to the struct before getting into the sequence. + // This tag notifies the struct builder to expect the sequence as a map. + assert_eq!( + visit_tag::<tags::Map, Blocking, _>(TagConst, visitor.cast(), NoopWalker::new()).value(), + Ok(VisitResult::Control(Flow::Continue)) + ); + + // Visit the sequence of fields. + assert_eq!( + visit_sequence(visitor, &mut scope).value(), + VisitResult::Control(Flow::Done) + ); + + // The struct is built as the mock walker above makes it. + assert_eq!(builder.build().value().unwrap(), X { a: false, b: true }); +} |