use macro_rules_attribute::derive;
use treaty::{
any::{OwnedStatic, TempBorrowedStatic},
effect::blocking::Blocking,
protocol::visitor::{tags, visit_sequence, visit_tag, TagConst, VisitResult},
walkers::core::noop::NoopWalker,
Build, BuildExt as _, Builder, Flow,
};
use crate::common::{
protocol::{sequence::MockSequenceScope, value::ValueVisitorExt as _},
walker::MockWalker,
};
mod common;
#[derive(Build!, Debug, PartialEq)]
struct X {
a: bool,
b: bool,
}
#[test]
fn a_struct_builder_can_build_from_a_sequence_of_field_values() {
let mut scope;
{
// A tuple-like struct is just a sequence.
scope = MockSequenceScope::<Blocking>::new();
// First field.
scope.expect_next().once().returning(|visitor| {
// Visit a bool value.
visitor.visit_value_and_done(OwnedStatic(true));
// We have another field.
Flow::Continue
});
// Second field.
scope.expect_next().once().returning(|visitor| {
// Visit a bool value.
visitor.visit_value_and_done(OwnedStatic(false));
// No more fields.
Flow::Done
});
}
// Make a builder for the struct.
let mut builder = X::new_builder();
// Visit the sequence of field values.
// The struct visitor should report as done.
assert!(matches!(
visit_sequence(builder.as_visitor(), &mut scope).value(),
VisitResult::Control(Flow::Done)
));
// The builder should be able to build a instance of the struct.
assert_eq!(builder.build().value().unwrap(), X { a: true, b: false });
}
#[test]
fn a_struct_builder_can_build_from_a_sequence_of_keyed_values() {
let mut scope;
{
// A map is a sequence of keyed values.
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;
{
walker = MockWalker::<(), ()>::new();
// We need to give the b field name in the key tag.
walker.expect_walk().once().returning(|visitor| {
visitor.visit_value_and_done(TempBorrowedStatic("b"));
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.
visitor.visit_value_and_done(OwnedStatic(true));
// There is another field.
Flow::Continue
});
// The other field.
scope.expect_next().once().returning(|mut visitor| {
let mut walker;
{
walker = MockWalker::<(), ()>::new();
// Here we do field a.
walker.expect_walk().once().returning(|visitor| {
visitor.visit_value_and_done(TempBorrowedStatic("a"));
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.
visitor.visit_value_and_done(OwnedStatic(false));
// 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 = X::new_builder();
// 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, builder.as_visitor(), NoopWalker::new())
.value(),
Ok(VisitResult::Control(Flow::Continue))
);
// Visit the sequence of fields.
assert_eq!(
visit_sequence(builder.as_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 });
}