id_cache/
lib.rs

1///! A crate providing a simple data structure for caching id values.
2///!
3///! See the documentation for the [`IdCache<I, T>`] type for more information.
4use id_collections::{Count, Id, IdVec};
5use std::{borrow::Borrow, collections::HashMap, fmt::Debug, hash::Hash, ops::Index};
6
7/// A cache which generates sequentially-assigned ids for unique values.
8///
9/// # Example
10///
11/// ```
12/// use id_collections::id_type;
13/// use id_cache::IdCache;
14///
15/// #[id_type]
16/// struct WordId(u32);
17///
18/// let mut word_cache: IdCache<WordId, &str> = IdCache::new();
19///
20/// let foo_id = word_cache.make_id("foo");
21/// let bar_id = word_cache.make_id("bar");
22///
23/// assert_eq!(word_cache[foo_id], "foo");
24/// assert_eq!(word_cache[bar_id], "bar");
25///
26/// // ids for repeated values are reused:
27/// assert_eq!(word_cache.make_id("foo"), foo_id);
28/// ```
29///
30/// # Serde Support
31///
32/// When the `serde` Cargo feature is enabled, the `IdCache<I, T>` type can be serialized and
33/// deserialized using [Serde](https://serde.rs). An `IdCache<I, T>` is serialized as a sequence
34/// consisting of the unique values in the cache, ordered by id:
35///
36/// ```
37/// # #[cfg(feature = "serde")]
38/// # {
39/// use id_collections::id_type;
40/// use id_cache::IdCache;
41///
42/// #[id_type]
43/// struct WordId(u32);
44///
45/// let mut word_cache: IdCache<WordId, &str> = IdCache::new();
46/// word_cache.make_id("foo");
47/// word_cache.make_id("bar");
48///
49/// let serialized = serde_json::to_string(&word_cache).unwrap();
50/// assert_eq!(&serialized, r#"["foo","bar"]"#);
51/// # }
52/// ```
53#[derive(Clone)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55#[cfg_attr(feature = "serde", serde(transparent))]
56pub struct IdCache<I: Id, T> {
57    #[cfg_attr(feature = "serde", serde(bound(serialize = "T: serde::Serialize")))]
58    id_to_value: IdVec<I, T>,
59    #[cfg_attr(feature = "serde", serde(skip))]
60    value_to_id: HashMap<T, I>,
61}
62
63#[cfg(feature = "serde")]
64impl<'de, I: Id, T: Eq + Hash + Clone + serde::Deserialize<'de>> serde::Deserialize<'de>
65    for IdCache<I, T>
66{
67    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
68    where
69        D: serde::Deserializer<'de>,
70    {
71        let id_to_value = IdVec::<I, T>::deserialize(deserializer)?;
72
73        let mut value_to_id = HashMap::new();
74        for (id, value) in &id_to_value {
75            let existing = value_to_id.insert(value.clone(), id);
76            if existing.is_some() {
77                use serde::de::Error;
78                return Err(D::Error::custom("duplicate value in IdCache"));
79            }
80        }
81
82        Ok(IdCache {
83            id_to_value,
84            value_to_id,
85        })
86    }
87}
88
89#[cfg(feature = "serde")]
90mod serde_test {
91    #[test]
92    fn test_round_trip() {
93        use crate::IdCache;
94        use id_collections::id_type;
95
96        #[id_type]
97        struct TestId(u32);
98
99        let mut cache: IdCache<u32, String> = IdCache::new();
100        cache.make_id("foo".to_owned());
101        cache.make_id("bar".to_owned());
102
103        let serialized = serde_json::to_string(&cache).unwrap();
104        assert_eq!(serialized, r#"["foo","bar"]"#);
105
106        let deserialized = serde_json::from_str::<IdCache<u32, String>>(&serialized).unwrap();
107        assert_eq!(&deserialized, &cache);
108    }
109
110    #[test]
111    fn test_duplicate_err() {
112        use crate::IdCache;
113
114        let result = serde_json::from_str::<IdCache<u32, String>>(r#"["foo","foo"]"#);
115        assert!(result.is_err());
116    }
117}
118
119impl<I: Id, T> Default for IdCache<I, T> {
120    fn default() -> Self {
121        Self::new()
122    }
123}
124
125impl<I: Id + Debug, T: Debug> Debug for IdCache<I, T> {
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        self.id_to_value.fmt(f)
128    }
129}
130
131impl<I: Id, T: PartialEq> PartialEq for IdCache<I, T> {
132    fn eq(&self, other: &Self) -> bool {
133        self.id_to_value == other.id_to_value
134    }
135}
136
137impl<I: Id, T: Eq> Eq for IdCache<I, T> {}
138
139impl<I: Id, T> IdCache<I, T> {
140    /// Constructs a new, empty `IdCache<I, T>`.
141    ///
142    /// # Examples
143    ///
144    /// ```
145    /// # use id_cache::IdCache;
146    /// let cache: IdCache<u32, &str> = IdCache::new();
147    /// assert!(cache.is_empty());
148    /// ```
149    pub fn new() -> Self {
150        IdCache {
151            id_to_value: IdVec::new(),
152            value_to_id: HashMap::new(),
153        }
154    }
155
156    /// Constructs a new, empty `IdCache<I, T>` with space to hold at least `capacity` unique
157    /// values.
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// # use id_cache::IdCache;
163    /// let mut cache: IdCache<u32, &str> = IdCache::with_capacity(100);
164    /// assert!(cache.is_empty());
165    /// ```
166    pub fn with_capacity(capacity: usize) -> Self {
167        IdCache {
168            id_to_value: IdVec::with_capacity(capacity),
169            value_to_id: HashMap::with_capacity(capacity),
170        }
171    }
172
173    /// Returns the total number of ids that have been assigned to unique values in the
174    /// `IdCache<I, T>`.
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// # use id_cache::IdCache;
180    /// let mut cache: IdCache<u32, &str> = IdCache::new();
181    /// assert!(cache.count().is_empty());
182    /// cache.make_id("foo");
183    /// cache.make_id("bar");
184    /// assert_eq!(cache.count().to_value(), 2);
185    /// cache.make_id("foo"); // value already present, so does not assign a new id
186    /// assert_eq!(cache.count().to_value(), 2);
187    /// ```
188    pub fn count(&self) -> Count<I> {
189        self.id_to_value.count()
190    }
191
192    /// Returns the total number of unique values in the `IdCache<I, T>`.
193    ///
194    /// # Examples
195    ///
196    /// ```
197    /// # use id_cache::IdCache;
198    /// let mut cache: IdCache<u32, &str> = IdCache::new();
199    /// assert_eq!(cache.len(), 0);
200    /// cache.make_id("foo");
201    /// cache.make_id("bar");
202    /// assert_eq!(cache.len(), 2);
203    /// cache.make_id("foo"); // value already present, so does not increase the len
204    /// assert_eq!(cache.len(), 2);
205    /// ```
206    pub fn len(&self) -> usize {
207        self.id_to_value.len()
208    }
209
210    /// Returns `true` if the `IdCache<I, T>` contains no values.
211    ///
212    /// # Examples
213    ///
214    /// ```
215    /// # use id_cache::IdCache;
216    /// let mut cache: IdCache<u32, &str> = IdCache::new();
217    /// assert!(cache.is_empty());
218    /// cache.make_id("foo");
219    /// assert!(!cache.is_empty());
220    /// ```
221    pub fn is_empty(&self) -> bool {
222        self.id_to_value.is_empty()
223    }
224
225    /// Ensures `value` has an id in the `IdCache<I, T>`, and returns that id.
226    ///
227    /// If `value` is already present in the `IdCache<I, T>`, then `make_id` returns its existing
228    /// id. Otherwise, `make_id` returns a new sequentally-assigned id.
229    ///
230    /// # Panics
231    ///
232    /// Panics if the number of ids in the `IdCache<I, T>` overflows `I`.
233    ///
234    /// # Examples
235    ///
236    /// ```
237    /// # use id_cache::IdCache;
238    /// let mut cache: IdCache<u32, &str> = IdCache::new();
239    /// assert_eq!(cache.make_id("foo"), 0);
240    /// assert_eq!(cache.make_id("bar"), 1);
241    /// assert_eq!(cache.make_id("foo"), 0);
242    /// ```
243    pub fn make_id(&mut self, value: T) -> I
244    where
245        T: Eq + Hash + Clone,
246    {
247        *self
248            .value_to_id
249            .entry(value)
250            .or_insert_with_key(|value| self.id_to_value.push(value.clone()))
251    }
252
253    /// Returns the id of a value in the `IdCache<I, T>`, or `None` if the value is not present.
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// # use id_cache::IdCache;
259    /// let mut cache: IdCache<u32, &str> = IdCache::new();
260    /// let foo_id = cache.make_id("foo");
261    /// assert_eq!(cache.get_id(&"foo"), Some(foo_id));
262    /// assert_eq!(cache.get_id(&"bar"), None);
263    /// ```
264    pub fn get_id<U>(&self, value: &U) -> Option<I>
265    where
266        T: Borrow<U> + Eq + Hash,
267        U: Eq + Hash,
268    {
269        self.value_to_id.get(value).cloned()
270    }
271
272    /// Returns a reference to the value in the `IdCache<I, T>` associated with a given `id`, or
273    /// `None` if the id has not been assigned.
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// # use id_cache::IdCache;
279    /// let mut cache: IdCache<u32, &str> = IdCache::new();
280    /// let foo_id = cache.make_id("foo");
281    /// assert_eq!(foo_id, 0);
282    /// assert_eq!(cache.get_value(foo_id), Some(&"foo"));
283    /// assert_eq!(cache.get_value(1), None);
284    /// ```
285    pub fn get_value(&self, id: I) -> Option<&T> {
286        self.id_to_value.get(id)
287    }
288}
289
290impl<I: Id, T, J: Borrow<I>> Index<J> for IdCache<I, T> {
291    type Output = T;
292
293    /// Returns a reference to the value in the `IdCache<I, T>` associated with a given `id`.
294    ///
295    /// # Panics
296    ///
297    /// Panics if `id` has not been assigned.
298    ///
299    /// # Examples
300    ///
301    /// ```
302    /// # use id_cache::IdCache;
303    /// let mut cache: IdCache<u32, &str> = IdCache::new();
304    /// let foo_id = cache.make_id("foo");
305    /// assert_eq!(cache[foo_id], "foo");
306    /// ```
307    #[inline]
308    fn index(&self, id: J) -> &Self::Output {
309        let id = *id.borrow();
310        &self.id_to_value[id]
311    }
312}