1pub use vyre_foundation::soundness::{
9 validate_dynamic_pipeline, validate_dynamic_primitive, validate_pipeline, validate_primitive,
10 DynamicPrimitiveSoundness, DynamicSoundnessViolation, PrecisionContract, PrimitiveSoundness,
11 Soundness, SoundnessTagged, SoundnessViolation,
12};
13
14#[derive(Clone, Debug, Eq, PartialEq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct DataflowEvidence<T> {
18 pub value: T,
20 pub soundness: Soundness,
22}
23
24impl<T> DataflowEvidence<T> {
25 #[inline]
27 #[must_use]
28 pub const fn new(value: T, soundness: Soundness) -> Self {
29 Self { value, soundness }
30 }
31
32 #[inline]
34 pub fn map<U>(self, transform: impl FnOnce(T) -> U) -> DataflowEvidence<U> {
35 DataflowEvidence {
36 value: transform(self.value),
37 soundness: self.soundness,
38 }
39 }
40
41 #[inline]
43 #[must_use]
44 pub fn as_ref(&self) -> DataflowEvidence<&T> {
45 DataflowEvidence {
46 value: &self.value,
47 soundness: self.soundness,
48 }
49 }
50}
51
52impl<T: PartialOrd> PartialOrd for DataflowEvidence<T> {
53 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
54 match soundness_rank(self.soundness).cmp(&soundness_rank(other.soundness)) {
55 std::cmp::Ordering::Equal => self.value.partial_cmp(&other.value),
56 ord => Some(ord),
57 }
58 }
59}
60
61impl<T: Ord> Ord for DataflowEvidence<T> {
62 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
63 match soundness_rank(self.soundness).cmp(&soundness_rank(other.soundness)) {
64 std::cmp::Ordering::Equal => self.value.cmp(&other.value),
65 ord => ord,
66 }
67 }
68}
69
70#[inline]
71const fn soundness_rank(s: Soundness) -> u8 {
72 match s {
73 Soundness::MustUnder => 0,
74 Soundness::Exact => 1,
75 Soundness::MayOver => 2,
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
88 fn map_identity_exact() {
89 let ev = DataflowEvidence::new(42, Soundness::Exact);
90 assert_eq!(ev.clone().map(|x| x), ev);
91 }
92
93 #[test]
94 fn map_identity_may_over() {
95 let ev = DataflowEvidence::new(String::from("hello"), Soundness::MayOver);
96 assert_eq!(ev.clone().map(|x| x), ev);
97 }
98
99 #[test]
100 fn map_identity_must_under() {
101 let ev = DataflowEvidence::new(vec![1, 2, 3], Soundness::MustUnder);
102 assert_eq!(ev.clone().map(|x| x), ev);
103 }
104
105 #[test]
106 fn map_composition_exact() {
107 let ev = DataflowEvidence::new(5, Soundness::Exact);
108 let f = |x: i32| x * 2;
109 let g = |x: i32| x + 3;
110 let left = ev.clone().map(f).map(g);
111 let right = ev.map(|x| g(f(x)));
112 assert_eq!(left, right);
113 }
114
115 #[test]
116 fn map_composition_may_over() {
117 let ev = DataflowEvidence::new(10, Soundness::MayOver);
118 let f = |x: i32| x - 1;
119 let g = |x: i32| x * x;
120 let left = ev.clone().map(f).map(g);
121 let right = ev.map(|x| g(f(x)));
122 assert_eq!(left, right);
123 }
124
125 #[test]
126 fn map_composition_must_under() {
127 let ev = DataflowEvidence::new(7u64, Soundness::MustUnder);
128 let f = |x: u64| x.wrapping_add(1);
129 let g = |x: u64| x >> 2;
130 let left = ev.clone().map(f).map(g);
131 let right = ev.map(|x| g(f(x)));
132 assert_eq!(left, right);
133 }
134
135 #[test]
136 fn map_preserves_soundness_exact() {
137 let ev = DataflowEvidence::new(1, Soundness::Exact);
138 let mapped = ev.map(|x| x.to_string());
139 assert_eq!(mapped.soundness, Soundness::Exact);
140 }
141
142 #[test]
143 fn map_preserves_soundness_may_over() {
144 let ev = DataflowEvidence::new(1, Soundness::MayOver);
145 let mapped = ev.map(|x| x.to_string());
146 assert_eq!(mapped.soundness, Soundness::MayOver);
147 }
148
149 #[test]
150 fn map_preserves_soundness_must_under() {
151 let ev = DataflowEvidence::new(1, Soundness::MustUnder);
152 let mapped = ev.map(|x| x.to_string());
153 assert_eq!(mapped.soundness, Soundness::MustUnder);
154 }
155
156 #[test]
157 fn map_changes_value_type() {
158 let ev = DataflowEvidence::new(123, Soundness::Exact);
159 let mapped: DataflowEvidence<String> = ev.map(|x| x.to_string());
160 assert_eq!(mapped.value, "123");
161 }
162
163 #[test]
164 fn map_on_reference_type() {
165 let ev = DataflowEvidence::new(vec![1, 2, 3], Soundness::Exact);
166 let mapped = ev.map(|mut v| {
167 v.push(4);
168 v
169 });
170 assert_eq!(mapped.value, vec![1, 2, 3, 4]);
171 }
172
173 #[test]
174 fn map_chain_three_functions() {
175 let ev = DataflowEvidence::new(2, Soundness::Exact);
176 let result = ev.map(|x| x + 1).map(|x| x * 2).map(|x| x - 1);
177 assert_eq!(result.value, 5); assert_eq!(result.soundness, Soundness::Exact);
179 }
180
181 #[test]
186 fn as_ref_exact_preserves_soundness() {
187 let ev = DataflowEvidence::new(42, Soundness::Exact);
188 let borrowed = ev.as_ref();
189 assert_eq!(borrowed.soundness, Soundness::Exact);
190 }
191
192 #[test]
193 fn as_ref_may_over_preserves_soundness() {
194 let ev = DataflowEvidence::new(42, Soundness::MayOver);
195 let borrowed = ev.as_ref();
196 assert_eq!(borrowed.soundness, Soundness::MayOver);
197 }
198
199 #[test]
200 fn as_ref_must_under_preserves_soundness() {
201 let ev = DataflowEvidence::new(42, Soundness::MustUnder);
202 let borrowed = ev.as_ref();
203 assert_eq!(borrowed.soundness, Soundness::MustUnder);
204 }
205
206 #[test]
207 fn as_ref_exact_value_matches() {
208 let ev = DataflowEvidence::new(String::from("test"), Soundness::Exact);
209 let borrowed = ev.as_ref();
210 assert_eq!(*borrowed.value, "test");
211 }
212
213 #[test]
214 fn as_ref_may_over_value_matches() {
215 let ev = DataflowEvidence::new(vec![7, 8, 9], Soundness::MayOver);
216 let borrowed = ev.as_ref();
217 assert_eq!(borrowed.value, &vec![7, 8, 9]);
218 }
219
220 #[test]
221 fn as_ref_must_under_value_matches() {
222 let ev = DataflowEvidence::new(std::f64::consts::PI, Soundness::MustUnder);
223 let borrowed = ev.as_ref();
224 assert_eq!(*borrowed.value, std::f64::consts::PI);
225 }
226
227 #[test]
228 fn as_ref_does_not_consume_original() {
229 let ev = DataflowEvidence::new(100, Soundness::Exact);
230 let _borrowed = ev.as_ref();
231 assert_eq!(ev.value, 100); }
233
234 #[test]
235 fn as_ref_twice_same_result() {
236 let ev = DataflowEvidence::new('a', Soundness::MayOver);
237 let b1 = ev.as_ref();
238 let b2 = ev.as_ref();
239 assert_eq!(b1.value, b2.value);
240 assert_eq!(b1.soundness, b2.soundness);
241 }
242
243 #[test]
244 fn as_ref_then_map() {
245 let ev = DataflowEvidence::new(5, Soundness::Exact);
246 let borrowed = ev.as_ref();
247 let mapped = borrowed.map(|&x| x * 3);
248 assert_eq!(mapped.value, 15);
249 assert_eq!(mapped.soundness, Soundness::Exact);
250 }
251
252 #[test]
257 fn eq_same_value_same_soundness_exact() {
258 let a = DataflowEvidence::new(42, Soundness::Exact);
259 let b = DataflowEvidence::new(42, Soundness::Exact);
260 assert_eq!(a, b);
261 }
262
263 #[test]
264 fn eq_same_value_same_soundness_may_over() {
265 let a = DataflowEvidence::new(42, Soundness::MayOver);
266 let b = DataflowEvidence::new(42, Soundness::MayOver);
267 assert_eq!(a, b);
268 }
269
270 #[test]
271 fn eq_same_value_same_soundness_must_under() {
272 let a = DataflowEvidence::new(42, Soundness::MustUnder);
273 let b = DataflowEvidence::new(42, Soundness::MustUnder);
274 assert_eq!(a, b);
275 }
276
277 #[test]
278 fn ne_same_value_different_soundness() {
279 let a = DataflowEvidence::new(42, Soundness::Exact);
280 let b = DataflowEvidence::new(42, Soundness::MayOver);
281 assert_ne!(a, b);
282 }
283
284 #[test]
285 fn ne_different_value_same_soundness() {
286 let a = DataflowEvidence::new(42, Soundness::Exact);
287 let b = DataflowEvidence::new(43, Soundness::Exact);
288 assert_ne!(a, b);
289 }
290
291 #[test]
292 fn ne_different_value_different_soundness() {
293 let a = DataflowEvidence::new(42, Soundness::Exact);
294 let b = DataflowEvidence::new(43, Soundness::MayOver);
295 assert_ne!(a, b);
296 }
297
298 #[test]
299 fn eq_reflected() {
300 let a = DataflowEvidence::new("x", Soundness::Exact);
301 let b = DataflowEvidence::new("x", Soundness::Exact);
302 assert!(a == b && b == a);
303 }
304
305 #[test]
306 fn eq_transitive() {
307 let a = DataflowEvidence::new(1, Soundness::MayOver);
308 let b = DataflowEvidence::new(1, Soundness::MayOver);
309 let c = DataflowEvidence::new(1, Soundness::MayOver);
310 assert!(a == b && b == c && a == c);
311 }
312
313 #[test]
314 fn ordering_must_under_less_than_exact() {
315 let a = DataflowEvidence::new(100, Soundness::MustUnder);
316 let b = DataflowEvidence::new(1, Soundness::Exact);
317 assert!(a < b);
318 }
319
320 #[test]
321 fn ordering_exact_less_than_may_over() {
322 let a = DataflowEvidence::new(100, Soundness::Exact);
323 let b = DataflowEvidence::new(1, Soundness::MayOver);
324 assert!(a < b);
325 }
326
327 #[test]
328 fn ordering_must_under_less_than_may_over() {
329 let a = DataflowEvidence::new(1000, Soundness::MustUnder);
330 let b = DataflowEvidence::new(0, Soundness::MayOver);
331 assert!(a < b);
332 }
333
334 #[test]
335 fn ordering_same_soundness_by_value() {
336 let a = DataflowEvidence::new(10, Soundness::Exact);
337 let b = DataflowEvidence::new(20, Soundness::Exact);
338 assert!(a < b);
339 assert!(b > a);
340 }
341
342 #[test]
343 fn ordering_equal_elements() {
344 let a = DataflowEvidence::new(5, Soundness::Exact);
345 let b = DataflowEvidence::new(5, Soundness::Exact);
346 assert_eq!(a.cmp(&b), std::cmp::Ordering::Equal);
347 }
348
349 #[test]
350 fn ordering_total_order_all_variants() {
351 let vals = vec![
352 DataflowEvidence::new(1, Soundness::MustUnder),
353 DataflowEvidence::new(2, Soundness::MustUnder),
354 DataflowEvidence::new(1, Soundness::Exact),
355 DataflowEvidence::new(2, Soundness::Exact),
356 DataflowEvidence::new(1, Soundness::MayOver),
357 DataflowEvidence::new(2, Soundness::MayOver),
358 ];
359 for i in 0..vals.len() {
360 for j in 0..vals.len() {
361 let ord = vals[i].cmp(&vals[j]);
362 let reverse = vals[j].cmp(&vals[i]);
363 assert_eq!(ord.reverse(), reverse);
364 }
365 }
366 }
367
368 #[test]
369 fn ordering_sorts_correctly() {
370 let mut vals = vec![
371 DataflowEvidence::new(2, Soundness::MayOver),
372 DataflowEvidence::new(1, Soundness::Exact),
373 DataflowEvidence::new(3, Soundness::MustUnder),
374 ];
375 vals.sort();
376 assert_eq!(vals[0].soundness, Soundness::MustUnder);
377 assert_eq!(vals[0].value, 3);
378 assert_eq!(vals[1].soundness, Soundness::Exact);
379 assert_eq!(vals[1].value, 1);
380 assert_eq!(vals[2].soundness, Soundness::MayOver);
381 assert_eq!(vals[2].value, 2);
382 }
383
384 #[test]
385 fn clone_produces_equal_value() {
386 let a = DataflowEvidence::new(42i32, Soundness::Exact);
387 let b = a.clone();
388 assert_eq!(a, b);
389 }
390
391 #[test]
392 fn debug_format_includes_value_and_soundness() {
393 let ev = DataflowEvidence::new(99, Soundness::Exact);
394 let s = format!("{:?}", ev);
395 assert!(s.contains("99"));
396 assert!(s.contains("Exact"));
397 }
398
399 #[test]
400 fn new_constructor_sets_fields() {
401 let ev = DataflowEvidence::new("payload", Soundness::MayOver);
402 assert_eq!(ev.value, "payload");
403 assert_eq!(ev.soundness, Soundness::MayOver);
404 }
405}