1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use macro_rules_attribute::derive;
use treaty::{
    any::{OwnedStatic, TempBorrowedStatic},
    effect::blocking::Blocking,
    protocol::visitor::{tags, visit_sequence, visit_tag, visit_value, TagConst, VisitResult},
    walkers::core::noop::NoopWalker,
    Build, BuildExt as _, Builder, DefaultMode, Flow,
};

use crate::common::{
    protocol::{
        sequence::MockSequenceScope,
        value::{MockValueVisitor, 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 });
}