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
use treaty::{
    any::{OwnedStatic, TypeName},
    protocol::{
        visitor::{tags, DynRecoverableScope},
        AsVisitor as _, DynVisitor,
    },
    walkers::core::value::{BorrowWalker, ValueWalker},
    Build, BuildExt as _, Builder, BuilderTypes, Status,
};

use crate::common::{
    protocol::{
        recoverable::{MockRecoverableScopeVisitor, RecoverableVisitorExt as _},
        tag::TagVisitorExt as _,
        value::ValueVisitorExt as _,
    },
    Blocking,
};

use effectful::{
    bound::Dynamic,
    effective::Effective,
    environment::{Environment, NativeForm},
    SendSync,
};

use macro_rules_attribute::derive;

mod common;

#[derive(Build!, PartialEq, Debug, SendSync)]
enum X {
    A(f32),
    B(bool),
}

#[test]
fn enum_builder_takes_unsigned_integer_variant_tag() {
    let mut builder = X::new_builder();

    // Use the int variant then value flow.
    {
        // Signal the first variant.
        builder.visit_tag_and_done::<{ tags::Variant::VALUE }, _>(ValueWalker::new(0u32));

        // Give the value for the variant.
        builder.visit_value_and_done(OwnedStatic(1.23f32));
    }

    assert_eq!(builder.build().into_value().unwrap().0, X::A(1.23));
}

#[test]
fn enum_builder_takes_string_variant_tag() {
    let mut builder = X::new_builder();

    // Use the string variant then value flow.
    {
        // Signal the A variant.
        builder.visit_tag_and_done::<{ tags::Variant::VALUE }, _>(BorrowWalker::new("A"));

        // Give the value for the variant.
        builder.visit_value_and_done(OwnedStatic(1.23f32));
    }

    assert_eq!(builder.build().into_value().unwrap().0, X::A(1.23));
}

#[test]
fn enum_builder_can_guess_the_variant() {
    let mut builder = X::new_builder();

    // Use the recoverable flow.
    {
        let mut scope = MockRecoverableScopeVisitor::<Blocking>::new();

        // The first builder for a f32 won't work.
        scope.expect_new_walk().once().returning(|mut visitor| {
            // The value for the B variant.
            visitor.visit_value_and_skipped(OwnedStatic(true));

            Status::Ok
        });

        // The second builder will work.
        scope.expect_new_walk().once().returning(|mut visitor| {
            // The value for the B variant.
            visitor.visit_value_and_done(OwnedStatic(true));

            Status::Ok
        });

        // Visit a recoverable scope the enum builder can use to try and find
        // a variant that can be built.
        builder.visit_recoverable_and_done(&mut scope);
    }

    // The enum should have a value now.
    assert_eq!(builder.build().into_value().unwrap().0, X::B(true));
}