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
use crate::ux::{Element, Elements, Value};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Outcome {
Passed,
Failed(Element),
}
pub trait Property {
fn result(&self) -> Outcome;
fn and<O>(self, other: O) -> And<Self, O>
where
Self: Sized,
{
And {
prop_a: self,
prop_b: other,
}
}
fn or<O>(self, other: O) -> Or<Self, O>
where
Self: Sized,
{
Or {
prop_a: self,
prop_b: other,
}
}
}
pub struct BoxProperty(Box<dyn Property>);
pub struct And<A, B> {
prop_a: A,
prop_b: B,
}
impl<A, B> Property for And<A, B>
where
A: Property,
B: Property,
{
fn result(&self) -> Outcome {
fn failure_element(left: Value, right: Value) -> Outcome {
let mut output = Elements::new();
output.append("left", left);
output.append("right", right);
Outcome::Failed(Element::new("and", output.into()))
}
match (self.prop_a.result(), self.prop_b.result()) {
(Outcome::Passed, Outcome::Passed) => Outcome::Passed,
(Outcome::Failed(f1), Outcome::Passed) => {
failure_element(Value::sub(f1), "passed".into())
}
(Outcome::Passed, Outcome::Failed(f2)) => {
failure_element("passed".into(), Value::sub(f2))
}
(Outcome::Failed(f1), Outcome::Failed(f2)) => {
failure_element(Value::sub(f1), Value::sub(f2))
}
}
}
}
pub struct Or<A, B> {
prop_a: A,
prop_b: B,
}
impl<A, B> Property for Or<A, B>
where
A: Property,
B: Property,
{
fn result(&self) -> Outcome {
match (self.prop_a.result(), self.prop_b.result()) {
(Outcome::Passed, _) => Outcome::Passed,
(_, Outcome::Passed) => Outcome::Passed,
(Outcome::Failed(f1), Outcome::Failed(f2)) => {
let mut output = Elements::new();
output.append("left", Value::sub(f1));
output.append("right", Value::sub(f2));
Outcome::Failed(Element::new("or", output.into()))
}
}
}
}