lattices/
conflict.rs

1use std::cmp::Ordering::{self, *};
2
3use crate::{DeepReveal, IsBot, IsTop, LatticeFrom, LatticeOrd, Merge};
4
5/// A `Conflict` lattice, stores a single instance of `T` and goes to a "conflict" state (`None`)
6/// if inequal `T` instances are merged together.
7///
8/// Like [`Point<T>`](crate::Point), but will go to "conflict" (top/`None`) instead of panicking.
9///
10/// Can be thought of as a lattice with a domain of size one, corresponding to the specific value
11/// inside.
12///
13/// This can be used to wrap non-lattice (scalar) data into a lattice type.
14#[repr(transparent)]
15#[derive(Copy, Clone, Debug, Eq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct Conflict<T>(Option<T>);
18impl<T> Conflict<T> {
19    /// Create a new `Conflict` lattice instance from a value.
20    pub fn new(val: Option<T>) -> Self {
21        Self(val)
22    }
23
24    /// Create a new `Conflict` lattice instance from a value using `Into`.
25    pub fn new_from(val: impl Into<Option<T>>) -> Self {
26        Self::new(val.into())
27    }
28
29    /// Reveal the inner value as a shared reference.
30    pub fn as_reveal_ref(&self) -> Option<&T> {
31        self.0.as_ref()
32    }
33
34    /// Reveal the inner value as an exclusive reference.
35    pub fn as_reveal_mut(&mut self) -> Option<&mut T> {
36        self.0.as_mut()
37    }
38
39    /// Gets the inner by value, consuming self.
40    pub fn into_reveal(self) -> Option<T> {
41        self.0
42    }
43}
44
45impl<T> DeepReveal for Conflict<T> {
46    type Revealed = Option<T>;
47
48    fn deep_reveal(self) -> Self::Revealed {
49        self.0
50    }
51}
52
53impl<T, O> Merge<Conflict<O>> for Conflict<T>
54where
55    T: PartialEq<O>,
56{
57    fn merge(&mut self, other: Conflict<O>) -> bool {
58        if let Some(val_self) = &self.0 {
59            if other.0.is_none_or(|val_other| val_self != &val_other) {
60                self.0 = None;
61                return true;
62            }
63        }
64        false
65    }
66}
67
68impl<T> LatticeFrom<Conflict<T>> for Conflict<T> {
69    fn lattice_from(other: Conflict<T>) -> Self {
70        other
71    }
72}
73
74impl<T, O> PartialOrd<Conflict<O>> for Conflict<T>
75where
76    T: PartialEq<O>,
77{
78    fn partial_cmp(&self, other: &Conflict<O>) -> Option<Ordering> {
79        match (&self.0, &other.0) {
80            (None, None) => Some(Equal),
81            (None, Some(_)) => Some(Greater),
82            (Some(_), None) => Some(Less),
83            (Some(val_self), Some(val_other)) => (val_self == val_other).then_some(Equal),
84        }
85    }
86}
87impl<T, O> LatticeOrd<Conflict<O>> for Conflict<T> where Self: PartialOrd<Conflict<O>> {}
88
89impl<T, O> PartialEq<Conflict<O>> for Conflict<T>
90where
91    T: PartialEq<O>,
92{
93    fn eq(&self, other: &Conflict<O>) -> bool {
94        match (&self.0, &other.0) {
95            (None, None) => true,
96            (Some(val_self), Some(val_other)) => val_self == val_other,
97            _ => false,
98        }
99    }
100}
101
102impl<T> IsBot for Conflict<T> {
103    fn is_bot(&self) -> bool {
104        false
105    }
106}
107
108impl<T> IsTop for Conflict<T> {
109    fn is_top(&self) -> bool {
110        self.0.is_none()
111    }
112}
113
114#[cfg(test)]
115mod test {
116    use super::*;
117    use crate::WithBot;
118    use crate::test::{
119        check_all, check_lattice_is_bot, check_lattice_is_top, check_lattice_ord,
120        check_lattice_properties, check_partial_ord_properties,
121    };
122
123    #[test]
124    fn consistency() {
125        let items = &[
126            Conflict::new_from("foo"),
127            Conflict::new_from("bar"),
128            Conflict::new(None),
129        ];
130        check_lattice_ord(items);
131        check_partial_ord_properties(items);
132        check_lattice_properties(items);
133        check_lattice_is_bot(items);
134        check_lattice_is_top(items);
135    }
136
137    #[test]
138    fn consistency_withbot() {
139        let items = &[
140            WithBot::new_from(Conflict::new_from("foo")),
141            WithBot::new_from(Conflict::new_from("bar")),
142            WithBot::new_from(Conflict::new(None)),
143            WithBot::new(None),
144        ];
145        check_all(items);
146    }
147}