bonsaidb_core/keyvalue/implementation/
get.rs1use futures::{Future, FutureExt};
2use serde::Deserialize;
3
4use super::{BuilderState, Command, KeyOperation, KeyValue, Output};
5use crate::keyvalue::{AsyncKeyValue, Value};
6use crate::Error;
7
8#[must_use = "the key-value operation is not performed until query() is called"]
10pub struct Builder<'a, KeyValue> {
11 kv: &'a KeyValue,
12 namespace: Option<String>,
13 key: String,
14 delete: bool,
15}
16impl<'a, K> Builder<'a, K>
17where
18 K: KeyValue,
19{
20 pub(crate) const fn new(kv: &'a K, namespace: Option<String>, key: String) -> Self {
21 Self {
22 key,
23 kv,
24 namespace,
25 delete: false,
26 }
27 }
28
29 pub const fn and_delete(mut self) -> Self {
31 self.delete = true;
32 self
33 }
34
35 pub fn into<V: for<'de> Deserialize<'de>>(self) -> Result<Option<V>, Error> {
38 self.query()?.map(|value| value.deserialize()).transpose()
39 }
40
41 #[allow(clippy::cast_sign_loss)]
45 pub fn into_u64(self) -> Result<Option<u64>, Error> {
46 match self.query()? {
47 Some(value) => value.as_u64().map_or_else(
48 || {
49 Err(Error::other(
50 "key-value",
51 "value not an u64 or would lose precision when converted to an u64",
52 ))
53 },
54 |value| Ok(Some(value)),
55 ),
56 None => Ok(None),
57 }
58 }
59
60 #[allow(clippy::cast_possible_wrap)]
64 pub fn into_i64(self) -> Result<Option<i64>, Error> {
65 match self.query()? {
66 Some(value) => value.as_i64().map_or_else(
67 || {
68 Err(Error::other(
69 "key-value",
70 "value not an i64 or would lose precision when converted to an i64",
71 ))
72 },
73 |value| Ok(Some(value)),
74 ),
75 None => Ok(None),
76 }
77 }
78
79 #[allow(clippy::cast_precision_loss)]
83 pub fn into_f64(self) -> Result<Option<f64>, Error> {
84 match self.query()? {
85 Some(value) => value.as_f64().map_or_else(
86 || {
87 Err(Error::other(
88 "key-value",
89 "value not an f64 or would lose precision when converted to an f64",
90 ))
91 },
92 |value| Ok(Some(value)),
93 ),
94 None => Ok(None),
95 }
96 }
97
98 #[allow(clippy::cast_sign_loss)]
102 pub fn into_u64_lossy(self, saturating: bool) -> Result<Option<u64>, Error> {
103 match self.query()? {
104 Some(value) => value.as_u64_lossy(saturating).map_or_else(
105 || Err(Error::other("key-value", "value not numeric")),
106 |value| Ok(Some(value)),
107 ),
108 None => Ok(None),
109 }
110 }
111
112 #[allow(clippy::cast_possible_wrap)]
116 pub fn into_i64_lossy(self, saturating: bool) -> Result<Option<i64>, Error> {
117 match self.query()? {
118 Some(value) => value.as_i64_lossy(saturating).map_or_else(
119 || Err(Error::other("key-value", "value not numeric")),
120 |value| Ok(Some(value)),
121 ),
122 None => Ok(None),
123 }
124 }
125
126 #[allow(clippy::cast_precision_loss)]
129 pub fn into_f64_lossy(self) -> Result<Option<f64>, Error> {
130 match self.query()? {
131 Some(value) => value.as_f64_lossy().map_or_else(
132 || Err(Error::other("key-value", "value not numeric")),
133 |value| Ok(Some(value)),
134 ),
135 None => Ok(None),
136 }
137 }
138
139 pub fn query(self) -> Result<Option<Value>, Error> {
141 let Self {
142 kv,
143 namespace,
144 key,
145 delete,
146 } = self;
147 let result = kv.execute_key_operation(KeyOperation {
148 namespace,
149 key,
150 command: Command::Get { delete },
151 })?;
152 if let Output::Value(value) = result {
153 Ok(value)
154 } else {
155 unreachable!("Unexpected result from get")
156 }
157 }
158}
159
160#[must_use = "futures do nothing unless you `.await` or poll them"]
163pub struct AsyncBuilder<'a, KeyValue> {
164 state: BuilderState<'a, Options<'a, KeyValue>, Result<Option<Value>, Error>>,
165}
166
167struct Options<'a, KeyValue> {
168 kv: &'a KeyValue,
169 namespace: Option<String>,
170 key: String,
171 delete: bool,
172}
173
174impl<'a, K> AsyncBuilder<'a, K>
175where
176 K: AsyncKeyValue,
177{
178 pub(crate) const fn new(kv: &'a K, namespace: Option<String>, key: String) -> Self {
179 Self {
180 state: BuilderState::Pending(Some(Options {
181 key,
182 kv,
183 namespace,
184 delete: false,
185 })),
186 }
187 }
188
189 fn options(&mut self) -> &mut Options<'a, K> {
190 if let BuilderState::Pending(Some(options)) = &mut self.state {
191 options
192 } else {
193 unreachable!("Attempted to use after retrieving the result")
194 }
195 }
196
197 pub fn and_delete(mut self) -> Self {
199 self.options().delete = true;
200 self
201 }
202
203 pub async fn into<V: for<'de> Deserialize<'de>>(self) -> Result<Option<V>, Error> {
206 self.await?.map(|value| value.deserialize()).transpose()
207 }
208
209 #[allow(clippy::cast_sign_loss)]
213 pub async fn into_u64(self) -> Result<Option<u64>, Error> {
214 match self.await? {
215 Some(value) => value.as_u64().map_or_else(
216 || {
217 Err(Error::other(
218 "key-value",
219 "value not an u64 or would lose precision when converted to an u64",
220 ))
221 },
222 |value| Ok(Some(value)),
223 ),
224 None => Ok(None),
225 }
226 }
227
228 #[allow(clippy::cast_possible_wrap)]
232 pub async fn into_i64(self) -> Result<Option<i64>, Error> {
233 match self.await? {
234 Some(value) => value.as_i64().map_or_else(
235 || {
236 Err(Error::other(
237 "key-value",
238 "value not an i64 or would lose precision when converted to an i64",
239 ))
240 },
241 |value| Ok(Some(value)),
242 ),
243 None => Ok(None),
244 }
245 }
246
247 #[allow(clippy::cast_precision_loss)]
251 pub async fn into_f64(self) -> Result<Option<f64>, Error> {
252 match self.await? {
253 Some(value) => value.as_f64().map_or_else(
254 || {
255 Err(Error::other(
256 "key-value",
257 "value not an f64 or would lose precision when converted to an f64",
258 ))
259 },
260 |value| Ok(Some(value)),
261 ),
262 None => Ok(None),
263 }
264 }
265
266 #[allow(clippy::cast_sign_loss)]
270 pub async fn into_u64_lossy(self, saturating: bool) -> Result<Option<u64>, Error> {
271 match self.await? {
272 Some(value) => value.as_u64_lossy(saturating).map_or_else(
273 || Err(Error::other("key-value", "value not numeric")),
274 |value| Ok(Some(value)),
275 ),
276 None => Ok(None),
277 }
278 }
279
280 #[allow(clippy::cast_possible_wrap)]
284 pub async fn into_i64_lossy(self, saturating: bool) -> Result<Option<i64>, Error> {
285 match self.await? {
286 Some(value) => value.as_i64_lossy(saturating).map_or_else(
287 || Err(Error::other("key-value", "value not numeric")),
288 |value| Ok(Some(value)),
289 ),
290 None => Ok(None),
291 }
292 }
293
294 #[allow(clippy::cast_precision_loss)]
297 pub async fn into_f64_lossy(self) -> Result<Option<f64>, Error> {
298 match self.await? {
299 Some(value) => value.as_f64_lossy().map_or_else(
300 || Err(Error::other("key-value", "value not numeric")),
301 |value| Ok(Some(value)),
302 ),
303 None => Ok(None),
304 }
305 }
306}
307
308impl<'a, K> Future for AsyncBuilder<'a, K>
309where
310 K: AsyncKeyValue,
311{
312 type Output = Result<Option<Value>, Error>;
313
314 fn poll(
315 mut self: std::pin::Pin<&mut Self>,
316 cx: &mut std::task::Context<'_>,
317 ) -> std::task::Poll<Self::Output> {
318 match &mut self.state {
319 BuilderState::Executing(future) => future.as_mut().poll(cx),
320 BuilderState::Pending(builder) => {
321 let Options {
322 kv,
323 namespace,
324 key,
325 delete,
326 } = builder.take().expect("expected builder to have options");
327 let future = async move {
328 let result = kv
329 .execute_key_operation(KeyOperation {
330 namespace,
331 key,
332 command: Command::Get { delete },
333 })
334 .await?;
335 if let Output::Value(value) = result {
336 Ok(value)
337 } else {
338 unreachable!("Unexpected result from get")
339 }
340 }
341 .boxed();
342
343 self.state = BuilderState::Executing(future);
344 self.poll(cx)
345 }
346 }
347 }
348}