1use {
2 core::convert::Infallible,
3 pbt::{
4 Pbt,
5 sigma::{Predicate, Sigma},
6 },
7};
8
9#[non_exhaustive]
10#[derive(Clone, Debug, Eq, PartialEq, Pbt)]
11pub enum Foo {
12 Bar,
13 Baz { a: u64, b: u64, c: Vec<Foo> },
14}
15
16#[derive(Clone, Debug, Eq, PartialEq, Pbt)]
17pub enum PartiallyInstantiable {
18 Instantiable,
19 Uninstantiable(Infallible),
20}
21
22#[derive(Clone, Debug, Eq, PartialEq, Pbt)]
23pub enum Uninhabited {}
24
25pub type NonAnswer = Sigma<u8, NotTheAnswer>;
26
27pub enum NotTheAnswer {}
28
29impl Foo {
30 #[inline]
31 #[must_use]
32 pub fn bus_factor(&self) -> usize {
33 match *self {
34 Self::Bar => 0,
35 Self::Baz { ref c, .. } => c.len(),
36 }
37 }
38}
39
40impl Predicate<u8> for NotTheAnswer {
41 type Error = String;
42
43 #[inline]
44 fn check(candidate: &u8) -> Result<(), Self::Error> {
45 if *candidate == 42 {
46 Err(format!(
47 "The Answer to the Ultimate Question of Life, the Universe, and Everything is {candidate}",
48 ))
49 } else {
50 Ok(())
51 }
52 }
53}
54
55#[cfg(test)]
56mod test {
57 use {super::*, pbt::search, pretty_assertions::assert_eq};
58
59 const N_CASES: usize = 1_000;
60
61 #[derive(Clone, Debug, Eq, PartialEq, Pbt)]
62 enum ShapedNever {}
63
64 #[derive(Clone, Debug, Eq, PartialEq, Pbt)]
65 enum ShapedPayload<T> {
66 Empty,
67 Bits(Vec<T>),
68 }
69
70 #[derive(Clone, Debug, Eq, PartialEq, Pbt)]
71 struct ShapedGenericWrapper<T> {
72 item: T,
73 }
74
75 #[derive(Clone, Debug, Eq, PartialEq, Pbt)]
76 struct ShapedUsesGenericWrapper {
77 wrapper: ShapedGenericWrapper<bool>,
78 }
79
80 type ShapedPayloadAlias = ShapedPayload<bool>;
81
82 #[derive(Clone, Debug, Eq, PartialEq, Pbt)]
83 struct ShapedAliasField {
84 payload: ShapedPayloadAlias,
85 }
86
87 #[derive(Clone, Debug, Eq, PartialEq, Pbt)]
88 struct ShapedOpaqueForeignPath {
89 set: std::collections::BTreeSet<u8>,
90 }
91
92 #[derive(Clone, Debug, Eq, PartialEq, Pbt)]
93 struct ShapedProjection<T> {
94 item: <Vec<T> as IntoIterator>::Item,
95 }
96
97 #[derive(Clone, Debug, Eq, PartialEq, Pbt)]
98 enum ShapedTree {
99 Leaf,
100 Branch {
101 left: Box<Self>,
102 value: usize,
103 flags: Vec<bool>,
104 right: Box<Self>,
105 },
106 }
107
108 #[test]
109 fn instantiability_logic() {
110 search::assert_eq(N_CASES, |pi: &PartiallyInstantiable| {
111 (pi.clone(), PartiallyInstantiable::Instantiable)
112 });
113 }
114
115 #[test]
116 fn search_and_minimize() {
117 let maybe_witness: Option<Foo> =
118 search::witness(N_CASES, |foo: &Foo| foo.bus_factor() >= 3);
119 assert_eq!(
120 maybe_witness,
121 Some(Foo::Baz {
122 a: 0,
123 b: 0,
124 c: vec![Foo::Bar, Foo::Bar, Foo::Bar],
125 }),
126 );
127 }
128
129 #[test]
130 fn sigma() {
131 search::assert(N_CASES, |u: &NonAnswer| **u != 42);
132 }
133
134 #[test]
135 fn empty_enum_is_supported() {
136 let maybe_witness: Option<Uninhabited> = search::witness(N_CASES, |_| true);
137 assert_eq!(maybe_witness, None);
138 }
139
140 #[test]
141 fn shaped_empty_enums_have_vacuous_eliminators() {
142 let absurd: fn(&ShapedNever) -> usize = |never| never.elim(());
143 let _ = absurd;
144 }
145
146 #[test]
147 fn shaped_generic_fields_do_not_need_recursion() {
148 let empty = <ShapedPayload<bool> as ShapedPayloadShaped>::empty(ShapedPayloadEmpty);
149 let empty_len = empty.elim(
150 (),
151 |(), ShapedPayloadEmpty| 0_usize,
152 |(), ShapedPayloadBits(bits)| bits.len(),
153 );
154 let payload =
155 <ShapedPayload<bool> as ShapedPayloadShaped>::bits(ShapedPayloadBits(vec![true]));
156 let len = payload.elim(
157 (),
158 |(), ShapedPayloadEmpty| 0_usize,
159 |(), ShapedPayloadBits(bits)| bits.len(),
160 );
161
162 assert_eq!(empty_len, 0);
163 assert_eq!(len, 1);
164 }
165
166 #[test]
167 fn shaped_custom_generic_field_paths_are_opaque_slots() {
168 let value = ShapedUsesGenericWrapper {
169 wrapper: ShapedGenericWrapper { item: true },
170 };
171 let selected = value.elim((), |(), ShapedUsesGenericWrapperShape { wrapper }| {
172 wrapper.item
173 });
174
175 assert!(selected);
176 }
177
178 #[test]
179 fn shaped_qualified_projection_fields_are_opaque_slots() {
180 let value = ShapedProjection::<u8> { item: 9 };
181 let selected = value.elim((), |(), ShapedProjectionShape { item }| *item);
182
183 assert_eq!(selected, 9);
184 }
185
186 #[test]
187 fn shaped_unknown_paths_are_opaque_slots() {
188 let alias = ShapedAliasField {
189 payload: ShapedPayload::Empty,
190 };
191 let is_empty = alias.elim((), |(), ShapedAliasFieldShape { payload }| {
192 matches!(*payload, ShapedPayload::Empty)
193 });
194 let foreign = ShapedOpaqueForeignPath {
195 set: [7_u8].into_iter().collect(),
196 };
197 let contains = foreign.elim((), |(), ShapedOpaqueForeignPathShape { set }| {
198 set.contains(&7)
199 });
200
201 assert!(is_empty);
202 assert!(contains);
203 }
204
205 #[test]
206 fn shaped_macro_generates_field_wise_eliminator() {
207 let tree = <ShapedTree as ShapedTreeShaped>::branch(ShapedTreeBranch {
208 left: Box::new(ShapedTree::Leaf),
209 value: 7,
210 flags: vec![true, false],
211 right: Box::new(ShapedTree::Leaf),
212 });
213
214 let selected = tree.elim(
215 5,
216 |_, ShapedTreeLeaf| 0,
217 |state, branch| {
218 assert!(matches!(**branch.left, ShapedTree::Leaf));
219 assert!(matches!(**branch.right, ShapedTree::Leaf));
220 assert_eq!(branch.flags.as_slice(), &[true, false]);
221 state + *branch.value
222 },
223 );
224
225 assert_eq!(selected, 12);
226 }
227}