candystore/
typed.rs

1use anyhow::anyhow;
2use bytemuck::bytes_of;
3use std::{borrow::Borrow, marker::PhantomData, ops::Range, sync::Arc};
4
5use crate::{
6    store::{ReplaceStatus, SetStatus, TYPED_NAMESPACE},
7    CandyStore, ListCompactionParams,
8};
9
10use crate::Result;
11use databuf::{config::num::LE, DecodeOwned, Encode};
12
13pub trait CandyTypedKey: Encode + DecodeOwned {
14    /// a random number that remains consistent (unlike [std::any::TypeId]), so that `MyPair(u32, u32)`
15    /// is different from `YourPair(u32, u32)`
16    const TYPE_ID: u32;
17}
18
19macro_rules! typed_builtin {
20    ($t:ty, $v:literal) => {
21        impl CandyTypedKey for $t {
22            const TYPE_ID: u32 = $v;
23        }
24    };
25}
26
27typed_builtin!(u8, 1);
28typed_builtin!(u16, 2);
29typed_builtin!(u32, 3);
30typed_builtin!(u64, 4);
31typed_builtin!(u128, 5);
32typed_builtin!(i8, 6);
33typed_builtin!(i16, 7);
34typed_builtin!(i32, 8);
35typed_builtin!(i64, 9);
36typed_builtin!(i128, 10);
37typed_builtin!(bool, 11);
38typed_builtin!(usize, 12);
39typed_builtin!(isize, 13);
40typed_builtin!(char, 14);
41typed_builtin!(String, 15);
42typed_builtin!(Vec<u8>, 16);
43typed_builtin!(uuid::Bytes, 17);
44
45fn from_bytes<T: DecodeOwned>(bytes: &[u8]) -> Result<T> {
46    T::from_bytes::<LE>(bytes).map_err(|e| anyhow!(e))
47}
48
49/// Typed stores are wrappers around an underlying [CandyStore], that serialize keys and values (using [databuf]).
50/// These are but thin wrappers, and multiple such wrappers can exist over the same store.
51///
52/// The keys and values must support [Encode] and [DecodeOwned], with the addition that keys also provide
53/// a `TYPE_ID` const, via the [CandyTypedKey] trait.
54///
55/// Notes:
56/// * All APIs take keys and values by-ref, because they will serialize them, so taking owned values doesn't
57///   make sense
58/// * [CandyStore::iter] will skip typed items, since it's meaningless to interpret them without the wrapper
59pub struct CandyTypedStore<K, V> {
60    store: Arc<CandyStore>,
61    _phantom: PhantomData<(K, V)>,
62}
63
64impl<K, V> Clone for CandyTypedStore<K, V> {
65    fn clone(&self) -> Self {
66        Self {
67            store: self.store.clone(),
68            _phantom: Default::default(),
69        }
70    }
71}
72
73impl<K, V> CandyTypedStore<K, V>
74where
75    K: CandyTypedKey,
76    V: Encode + DecodeOwned,
77{
78    /// Constructs a typed wrapper over a CandyStore
79    pub fn new(store: Arc<CandyStore>) -> Self {
80        Self {
81            store,
82            _phantom: Default::default(),
83        }
84    }
85
86    fn make_key<Q: ?Sized + Encode>(key: &Q) -> Vec<u8>
87    where
88        K: Borrow<Q>,
89    {
90        let mut kbytes = key.to_bytes::<LE>();
91        kbytes.extend_from_slice(bytes_of(&K::TYPE_ID));
92        kbytes.extend_from_slice(TYPED_NAMESPACE);
93        kbytes
94    }
95
96    /// Same as [CandyStore::contains] but serializes the key
97    pub fn contains<Q: ?Sized + Encode>(&self, key: &Q) -> Result<bool>
98    where
99        K: Borrow<Q>,
100    {
101        Ok(self.store.get_raw(&Self::make_key(key))?.is_some())
102    }
103
104    /// Same as [CandyStore::get] but serializes the key and deserializes the value
105    pub fn get<Q: ?Sized + Encode>(&self, key: &Q) -> Result<Option<V>>
106    where
107        K: Borrow<Q>,
108    {
109        let kbytes = Self::make_key(key);
110        if let Some(vbytes) = self.store.get_raw(&kbytes)? {
111            Ok(Some(from_bytes::<V>(&vbytes)?))
112        } else {
113            Ok(None)
114        }
115    }
116
117    /// Same as [CandyStore::replace] but serializes the key and the value
118    pub fn replace<Q1: ?Sized + Encode, Q2: ?Sized + Encode>(
119        &self,
120        key: &Q1,
121        val: &Q2,
122        expected_val: Option<&Q2>,
123    ) -> Result<Option<V>>
124    where
125        K: Borrow<Q1>,
126        V: Borrow<Q2>,
127    {
128        let kbytes = Self::make_key(key);
129        let vbytes = val.to_bytes::<LE>();
130        let ebytes = expected_val.map(|ev| ev.to_bytes::<LE>()).unwrap_or(vec![]);
131        match self
132            .store
133            .replace_raw(&kbytes, &vbytes, expected_val.map(|_| &*ebytes))?
134        {
135            ReplaceStatus::DoesNotExist => Ok(None),
136            ReplaceStatus::PrevValue(v) => Ok(Some(from_bytes::<V>(&v)?)),
137            ReplaceStatus::WrongValue(_) => Ok(None),
138        }
139    }
140
141    /// Same as [CandyStore::set] but serializes the key and the value.
142    pub fn set<Q1: ?Sized + Encode, Q2: ?Sized + Encode>(
143        &self,
144        key: &Q1,
145        val: &Q2,
146    ) -> Result<Option<V>>
147    where
148        K: Borrow<Q1>,
149        V: Borrow<Q2>,
150    {
151        let kbytes = Self::make_key(key);
152        let vbytes = val.to_bytes::<LE>();
153        match self.store.set_raw(&kbytes, &vbytes)? {
154            SetStatus::CreatedNew => Ok(None),
155            SetStatus::PrevValue(v) => Ok(Some(from_bytes::<V>(&v)?)),
156        }
157    }
158
159    /// Same as [CandyStore::get_or_create] but serializes the key and the default value
160    pub fn get_or_create<Q1: ?Sized + Encode, Q2: ?Sized + Encode>(
161        &self,
162        key: &Q1,
163        default_val: &Q2,
164    ) -> Result<V>
165    where
166        K: Borrow<Q1>,
167        V: Borrow<Q2>,
168    {
169        let kbytes = Self::make_key(key);
170        Ok(from_bytes::<V>(
171            &self
172                .store
173                .get_or_create_raw(&kbytes, default_val.to_bytes::<LE>())?
174                .value(),
175        )?)
176    }
177
178    /// Same as [CandyStore::remove] but serializes the key
179    pub fn remove<Q: ?Sized + Encode>(&self, k: &Q) -> Result<Option<V>>
180    where
181        K: Borrow<Q>,
182    {
183        let kbytes = Self::make_key(k);
184        if let Some(vbytes) = self.store.remove_raw(&kbytes)? {
185            Ok(Some(from_bytes::<V>(&vbytes)?))
186        } else {
187            Ok(None)
188        }
189    }
190
191    /// Same as [CandyStore::get_big] but serializes the key and deserializes the value
192    pub fn get_big<Q: ?Sized + Encode>(&self, key: &Q) -> Result<Option<V>>
193    where
194        K: Borrow<Q>,
195    {
196        let kbytes = Self::make_key(key);
197        if let Some(vbytes) = self.store.get_big(&kbytes)? {
198            Ok(Some(from_bytes::<V>(&vbytes)?))
199        } else {
200            Ok(None)
201        }
202    }
203
204    /// Same as [CandyStore::set_big] but serializes the key and the value.
205    pub fn set_big<Q1: ?Sized + Encode, Q2: ?Sized + Encode>(
206        &self,
207        key: &Q1,
208        val: &Q2,
209    ) -> Result<bool>
210    where
211        K: Borrow<Q1>,
212        V: Borrow<Q2>,
213    {
214        let kbytes = Self::make_key(key);
215        let vbytes = val.to_bytes::<LE>();
216        self.store.set_big(&kbytes, &vbytes)
217    }
218
219    /// Same as [CandyStore::remove_big] but serializes the key
220    pub fn remove_big<Q: ?Sized + Encode>(&self, k: &Q) -> Result<bool>
221    where
222        K: Borrow<Q>,
223    {
224        let kbytes = Self::make_key(k);
225        self.store.remove_big(&kbytes)
226    }
227}
228
229/// A wrapper around [CandyStore] that exposes the list API in a typed manner. See [CandyTypedStore] for more
230/// info
231pub struct CandyTypedList<L, K, V> {
232    store: Arc<CandyStore>,
233    _phantom: PhantomData<(L, K, V)>,
234}
235
236impl<L, K, V> Clone for CandyTypedList<L, K, V> {
237    fn clone(&self) -> Self {
238        Self {
239            store: self.store.clone(),
240            _phantom: Default::default(),
241        }
242    }
243}
244
245impl<L, K, V> CandyTypedList<L, K, V>
246where
247    L: CandyTypedKey,
248    K: Encode + DecodeOwned,
249    V: Encode + DecodeOwned,
250{
251    /// Constructs a [CandyTypedList] over an existing [CandyStore]
252    pub fn new(store: Arc<CandyStore>) -> Self {
253        Self {
254            store,
255            _phantom: PhantomData,
256        }
257    }
258
259    fn make_list_key<Q: ?Sized + Encode>(list_key: &Q) -> Vec<u8>
260    where
261        L: Borrow<Q>,
262    {
263        let mut kbytes = list_key.to_bytes::<LE>();
264        kbytes.extend_from_slice(bytes_of(&L::TYPE_ID));
265        kbytes
266    }
267
268    /// Tests if the given typed `item_key` exists in this list (identified by `list_key`)
269    pub fn contains<Q1: ?Sized + Encode, Q2: ?Sized + Encode>(
270        &self,
271        list_key: &Q1,
272        item_key: &Q2,
273    ) -> Result<bool>
274    where
275        L: Borrow<Q1>,
276        K: Borrow<Q2>,
277    {
278        let list_key = Self::make_list_key(list_key);
279        let item_key = item_key.to_bytes::<LE>();
280        Ok(self
281            .store
282            .owned_get_from_list(list_key, item_key)?
283            .is_some())
284    }
285
286    /// Same as [CandyStore::get_from_list], but `list_key` and `item_key` are typed
287    pub fn get<Q1: ?Sized + Encode, Q2: ?Sized + Encode>(
288        &self,
289        list_key: &Q1,
290        item_key: &Q2,
291    ) -> Result<Option<V>>
292    where
293        L: Borrow<Q1>,
294        K: Borrow<Q2>,
295    {
296        let list_key = Self::make_list_key(list_key);
297        let item_key = item_key.to_bytes::<LE>();
298        if let Some(vbytes) = self.store.owned_get_from_list(list_key, item_key)? {
299            Ok(Some(from_bytes::<V>(&vbytes)?))
300        } else {
301            Ok(None)
302        }
303    }
304
305    fn _set<Q1: ?Sized + Encode, Q2: ?Sized + Encode, Q3: ?Sized + Encode>(
306        &self,
307        list_key: &Q1,
308        item_key: &Q2,
309        val: &Q3,
310        promote: bool,
311    ) -> Result<Option<V>>
312    where
313        L: Borrow<Q1>,
314        K: Borrow<Q2>,
315        V: Borrow<Q3>,
316    {
317        let list_key = Self::make_list_key(list_key);
318        let item_key = item_key.to_bytes::<LE>();
319        let val = val.to_bytes::<LE>();
320        match self
321            .store
322            .owned_set_in_list(list_key, item_key, val, promote)?
323        {
324            SetStatus::CreatedNew => Ok(None),
325            SetStatus::PrevValue(v) => Ok(Some(from_bytes::<V>(&v)?)),
326        }
327    }
328
329    /// Same as [CandyStore::set_in_list], but `list_key`, `item_key` and `val` are typed
330    pub fn set<Q1: ?Sized + Encode, Q2: ?Sized + Encode, Q3: ?Sized + Encode>(
331        &self,
332        list_key: &Q1,
333        item_key: &Q2,
334        val: &Q3,
335    ) -> Result<Option<V>>
336    where
337        L: Borrow<Q1>,
338        K: Borrow<Q2>,
339        V: Borrow<Q3>,
340    {
341        self._set(list_key, item_key, val, false)
342    }
343
344    /// Same as [CandyStore::set_in_list_promoting], but `list_key`, `item_key` and `val` are typed
345    pub fn set_promoting<Q1: ?Sized + Encode, Q2: ?Sized + Encode, Q3: ?Sized + Encode>(
346        &self,
347        list_key: &Q1,
348        item_key: &Q2,
349        val: &Q3,
350    ) -> Result<Option<V>>
351    where
352        L: Borrow<Q1>,
353        K: Borrow<Q2>,
354        V: Borrow<Q3>,
355    {
356        self._set(list_key, item_key, val, true)
357    }
358
359    /// Same as [CandyStore::get_or_create_in_list], but `list_key`, `item_key` and `default_val` are typed
360    pub fn get_or_create<Q1: ?Sized + Encode, Q2: ?Sized + Encode, Q3: ?Sized + Encode>(
361        &self,
362        list_key: &Q1,
363        item_key: &Q2,
364        default_val: &Q3,
365    ) -> Result<V>
366    where
367        L: Borrow<Q1>,
368        K: Borrow<Q2>,
369    {
370        let list_key = Self::make_list_key(list_key);
371        let item_key = item_key.to_bytes::<LE>();
372        let default_val = default_val.to_bytes::<LE>();
373        let vbytes = self
374            .store
375            .owned_get_or_create_in_list(list_key, item_key, default_val)?
376            .value();
377        from_bytes::<V>(&vbytes)
378    }
379
380    /// Same as [CandyStore::replace_in_list], but `list_key`, `item_key` and `val` are typed
381    pub fn replace<Q1: ?Sized + Encode, Q2: ?Sized + Encode, Q3: ?Sized + Encode>(
382        &self,
383        list_key: &Q1,
384        item_key: &Q2,
385        val: &Q3,
386        expected_val: Option<&Q3>,
387    ) -> Result<Option<V>>
388    where
389        L: Borrow<Q1>,
390        K: Borrow<Q2>,
391        V: Borrow<Q3>,
392    {
393        let list_key = Self::make_list_key(list_key);
394        let item_key = item_key.to_bytes::<LE>();
395        let val = val.to_bytes::<LE>();
396        let ebytes = expected_val
397            .map(|ev| ev.to_bytes::<LE>())
398            .unwrap_or_default();
399        match self.store.owned_replace_in_list(
400            list_key,
401            item_key,
402            val,
403            expected_val.map(|_| &*ebytes),
404        )? {
405            ReplaceStatus::DoesNotExist => Ok(None),
406            ReplaceStatus::PrevValue(v) => Ok(Some(from_bytes::<V>(&v)?)),
407            ReplaceStatus::WrongValue(_) => Ok(None),
408        }
409    }
410
411    /// Same as [CandyStore::remove_from_list], but `list_key` and `item_key`  are typed
412    pub fn remove<Q1: ?Sized + Encode, Q2: ?Sized + Encode>(
413        &self,
414        list_key: &Q1,
415        item_key: &Q2,
416    ) -> Result<Option<V>>
417    where
418        L: Borrow<Q1>,
419        K: Borrow<Q2>,
420    {
421        let list_key = Self::make_list_key(list_key);
422        let item_key = item_key.to_bytes::<LE>();
423        if let Some(vbytes) = self.store.owned_remove_from_list(list_key, item_key)? {
424            Ok(Some(from_bytes::<V>(&vbytes)?))
425        } else {
426            Ok(None)
427        }
428    }
429
430    /// Same as [CandyStore::iter_list], but `list_key` is typed
431    pub fn iter<'a, Q: ?Sized + Encode>(
432        &'a self,
433        list_key: &Q,
434    ) -> impl Iterator<Item = Result<(K, V)>> + 'a
435    where
436        L: Borrow<Q>,
437    {
438        let list_key = Self::make_list_key(list_key);
439        self.store.owned_iter_list(list_key).map(|res| match res {
440            Err(e) => Err(e),
441            Ok((k, v)) => {
442                let key = from_bytes::<K>(&k)?;
443                let val = from_bytes::<V>(&v)?;
444                Ok((key, val))
445            }
446        })
447    }
448
449    /// Same as [CandyStore::iter_list_backwards], but `list_key` is typed
450    pub fn iter_backwards<'a, Q: ?Sized + Encode>(
451        &'a self,
452        list_key: &Q,
453    ) -> impl Iterator<Item = Result<(K, V)>> + 'a
454    where
455        L: Borrow<Q>,
456    {
457        let list_key = Self::make_list_key(list_key);
458        self.store
459            .owned_iter_list_backwards(list_key)
460            .map(|res| match res {
461                Err(e) => Err(e),
462                Ok((k, v)) => {
463                    let key = from_bytes::<K>(&k)?;
464                    let val = from_bytes::<V>(&v)?;
465                    Ok((key, val))
466                }
467            })
468    }
469
470    /// Same as [CandyStore::discard_list], but `list_key` is typed
471    pub fn discard<Q: ?Sized + Encode>(&self, list_key: &Q) -> Result<bool>
472    where
473        L: Borrow<Q>,
474    {
475        let list_key = Self::make_list_key(list_key);
476        self.store.owned_discard_list(list_key)
477    }
478
479    /// Same as [CandyStore::compact_list_if_needed], but `list_key` is typed
480    pub fn compact_if_needed<Q: ?Sized + Encode>(
481        &self,
482        list_key: &Q,
483        params: ListCompactionParams,
484    ) -> Result<bool>
485    where
486        L: Borrow<Q>,
487    {
488        let list_key = Self::make_list_key(list_key);
489        self.store.compact_list_if_needed(&list_key, params)
490    }
491
492    /// Same as [CandyStore::pop_list_tail], but `list_key` is typed
493    pub fn pop_tail<Q: ?Sized + Encode>(&self, list_key: &Q) -> Result<Option<(K, V)>>
494    where
495        L: Borrow<Q>,
496    {
497        let list_key = Self::make_list_key(list_key);
498        let Some((k, v)) = self.store.owned_pop_list_tail(list_key)? else {
499            return Ok(None);
500        };
501        Ok(Some((from_bytes::<K>(&k)?, from_bytes::<V>(&v)?)))
502    }
503
504    /// Same as [CandyStore::pop_list_head], but `list_key` is typed
505    pub fn pop_head<Q: ?Sized + Encode>(&self, list_key: &Q) -> Result<Option<(K, V)>>
506    where
507        L: Borrow<Q>,
508    {
509        let list_key = Self::make_list_key(list_key);
510        let Some((k, v)) = self.store.owned_pop_list_head(list_key)? else {
511            return Ok(None);
512        };
513        Ok(Some((from_bytes::<K>(&k)?, from_bytes::<V>(&v)?)))
514    }
515
516    /// Same as [CandyStore::peek_list_tail], but `list_key` is typed
517    pub fn peek_tail<Q: ?Sized + Encode>(&self, list_key: &Q) -> Result<Option<(K, V)>>
518    where
519        L: Borrow<Q>,
520    {
521        let list_key = Self::make_list_key(list_key);
522        let Some((k, v)) = self.store.owned_peek_list_tail(list_key)? else {
523            return Ok(None);
524        };
525        Ok(Some((from_bytes::<K>(&k)?, from_bytes::<V>(&v)?)))
526    }
527
528    /// Same as [CandyStore::peek_list_head], but `list_key` is typed
529    pub fn peek_head<Q: ?Sized + Encode>(&self, list_key: &Q) -> Result<Option<(K, V)>>
530    where
531        L: Borrow<Q>,
532    {
533        let list_key = Self::make_list_key(list_key);
534        let Some((k, v)) = self.store.owned_peek_list_head(list_key)? else {
535            return Ok(None);
536        };
537        Ok(Some((from_bytes::<K>(&k)?, from_bytes::<V>(&v)?)))
538    }
539
540    /// Same as [CandyStore::list_len], but `list_key` is typed
541    pub fn len<Q: ?Sized + Encode>(&self, list_key: &Q) -> Result<usize>
542    where
543        L: Borrow<Q>,
544    {
545        self.store.owned_list_len(Self::make_list_key(list_key))
546    }
547
548    /// Same as [CandyStore::retain_in_list], but `list_key` is typed
549    pub fn retain<Q: ?Sized + Encode>(
550        &self,
551        list_key: &Q,
552        mut func: impl FnMut(&K, &V) -> Result<bool>,
553    ) -> Result<()>
554    where
555        L: Borrow<Q>,
556    {
557        let list_key = Self::make_list_key(list_key);
558        self.store.owned_retain_in_list(list_key, |k, v| {
559            let tk = from_bytes::<K>(&k)?;
560            let tv = from_bytes::<V>(&v)?;
561            func(&tk, &tv)
562        })
563    }
564}
565
566/// A wrapper around [CandyStore] that exposes the queue API in a typed manner. See [CandyTypedStore] for more
567/// info
568pub struct CandyTypedDeque<L, V> {
569    store: Arc<CandyStore>,
570    _phantom: PhantomData<(L, V)>,
571}
572
573impl<L, V> Clone for CandyTypedDeque<L, V> {
574    fn clone(&self) -> Self {
575        Self {
576            store: self.store.clone(),
577            _phantom: Default::default(),
578        }
579    }
580}
581
582impl<L, V> CandyTypedDeque<L, V>
583where
584    L: CandyTypedKey,
585    V: Encode + DecodeOwned,
586{
587    pub fn new(store: Arc<CandyStore>) -> Self {
588        Self {
589            store,
590            _phantom: Default::default(),
591        }
592    }
593
594    /// Pushes a value at the beginning (head) of the queue
595    pub fn push_head<Q1: ?Sized + Encode, Q2: ?Sized + Encode>(
596        &self,
597        queue_key: &Q1,
598        val: &Q2,
599    ) -> Result<()>
600    where
601        L: Borrow<Q1>,
602        V: Borrow<Q2>,
603    {
604        let queue_key = CandyTypedList::<L, (), ()>::make_list_key(queue_key);
605        let val = val.to_bytes::<LE>();
606        self.store.push_to_queue_head(&queue_key, &val)?;
607        Ok(())
608    }
609
610    /// Pushes a value at the end (tail) of the queue
611    pub fn push_tail<Q1: ?Sized + Encode, Q2: ?Sized + Encode>(
612        &self,
613        queue_key: &Q1,
614        val: &Q2,
615    ) -> Result<()>
616    where
617        L: Borrow<Q1>,
618        V: Borrow<Q2>,
619    {
620        let queue_key = CandyTypedList::<L, (), ()>::make_list_key(queue_key);
621        let val = val.to_bytes::<LE>();
622        self.store.push_to_queue_tail(&queue_key, &val)?;
623        Ok(())
624    }
625
626    /// Pops a value from the beginning (head) of the queue
627    pub fn pop_head_with_idx<Q: ?Sized + Encode>(&self, queue_key: &Q) -> Result<Option<(usize, V)>>
628    where
629        L: Borrow<Q>,
630    {
631        let queue_key = CandyTypedList::<L, (), ()>::make_list_key(queue_key);
632        let Some((idx, v)) = self.store.pop_queue_head_with_idx(&queue_key)? else {
633            return Ok(None);
634        };
635        Ok(Some((idx, from_bytes::<V>(&v)?)))
636    }
637
638    /// Pops a value from the beginning (head) of the queue
639    pub fn pop_head<Q: ?Sized + Encode>(&self, queue_key: &Q) -> Result<Option<V>>
640    where
641        L: Borrow<Q>,
642    {
643        Ok(self.pop_head_with_idx(queue_key)?.map(|iv| iv.1))
644    }
645
646    /// Pops a value from the end (tail) of the queue
647    pub fn pop_tail_with_idx<Q: ?Sized + Encode>(&self, queue_key: &Q) -> Result<Option<(usize, V)>>
648    where
649        L: Borrow<Q>,
650    {
651        let queue_key = CandyTypedList::<L, (), ()>::make_list_key(queue_key);
652        let Some((idx, v)) = self.store.pop_queue_tail_with_idx(&queue_key)? else {
653            return Ok(None);
654        };
655        Ok(Some((idx, from_bytes::<V>(&v)?)))
656    }
657
658    /// Pops a value from the end (tail) of the queue
659    pub fn pop_tail<Q: ?Sized + Encode>(&self, queue_key: &Q) -> Result<Option<V>>
660    where
661        L: Borrow<Q>,
662    {
663        Ok(self.pop_tail_with_idx(queue_key)?.map(|iv| iv.1))
664    }
665
666    /// Peek at the value from the beginning (head) of the queue and its index
667    pub fn peek_head_with_idx<Q: ?Sized + Encode>(
668        &self,
669        queue_key: &Q,
670    ) -> Result<Option<(usize, V)>>
671    where
672        L: Borrow<Q>,
673    {
674        let queue_key = CandyTypedList::<L, (), ()>::make_list_key(queue_key);
675        let Some((idx, v)) = self.store.peek_queue_head_with_idx(&queue_key)? else {
676            return Ok(None);
677        };
678        Ok(Some((idx, from_bytes::<V>(&v)?)))
679    }
680
681    /// Peek at the value from the beginning (head) of the queue
682    pub fn peek_head<Q: ?Sized + Encode>(&self, queue_key: &Q) -> Result<Option<V>>
683    where
684        L: Borrow<Q>,
685    {
686        Ok(self.peek_head_with_idx(queue_key)?.map(|iv| iv.1))
687    }
688
689    /// Peek at the value from the end (tail) of the queue
690    pub fn peek_tail_with_idx<Q: ?Sized + Encode>(
691        &self,
692        queue_key: &Q,
693    ) -> Result<Option<(usize, V)>>
694    where
695        L: Borrow<Q>,
696    {
697        let queue_key = CandyTypedList::<L, (), ()>::make_list_key(queue_key);
698        let Some((idx, v)) = self.store.peek_queue_tail_with_idx(&queue_key)? else {
699            return Ok(None);
700        };
701        Ok(Some((idx, from_bytes::<V>(&v)?)))
702    }
703
704    /// Peek at the value from the end (tail) of the queue
705    pub fn peek_tail<Q: ?Sized + Encode>(&self, queue_key: &Q) -> Result<Option<V>>
706    where
707        L: Borrow<Q>,
708    {
709        Ok(self.peek_tail_with_idx(queue_key)?.map(|iv| iv.1))
710    }
711
712    /// See [CandyTypedList::iter]
713    pub fn iter<'a, Q: ?Sized + Encode>(
714        &'a self,
715        queue_key: &Q,
716    ) -> impl Iterator<Item = Result<(usize, V)>> + 'a
717    where
718        L: Borrow<Q>,
719    {
720        let queue_key = CandyTypedList::<L, (), ()>::make_list_key(queue_key);
721        self.store.iter_queue(&queue_key).map(|res| match res {
722            Err(e) => Err(e),
723            Ok((idx, v)) => Ok((idx, from_bytes::<V>(&v).unwrap())),
724        })
725    }
726
727    /// See [CandyTypedList::iter_backwards]
728    pub fn iter_backwards<'a, Q: ?Sized + Encode>(
729        &'a self,
730        queue_key: &Q,
731    ) -> impl Iterator<Item = Result<(usize, V)>> + 'a
732    where
733        L: Borrow<Q>,
734    {
735        let queue_key = CandyTypedList::<L, (), ()>::make_list_key(queue_key);
736        self.store
737            .iter_queue_backwards(&queue_key)
738            .map(|res| match res {
739                Err(e) => Err(e),
740                Ok((idx, v)) => Ok((idx, from_bytes::<V>(&v).unwrap())),
741            })
742    }
743
744    pub fn len<Q: ?Sized + Encode>(&self, queue_key: &Q) -> Result<usize>
745    where
746        L: Borrow<Q>,
747    {
748        let queue_key = CandyTypedList::<L, (), ()>::make_list_key(queue_key);
749        self.store.queue_len(&queue_key)
750    }
751
752    pub fn range<Q: ?Sized + Encode>(&self, queue_key: &Q) -> Result<Range<usize>>
753    where
754        L: Borrow<Q>,
755    {
756        let queue_key = CandyTypedList::<L, (), ()>::make_list_key(queue_key);
757        self.store.queue_range(&queue_key)
758    }
759}