cipherstash_config/
list.rs

1use serde::{
2    de::{Deserialize, Deserializer, Visitor},
3    ser::{Serialize, SerializeSeq},
4};
5use std::fmt::Debug;
6use thiserror::Error;
7
8#[derive(Debug, Error)]
9#[error("Cannot insert duplicate entry to list: `{0:?}`")]
10pub struct DuplicateEntry<E: ListEntry>(E);
11
12pub trait ListEntry: PartialEq + Debug + Clone {}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct UniqueList<E: ListEntry> {
16    items: Vec<E>,
17}
18
19impl<E: ListEntry> Default for UniqueList<E> {
20    fn default() -> Self {
21        Self { items: vec![] }
22    }
23}
24
25impl<E: ListEntry> UniqueList<E> {
26    pub fn new() -> Self {
27        Self { items: vec![] }
28    }
29
30    pub fn with_capacity(size: usize) -> Self {
31        Self {
32            items: Vec::with_capacity(size),
33        }
34    }
35
36    pub fn try_insert(&mut self, entry: E) -> Result<(), DuplicateEntry<E>> {
37        if self.has_entry(&entry) {
38            Err(DuplicateEntry(entry))
39        } else {
40            self.items.push(entry);
41            Ok(())
42        }
43    }
44
45    pub fn has_entry<Q>(&self, query: &Q) -> bool
46    where
47        E: PartialEq<Q>,
48    {
49        self.get(query).is_some()
50    }
51
52    pub fn get<Q>(&self, query: &Q) -> Option<&E>
53    where
54        E: PartialEq<Q>,
55    {
56        if let Some(e) = self.items.iter().find(|e: &&E| *e == query) {
57            Some(e)
58        } else {
59            None
60        }
61    }
62
63    pub fn iter(&self) -> std::slice::Iter<'_, E> {
64        self.items.iter()
65    }
66
67    pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, E> {
68        self.items.iter_mut()
69    }
70
71    pub fn len(&self) -> usize {
72        self.items.len()
73    }
74
75    pub fn is_empty(&self) -> bool {
76        self.items.is_empty()
77    }
78}
79
80impl<E: ListEntry> std::ops::Index<usize> for UniqueList<E> {
81    type Output = E;
82
83    fn index(&self, i: usize) -> &Self::Output {
84        &self.items[i]
85    }
86}
87
88impl<E: ListEntry> Serialize for UniqueList<E>
89where
90    E: Serialize,
91{
92    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
93    where
94        S: serde::Serializer,
95    {
96        let mut seq = serializer.serialize_seq(Some(self.items.len()))?;
97
98        for element in self.items.iter() {
99            seq.serialize_element(element)?;
100        }
101
102        seq.end()
103    }
104}
105
106struct UniqueListDeserializer<E>(std::marker::PhantomData<E>);
107
108impl<E> UniqueListDeserializer<E> {
109    fn new() -> Self {
110        Self(Default::default())
111    }
112}
113
114impl<'de, E: ListEntry + Deserialize<'de>> Visitor<'de> for UniqueListDeserializer<E> {
115    type Value = UniqueList<E>;
116
117    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
118        write!(formatter, "a unique list of values")
119    }
120
121    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
122    where
123        A: serde::de::SeqAccess<'de>,
124    {
125        let mut list = seq
126            .size_hint()
127            .map(Self::Value::with_capacity)
128            .unwrap_or_else(Self::Value::new);
129
130        while let Some(value) = seq.next_element::<E>()? {
131            list.try_insert(value).map_err(serde::de::Error::custom)?;
132        }
133
134        Ok(list)
135    }
136}
137
138impl<'de, E: ListEntry> Deserialize<'de> for UniqueList<E>
139where
140    E: Deserialize<'de>,
141{
142    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
143    where
144        D: Deserializer<'de>,
145    {
146        deserializer.deserialize_seq(UniqueListDeserializer::<E>::new())
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153    use serde_json::{from_str, to_string};
154
155    impl ListEntry for usize {}
156
157    #[derive(Clone, PartialEq, Debug)]
158    struct MyEntry(u32, u32);
159    impl ListEntry for MyEntry {}
160
161    #[test]
162    fn empty_has_key() -> Result<(), Box<dyn std::error::Error>> {
163        let list: UniqueList<MyEntry> = UniqueList::new();
164        assert!(!list.has_entry(&MyEntry(10, 10)));
165
166        Ok(())
167    }
168
169    #[test]
170    fn empty_get() -> Result<(), Box<dyn std::error::Error>> {
171        let list: UniqueList<MyEntry> = UniqueList::new();
172        assert_eq!(list.get(&MyEntry(1, 1)), None);
173
174        Ok(())
175    }
176
177    #[test]
178    fn insert_dupe() -> Result<(), Box<dyn std::error::Error>> {
179        let mut list: UniqueList<MyEntry> = UniqueList::new();
180        list.try_insert(MyEntry(10, 10))?;
181        assert!(list.try_insert(MyEntry(10, 10)).is_err());
182
183        Ok(())
184    }
185
186    #[test]
187    fn insert_and_get() -> Result<(), Box<dyn std::error::Error>> {
188        let mut list: UniqueList<MyEntry> = UniqueList::new();
189        list.try_insert(MyEntry(5, 15))?;
190        let result = list.get(&MyEntry(5, 15));
191        assert_eq!(result, Some(&MyEntry(5, 15)));
192
193        Ok(())
194    }
195
196    #[test]
197    fn test_serialise_with_serde() -> Result<(), Box<dyn std::error::Error>> {
198        let mut list = UniqueList::<usize>::new();
199
200        list.try_insert(40)?;
201        list.try_insert(10)?;
202        list.try_insert(20)?;
203        list.try_insert(30)?;
204
205        assert_eq!(to_string(&list)?, "[40,10,20,30]");
206
207        Ok(())
208    }
209
210    #[test]
211    fn test_deserialise_with_serde() -> Result<(), Box<dyn std::error::Error>> {
212        let mut list = UniqueList::<usize>::new();
213        list.try_insert(40)?;
214        list.try_insert(10)?;
215        list.try_insert(20)?;
216        list.try_insert(30)?;
217
218        assert_eq!(from_str::<UniqueList<usize>>("[40,10,20,30]")?, list);
219
220        Ok(())
221    }
222
223    #[test]
224    fn test_fail_deserialise_with_serde() -> Result<(), Box<dyn std::error::Error>> {
225        assert_eq!(
226            from_str::<UniqueList<usize>>("[10,10,10,10]")
227                .expect_err("Expected de to fail")
228                .to_string(),
229            "Cannot insert duplicate entry to list: `10` at line 1 column 7"
230        );
231
232        Ok(())
233    }
234}