unc_sdk/store/iterable_map/
entry.rs

1use crate::env;
2use borsh::{BorshDeserialize, BorshSerialize};
3
4use super::ValueAndIndex;
5use crate::store::key::ToKey;
6use crate::store::{IterableMap, LookupMap, Vector, ERR_INCONSISTENT_STATE};
7
8/// A view into a single entry in the map, which can be vacant or occupied.
9pub enum Entry<'a, K: 'a, V: 'a, H: 'a>
10where
11    K: BorshSerialize + Ord + Clone,
12    V: BorshSerialize + BorshDeserialize,
13    H: ToKey,
14{
15    Occupied(OccupiedEntry<'a, K, V, H>),
16    Vacant(VacantEntry<'a, K, V, H>),
17}
18
19impl<'a, K, V, H> Entry<'a, K, V, H>
20where
21    K: BorshSerialize + Ord + Clone,
22    V: BorshSerialize + BorshDeserialize,
23    H: ToKey,
24{
25    pub(super) fn new(
26        key: K,
27        keys: &'a mut Vector<K>,
28        values: &'a mut LookupMap<K, ValueAndIndex<V>, H>,
29    ) -> Self {
30        if values.contains_key(&key) {
31            return Self::Occupied(OccupiedEntry { key, keys, values });
32        }
33        Self::Vacant(VacantEntry { key, keys, values })
34    }
35}
36
37impl<'a, K, V, H> Entry<'a, K, V, H>
38where
39    K: BorshSerialize + Ord + Clone,
40    V: BorshSerialize + BorshDeserialize,
41    H: ToKey,
42{
43    /// Returns a reference to this entry's key.
44    ///
45    /// # Examples
46    ///
47    /// ```
48    /// use unc_sdk::store::IterableMap;
49    ///
50    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
51    /// assert_eq!(map.entry("poneyland".to_string()).key(), "poneyland");
52    /// ```
53    pub fn key(&self) -> &K {
54        match self {
55            Entry::Occupied(entry) => entry.key(),
56            Entry::Vacant(entry) => entry.key(),
57        }
58    }
59}
60
61impl<'a, K, V, H> Entry<'a, K, V, H>
62where
63    K: BorshSerialize + BorshDeserialize + Ord + Clone,
64    V: BorshSerialize + BorshDeserialize,
65    H: ToKey,
66{
67    /// Ensures a value is in the entry by inserting the default if empty, and returns
68    /// a mutable reference to the value in the entry.
69    ///
70    /// # Examples
71    ///
72    /// ```
73    /// use unc_sdk::store::IterableMap;
74    ///
75    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
76    ///
77    /// map.entry("poneyland".to_string()).or_insert(3);
78    /// assert_eq!(map["poneyland"], 3);
79    ///
80    /// *map.entry("poneyland".to_string()).or_insert(10) *= 2;
81    /// assert_eq!(map["poneyland"], 6);
82    /// ```
83    pub fn or_insert(self, default: V) -> &'a mut V {
84        self.or_insert_with(|| default)
85    }
86
87    /// Ensures a value is in the entry by inserting the result of the default function if empty,
88    /// and returns a mutable reference to the value in the entry.
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// use unc_sdk::store::IterableMap;
94    ///
95    /// let mut map: IterableMap<String, String> = IterableMap::new(b"m");
96    /// let s = "hoho".to_string();
97    ///
98    /// map.entry("poneyland".to_string()).or_insert_with(|| s);
99    ///
100    /// assert_eq!(map["poneyland"], "hoho".to_string());
101    /// ```
102    pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
103        self.or_insert_with_key(|_| default())
104    }
105
106    /// Ensures a value is in the entry by inserting, if empty, the result of the default function.
107    /// This method allows for generating key-derived values for insertion by providing the default
108    /// function a reference to the key that was moved during the `.entry(key)` method call.
109    ///
110    /// The reference to the moved key is provided so that cloning or copying the key is
111    /// unnecessary, unlike with `.or_insert_with(|| ... )`.
112    ///
113    /// # Examples
114    ///
115    /// ```
116    /// use unc_sdk::store::IterableMap;
117    ///
118    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
119    ///
120    /// map.entry("poneyland".to_string()).or_insert_with_key(|key| key.chars().count() as u32);
121    ///
122    /// assert_eq!(map["poneyland"], 9);
123    /// ```
124    pub fn or_insert_with_key<F: FnOnce(&K) -> V>(self, default: F) -> &'a mut V
125    where
126        K: BorshDeserialize,
127    {
128        match self {
129            Self::Occupied(entry) => entry.into_mut(),
130            Self::Vacant(entry) => {
131                let value = default(entry.key());
132                entry.insert(value)
133            }
134        }
135    }
136
137    /// Ensures a value is in the entry by inserting the default value if empty,
138    /// and returns a mutable reference to the value in the entry.
139    ///
140    /// # Examples
141    ///
142    /// ```
143    /// # fn main() {
144    /// use unc_sdk::store::IterableMap;
145    ///
146    /// let mut map: IterableMap<String, Option<u32>> = IterableMap::new(b"m");
147    /// map.entry("poneyland".to_string()).or_default();
148    ///
149    /// assert_eq!(map["poneyland"], None);
150    /// # }
151    /// ```
152    pub fn or_default(self) -> &'a mut V
153    where
154        V: Default,
155    {
156        match self {
157            Self::Occupied(entry) => entry.into_mut(),
158            Self::Vacant(entry) => entry.insert(Default::default()),
159        }
160    }
161
162    /// Provides in-place mutable access to an occupied entry before any
163    /// potential inserts into the map.
164    ///
165    /// # Examples
166    ///
167    /// ```
168    /// use unc_sdk::store::IterableMap;
169    ///
170    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
171    ///
172    /// map.entry("poneyland".to_string())
173    ///    .and_modify(|e| { *e += 1 })
174    ///    .or_insert(42);
175    /// assert_eq!(map["poneyland"], 42);
176    ///
177    /// map.entry("poneyland".to_string())
178    ///    .and_modify(|e| { *e += 1 })
179    ///    .or_insert(42);
180    /// assert_eq!(map["poneyland"], 43);
181    /// ```
182    pub fn and_modify<F>(mut self, f: F) -> Self
183    where
184        F: FnOnce(&mut V),
185    {
186        if let Self::Occupied(entry) = &mut self {
187            f(entry.get_mut());
188        }
189        self
190    }
191}
192
193/// View into an occupied entry in a [`IterableMap`](super::IterableMap).
194/// This is part of the [`Entry`] enum.
195pub struct OccupiedEntry<'a, K, V, H>
196where
197    K: BorshSerialize + Ord + Clone,
198    V: BorshSerialize + BorshDeserialize,
199    H: ToKey,
200{
201    key: K,
202    keys: &'a mut Vector<K>,
203    values: &'a mut LookupMap<K, ValueAndIndex<V>, H>,
204}
205
206impl<'a, K, V, H> OccupiedEntry<'a, K, V, H>
207where
208    K: BorshSerialize + Ord + Clone,
209    V: BorshSerialize + BorshDeserialize,
210    H: ToKey,
211{
212    /// Gets a reference to the key in the entry.
213    pub fn key(&self) -> &K {
214        &self.key
215    }
216
217    /// Take the ownership of the key and value from the map.
218    /// TODO: This shared a lot of code with [`super::IterableMap::remove_entry`], it would be best
219    /// to share that code.
220    ///
221    /// # Examples
222    ///
223    /// ```
224    /// use unc_sdk::store::IterableMap;
225    /// use unc_sdk::store::iterable_map::Entry;
226    ///
227    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
228    /// map.entry("poneyland".to_string()).or_insert(12);
229    ///
230    /// if let Entry::Occupied(o) = map.entry("poneyland".to_string()) {
231    ///     // We delete the entry from the map.
232    ///     o.remove_entry();
233    /// }
234    ///
235    /// assert_eq!(map.contains_key("poneyland"), false);
236    /// ```
237    pub fn remove_entry(self) -> (K, V)
238    where
239        K: BorshDeserialize + Ord + Clone,
240        V: BorshDeserialize,
241    {
242        let old_value =
243            self.values.remove(&self.key).unwrap_or_else(|| env::panic_str(ERR_INCONSISTENT_STATE));
244        let last_index = self.keys.len() - 1;
245        self.keys.swap_remove(old_value.key_index);
246
247        IterableMap::remove_entry_helper(self.keys, self.values, old_value.key_index, last_index);
248
249        (self.key, old_value.value)
250    }
251
252    /// Gets a reference to the value in the entry.
253    ///
254    /// # Examples
255    ///
256    /// ```
257    /// use unc_sdk::store::IterableMap;
258    /// use unc_sdk::store::iterable_map::Entry;
259    ///
260    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
261    /// map.entry("poneyland".to_string()).or_insert(12);
262    ///
263    /// if let Entry::Occupied(o) = map.entry("poneyland".to_string()) {
264    ///     assert_eq!(o.get(), &12);
265    /// }
266    /// ```
267    pub fn get(&self) -> &V {
268        &self.values.get(&self.key).unwrap_or_else(|| env::panic_str(ERR_INCONSISTENT_STATE)).value
269    }
270
271    /// Gets a mutable reference to the value in the entry.
272    ///
273    /// If you need a reference to the `OccupiedEntry` which may outlive the
274    /// destruction of the `Entry` value, see [`into_mut`].
275    ///
276    /// [`into_mut`]: Self::into_mut
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// use unc_sdk::store::IterableMap;
282    /// use unc_sdk::store::iterable_map::Entry;
283    ///
284    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
285    /// map.entry("poneyland".to_string()).or_insert(12);
286    ///
287    /// assert_eq!(map["poneyland"], 12);
288    /// if let Entry::Occupied(mut o) = map.entry("poneyland".to_string()) {
289    ///     *o.get_mut() += 10;
290    ///     assert_eq!(*o.get(), 22);
291    ///
292    ///     // We can use the same Entry multiple times.
293    ///     *o.get_mut() += 2;
294    /// }
295    ///
296    /// assert_eq!(map["poneyland"], 24);
297    /// ```
298    pub fn get_mut(&mut self) -> &mut V {
299        &mut self
300            .values
301            .get_mut(&self.key)
302            .unwrap_or_else(|| env::panic_str(ERR_INCONSISTENT_STATE))
303            .value
304    }
305
306    /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
307    /// with a lifetime bound to the map itself.
308    ///
309    /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`].
310    ///
311    /// [`get_mut`]: Self::get_mut
312    ///
313    /// # Examples
314    ///
315    /// ```
316    /// use unc_sdk::store::IterableMap;
317    /// use unc_sdk::store::iterable_map::Entry;
318    ///
319    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
320    /// map.entry("poneyland".to_string()).or_insert(12);
321    ///
322    /// assert_eq!(map["poneyland"], 12);
323    /// if let Entry::Occupied(o) = map.entry("poneyland".to_string()) {
324    ///     *o.into_mut() += 10;
325    /// }
326    ///
327    /// assert_eq!(map["poneyland"], 22);
328    /// ```
329    pub fn into_mut(self) -> &'a mut V {
330        &mut self
331            .values
332            .get_mut(&self.key)
333            .unwrap_or_else(|| env::panic_str(ERR_INCONSISTENT_STATE))
334            .value
335    }
336
337    /// Sets the value of the entry, and returns the entry's old value.
338    ///
339    /// # Examples
340    ///
341    /// ```
342    /// use unc_sdk::store::IterableMap;
343    /// use unc_sdk::store::iterable_map::Entry;
344    ///
345    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
346    /// map.entry("poneyland".to_string()).or_insert(12);
347    ///
348    /// if let Entry::Occupied(mut o) = map.entry("poneyland".to_string()) {
349    ///     assert_eq!(o.insert(15), 12);
350    /// }
351    ///
352    /// assert_eq!(map["poneyland"], 15);
353    /// ```
354    pub fn insert(&mut self, value: V) -> V {
355        core::mem::replace(self.get_mut(), value)
356    }
357
358    /// Takes the value out of the entry, and returns it.
359    ///
360    /// # Examples
361    ///
362    /// ```
363    /// use unc_sdk::store::IterableMap;
364    /// use unc_sdk::store::iterable_map::Entry;
365    ///
366    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
367    /// map.entry("poneyland".to_string()).or_insert(12);
368    ///
369    /// if let Entry::Occupied(o) = map.entry("poneyland".to_string()) {
370    ///     assert_eq!(o.remove(), 12);
371    /// }
372    ///
373    /// assert_eq!(map.contains_key("poneyland"), false);
374    /// ```
375    pub fn remove(self) -> V
376    where
377        K: BorshDeserialize,
378    {
379        self.remove_entry().1
380    }
381}
382
383/// View into a vacant entry in a [`IterableMap`](super::IterableMap).
384/// This is part of the [`Entry`] enum.
385pub struct VacantEntry<'a, K, V, H>
386where
387    K: BorshSerialize + Ord,
388    V: BorshSerialize,
389    H: ToKey,
390{
391    key: K,
392    values: &'a mut LookupMap<K, ValueAndIndex<V>, H>,
393    keys: &'a mut Vector<K>,
394}
395
396impl<'a, K, V, H> VacantEntry<'a, K, V, H>
397where
398    K: BorshSerialize + Ord,
399    V: BorshSerialize + BorshDeserialize,
400    H: ToKey,
401{
402    /// Gets a reference to the key that would be used when inserting a value
403    /// through the `VacantEntry`.
404    pub fn key(&self) -> &K {
405        &self.key
406    }
407
408    /// Take ownership of the key.
409    ///
410    /// # Examples
411    ///
412    /// ```
413    /// use unc_sdk::store::IterableMap;
414    /// use unc_sdk::store::iterable_map::Entry;
415    ///
416    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
417    ///
418    /// if let Entry::Vacant(v) = map.entry("poneyland".to_string()) {
419    ///     v.into_key();
420    /// }
421    /// ```
422    pub fn into_key(self) -> K {
423        self.key
424    }
425
426    /// Sets the value of the entry with the `VacantEntry`'s key,
427    /// and returns a mutable reference to it.
428    ///
429    /// # Examples
430    ///
431    /// ```
432    /// use unc_sdk::store::IterableMap;
433    /// use unc_sdk::store::iterable_map::Entry;
434    ///
435    /// let mut map: IterableMap<String, u32> = IterableMap::new(b"m");
436    ///
437    /// if let Entry::Vacant(o) = map.entry("poneyland".to_string()) {
438    ///     o.insert(37);
439    /// }
440    /// assert_eq!(map["poneyland"], 37);
441    /// ```
442    pub fn insert(self, value: V) -> &'a mut V
443    where
444        K: BorshDeserialize + Clone,
445    {
446        // Vacant entry so we know key doesn't exist
447        self.keys.push(self.key().to_owned());
448        let key_index = self.keys.len() - 1;
449        // Guaranteed to be the case as the entry is vacant.
450        &mut self.values.entry(self.key).or_insert(ValueAndIndex { value, key_index }).value
451    }
452}