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}