Skip to main content

thruput_sdk/storage/
rcq.rs

1use super::keypath::{Keypath, LeafKey};
2use crate::StorageKey;
3use crate::exn::LibraryExn;
4use crate::re_throw::OrReThrow;
5use crate::sys_exn::rcq::*;
6use crate::xdr::*;
7use core::fmt::Debug;
8use core::marker::PhantomData;
9
10/// A retro-causal queue.
11///
12/// Items of the contained type are encoded into raw queue entries before being
13/// inserted, and decoded from raw queue entries after being retrieved.
14///
15/// Methods named `*_raw` and `*_sized` are provided to manipulate raw entry
16/// contents, which may be arbitrary bytestrings.  However, `insert_raw` may
17/// create entries that the typed methods cannot successfully decode.
18///
19/// The `try_` forms report failure — including an empty queue, an `Err` for
20/// which [`is_storage_missing`](LibraryExn::is_storage_missing) is true —
21/// through their return value instead of aborting (the `try_` law; see the
22/// [`exn`](mod@crate::exn) module docs). The plain forms abort with exactly
23/// the exception their `try_` counterpart would have returned.
24///
25/// # Size cap
26///
27/// A queue entry is capped at
28/// [`kQueueEntryMaxLen`](crate::types::kQueueEntryMaxLen)
29/// bytes, enforced by the insert syscall: `insert` encodes first, so an entry
30/// that encodes cleanly but exceeds the cap aborts with the syscall's untyped
31/// `LibraryExn::Syscall` rather than a typed exception. `consume` and `peek`
32/// throw (and `try_consume` and `try_peek` return) `StorageSerialization` if
33/// an entry fails to decode as `T`.
34///
35#[derive(Debug)]
36pub struct RCQ<T> {
37    key: LeafKey,
38    phantom_entry: PhantomData<fn() -> T>,
39}
40
41impl<T> From<Keypath> for RCQ<T> {
42    fn from(keypath: Keypath) -> Self {
43        Self {
44            key: keypath.into(),
45            phantom_entry: PhantomData,
46        }
47    }
48}
49
50impl<T> RCQ<T> {
51    /// Retrieve the storage key of the RCQ item.
52    pub fn key(&self) -> &StorageKey {
53        self.key.get()
54    }
55}
56
57impl<T> RCQ<T> {
58    /// Insert a queue entry containing raw bytes.
59    pub fn insert_raw(&mut self, queue_entry: &[u8]) {
60        retrocausal_queue_insert(self.key(), queue_entry);
61    }
62
63    /// Consume a fixed-size queue entry.
64    pub fn consume_sized<const N: usize>(&mut self) -> [u8; N] {
65        retrocausal_queue_consume_sized(self.key())
66    }
67
68    /// Consume a fixed-size queue entry, reporting failure as `Err`.
69    ///
70    /// An entry of a different size is `LibraryExn::RetrocausalQueueEntryWrongSize`
71    /// and is left in the queue.
72    pub fn try_consume_sized<const N: usize>(
73        &mut self,
74    ) -> Result<[u8; N], LibraryExn> {
75        retrocausal_queue_try_consume_sized(self.key())
76    }
77
78    /// Consume a raw queue entry into a boxed slice.
79    #[cfg(feature = "alloc")]
80    pub fn consume_raw(&mut self) -> alloc::boxed::Box<[u8]> {
81        self.try_consume_raw().or_re_throw()
82    }
83
84    /// Consume a raw queue entry into a boxed slice, reporting failure as
85    /// `Err`.
86    #[cfg(feature = "alloc")]
87    pub fn try_consume_raw(
88        &mut self,
89    ) -> Result<alloc::boxed::Box<[u8]>, LibraryExn> {
90        retrocausal_queue_try_consume(self.key())
91    }
92
93    /// Retrieve a fixed-size queue entry without consuming it.
94    pub fn peek_sized<const N: usize>(&self) -> [u8; N] {
95        retrocausal_queue_peek_sized(self.key())
96    }
97
98    /// Retrieve a fixed-size queue entry without consuming it, reporting
99    /// failure as `Err`.
100    ///
101    /// An entry of a different size is `LibraryExn::RetrocausalQueueEntryWrongSize`.
102    pub fn try_peek_sized<const N: usize>(
103        &self,
104    ) -> Result<[u8; N], LibraryExn> {
105        retrocausal_queue_try_peek_sized(self.key())
106    }
107
108    /// Retrieve a raw queue entry into a boxed slice without consuming it.
109    #[cfg(feature = "alloc")]
110    pub fn peek_raw(&self) -> alloc::boxed::Box<[u8]> {
111        self.try_peek_raw().or_re_throw()
112    }
113
114    /// Retrieve a raw queue entry into a boxed slice without consuming it,
115    /// reporting failure as `Err`.
116    #[cfg(feature = "alloc")]
117    pub fn try_peek_raw(&self) -> Result<alloc::boxed::Box<[u8]>, LibraryExn> {
118        retrocausal_queue_try_peek(self.key())
119    }
120}
121
122impl<T> RCQ<T>
123where
124    T: WriteXDR,
125{
126    /// Insert a queue entry containing the encoded item.
127    pub fn insert(&mut self, entry: &T) {
128        crate::call::with_scratch(|buf| {
129            crate::internal::encode_scratch(buf, entry)
130                .unwrap_or_else(|e| super::error::throw_store_exn(e));
131            self.insert_raw(buf.as_slice());
132        });
133    }
134}
135
136impl<T> RCQ<T>
137where
138    T: ReadXDROwned,
139{
140    /// Consume a queue entry and return the decoded item.
141    pub fn consume(&mut self) -> T {
142        // A single consuming read: a decode failure throws, and the trap
143        // rolls the consume back, so the destructive read is unobservable.
144        self.try_decode_entry(false).or_re_throw()
145    }
146
147    /// Consume a queue entry and return the decoded item, reporting failure
148    /// as `Err`. An entry that fails to decode as `T` is
149    /// `LibraryExn::StorageSerialization` and is left in the queue.
150    pub fn try_consume(&mut self) -> Result<T, LibraryExn> {
151        // Decode from a peek so a bad entry is reported without being
152        // destroyed (matching try_consume_sized), then pop the validated
153        // entry without copying it out again.
154        let value = self.try_decode_entry(true)?;
155        retrocausal_queue_try_discard(self.key())?;
156        Ok(value)
157    }
158
159    /// Retrieve a queue entry and return the decoded item, without consuming the entry.
160    pub fn peek(&self) -> T {
161        self.try_peek().or_re_throw()
162    }
163
164    /// Retrieve a queue entry and return the decoded item without consuming
165    /// the entry, reporting failure as `Err`. Same error contract as
166    /// [`try_consume`](Self::try_consume).
167    pub fn try_peek(&self) -> Result<T, LibraryExn> {
168        self.try_decode_entry(true)
169    }
170
171    fn try_decode_entry(&self, peek: bool) -> Result<T, LibraryExn> {
172        crate::call::with_scratch(|buf| {
173            retrocausal_queue_try_read_into(self.key(), peek, buf)?;
174            cxs::decode(buf.as_slice())
175                .map_err(|_| LibraryExn::StorageSerialization)
176        })
177    }
178}
179
180impl<S> super::sealed::Sealed for RCQ<S> {}
181impl<S> super::node::StorageType for RCQ<S> {}
182
183impl<T> super::deletable::Deletable for RCQ<T> {
184    fn delete_internal(&mut self, exclusive: bool) {
185        let exclusive = exclusive || self.key.is_exclusive();
186        crate::sys_exn::delete::delete(self.key(), exclusive);
187    }
188}