Skip to main content

ferroui_base/utilities/
value_single_or_list.rs

1/// A list like struct optimized for holding zero or one items.
2///
3/// Once more than one value has been added to this storage it will switch to
4/// using a `Vec` internally.
5pub struct ValueSingleOrList<T> {
6    is_single_set: bool,
7    single: Option<T>,
8    list: Option<Vec<T>>,
9}
10
11impl<T> Default for ValueSingleOrList<T> {
12    fn default() -> Self {
13        Self { is_single_set: false, single: None, list: None }
14    }
15}
16
17impl<T: PartialEq> ValueSingleOrList<T> {
18    /// Single contained value. Only valid if [`is_single`](Self::is_single) is set.
19    pub fn single(&self) -> Option<&T> {
20        self.single.as_ref()
21    }
22
23    /// List of values.
24    pub fn list(&self) -> Option<&Vec<T>> {
25        self.list.as_ref()
26    }
27
28    /// If this struct is backed by a list.
29    pub fn has_list(&self) -> bool {
30        self.list.is_some()
31    }
32
33    /// If this struct contains only single value and storage was not promoted to a list.
34    pub fn is_single(&self) -> bool {
35        self.list.is_none() && self.is_single_set
36    }
37
38    /// Adds a value.
39    pub fn add(&mut self, value: T) {
40        if self.list.is_some() {
41            if let Some(list) = &mut self.list {
42                list.push(value);
43            }
44        } else if !self.is_single_set {
45            self.single = Some(value);
46
47            self.is_single_set = true;
48        } else {
49            let mut list = Vec::new();
50
51            if let Some(single) = self.single.take() {
52                list.push(single);
53            }
54            list.push(value);
55
56            self.list = Some(list);
57        }
58    }
59
60    /// Removes a value.
61    pub fn remove(&mut self, value: &T) -> bool {
62        if let Some(list) = &mut self.list {
63            return match list.iter().position(|item| item == value) {
64                Some(index) => {
65                    list.remove(index);
66                    true
67                }
68                None => false,
69            };
70        }
71
72        if !self.is_single_set {
73            return false;
74        }
75
76        if self.single.as_ref() == Some(value) {
77            self.single = None;
78
79            self.is_single_set = false;
80
81            return true;
82        }
83
84        false
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    // Not from upstream: the original has no tests of this type.
91    use super::*;
92
93    #[test]
94    fn switches_from_a_single_value_to_a_list() {
95        let mut target = ValueSingleOrList::<i32>::default();
96        assert!(!target.is_single());
97        assert!(!target.has_list());
98
99        target.add(1);
100        assert!(target.is_single());
101        assert_eq!(Some(&1), target.single());
102
103        target.add(2);
104        assert!(!target.is_single());
105        assert!(target.has_list());
106        assert_eq!(None, target.single());
107        assert_eq!(Some(&vec![1, 2]), target.list());
108
109        assert!(target.remove(&1));
110        assert!(!target.remove(&1));
111        assert!(target.has_list());
112        assert_eq!(Some(&vec![2]), target.list());
113    }
114
115    #[test]
116    fn removes_the_single_value() {
117        let mut target = ValueSingleOrList::<i32>::default();
118        assert!(!target.remove(&1));
119
120        target.add(1);
121        assert!(!target.remove(&2));
122        assert!(target.remove(&1));
123        assert!(!target.is_single());
124        assert!(!target.remove(&1));
125    }
126}