Diffstat (limited to 'tests/builder_struct.rs')
-rw-r--r--tests/builder_struct.rs285
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 });
+}