Skip to main content

weir/
soundness.rs

1//! Soundness regime markers  -  re-exported from `vyre_foundation::soundness`.
2//!
3//! The canonical definition lives in vyre per the LEGO discipline (weir
4//! always calls vyre, vyre never calls anything else). This module exists
5//! to keep `weir::soundness::*` paths stable for existing consumers while
6//! the implementation lives upstream.
7
8pub 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/// Dataflow result paired with the soundness regime under which it was derived.
15#[derive(Clone, Debug, Eq, PartialEq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct DataflowEvidence<T> {
18    /// Decoded dataflow payload.
19    pub value: T,
20    /// Soundness regime for `value`.
21    pub soundness: Soundness,
22}
23
24impl<T> DataflowEvidence<T> {
25    /// Attach a soundness regime to decoded dataflow evidence.
26    #[inline]
27    #[must_use]
28    pub const fn new(value: T, soundness: Soundness) -> Self {
29        Self { value, soundness }
30    }
31
32    /// Transform the payload while preserving the soundness regime.
33    #[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    /// Borrow the payload without dropping the soundness tag.
42    #[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    // ------------------------------------------------------------------
84    // Functor laws for DataflowEvidence::map
85    // ------------------------------------------------------------------
86
87    #[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); // (2+1)*2-1 = 5
178        assert_eq!(result.soundness, Soundness::Exact);
179    }
180
181    // ------------------------------------------------------------------
182    // DataflowEvidence::as_ref correctness
183    // ------------------------------------------------------------------
184
185    #[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); // ev still usable
232    }
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    // ------------------------------------------------------------------
253    // Equality and ordering
254    // ------------------------------------------------------------------
255
256    #[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}