fastly/kv_store.rs
1//! Interface to the [Compute KV Store][blog].
2//!
3//! [blog]: https://www.fastly.com/blog/introducing-the-compute-edge-kv-store-global-persistent-storage-for-compute-functions
4
5use crate::kv_store;
6use crate::Body;
7use serde::Deserialize;
8
9pub use self::handle::InsertMode;
10pub use self::handle::KVStoreError;
11pub use self::handle::ListMode;
12pub use self::handle::StoreHandle;
13
14pub use self::handle::PendingDeleteHandle;
15pub use self::handle::PendingInsertHandle;
16pub use self::handle::PendingListHandle;
17pub use self::handle::PendingLookupHandle;
18
19// TODO ACF 2022-10-10: this module is temporarily public for the large kv preview.
20#[doc(hidden)]
21pub mod handle;
22
23/// A response from a KV Store Lookup operation.
24///
25/// This type holds the [`Body`][`crate::Body`], metadata, and generation of found key.
26pub struct LookupResponse {
27 body: Option<Body>,
28 metadata: Option<bytes::Bytes>,
29 generation: u64,
30}
31
32impl LookupResponse {
33 /// Return the `Body` from a `LookupResponse`.
34 ///
35 /// Returns the body contents in a `Body`
36 pub fn take_body(&mut self) -> Body {
37 self.body.take().unwrap_or_else(Body::new)
38 }
39
40 /// Take and return the body from this response if it has one, otherwise return `None`.
41 ///
42 /// After calling this method, this response will no longer have a body.
43 pub fn try_take_body(&mut self) -> Option<Body> {
44 self.body.take()
45 }
46
47 /// Convert the `LookupResponse` body into a byte vector.
48 ///
49 /// Returns the body contents in a `Vec<u8>`
50 pub fn take_body_bytes(&mut self) -> Vec<u8> {
51 if let Some(body) = self.try_take_body() {
52 body.into_bytes()
53 } else {
54 Vec::new()
55 }
56 }
57
58 /// Read the metadata of the KVStore item.
59 ///
60 /// Returns the metadata in a `Option<bytes::Bytes>`
61 pub fn metadata(&self) -> Option<bytes::Bytes> {
62 self.metadata.to_owned()
63 }
64
65 /// Read the generation of the KVStore item.
66 ///
67 /// Returns the generation in a `u32`
68 #[deprecated(
69 since = "0.11.0",
70 note = "`generation` has a bug in this version of the SDK, and will always return 0"
71 )]
72 pub fn generation(&self) -> u32 {
73 0
74 }
75
76 /// Read the current generation of the KVStore item.
77 ///
78 /// Returns the current generation in a `u64`
79 pub fn current_generation(&self) -> u64 {
80 self.generation
81 }
82}
83
84#[derive(Deserialize, Debug, Clone)]
85struct ListMetadata {
86 limit: u32,
87 next_cursor: Option<String>,
88 prefix: Option<String>,
89 consistency: Option<String>,
90}
91
92/// A page from a KV Store List operation.
93#[derive(Deserialize, Debug, Clone)]
94pub struct ListPage {
95 data: Vec<String>,
96 meta: ListMetadata,
97}
98
99impl ListPage {
100 /// Returns a slice of the listed keys in the current page.
101 pub fn keys(&self) -> &[String] {
102 self.data.as_slice()
103 }
104 /// Consumes the page, and returns a `Vec<String>` of its listed keys.
105 pub fn into_keys(self) -> Vec<String> {
106 self.data
107 }
108 /// Returns an `Option<String>` of the List operation's `next_cursor`
109 pub fn next_cursor(&self) -> Option<String> {
110 self.meta.next_cursor.clone()
111 }
112 /// Returns an `Option<&str>` of the List operation's `prefix`
113 pub fn prefix(&self) -> Option<&str> {
114 self.meta.prefix.as_deref()
115 }
116 /// Returns a `u32` of the List operation's `limit`
117 pub fn limit(&self) -> u32 {
118 self.meta.limit
119 }
120 /// Returns the `ListMode` of the List operation
121 pub fn mode(&self) -> ListMode {
122 match self.meta.consistency.as_deref() {
123 // default
124 None => ListMode::Strong,
125 // api doesn't actually return strong, but returns none, as above
126 // however if this should change, we'll catch it
127 Some("strong") => ListMode::Strong,
128 Some("eventual") => ListMode::Eventual,
129 Some(other) => ListMode::Other(other.to_string()),
130 }
131 }
132}
133
134/// A response from a KV Store List operation.
135#[derive(Debug)]
136pub struct ListResponse<'a> {
137 store_handle: &'a kv_store::StoreHandle,
138 page: ListPage,
139 iterator_did_error: bool,
140}
141
142impl<'a> ListResponse<'a> {
143 /// Creates an initialized ListResponse, only used to create an iterable
144 fn new(
145 handle: &'a kv_store::StoreHandle,
146 mode: ListMode,
147 cursor: Option<String>,
148 limit: Option<u32>,
149 prefix: Option<String>,
150 ) -> Self {
151 let m_limit = limit.unwrap_or(1000);
152
153 let m_consistency = match mode {
154 ListMode::Strong => None,
155 ListMode::Eventual => Some("eventual".to_string()),
156 ListMode::Other(_) => None,
157 };
158
159 ListResponse {
160 store_handle: handle,
161 page: ListPage {
162 data: vec![],
163 meta: ListMetadata {
164 limit: m_limit,
165 next_cursor: cursor,
166 prefix,
167 consistency: m_consistency,
168 },
169 },
170 iterator_did_error: false,
171 }
172 }
173 /// Consumes the `ListResponse` and returns the current `ListPage`
174 pub fn into_page(self) -> ListPage {
175 self.page
176 }
177}
178
179impl Iterator for ListResponse<'_> {
180 type Item = Result<ListPage, KVStoreError>;
181
182 fn next(&mut self) -> Option<Self::Item> {
183 let cursor = self.page.meta.next_cursor.to_owned();
184 // close the iterator after the error
185 if self.iterator_did_error {
186 return None;
187 }
188
189 // end of pagination, unless it's a ListResponse::new()
190 if self.page.meta.next_cursor.is_none() && !self.page.data.is_empty() {
191 return None;
192 }
193
194 let mode = match self.page.meta.consistency.as_deref() {
195 None => ListMode::Strong, // default
196 Some("strong") => ListMode::Strong,
197 Some("eventual") => ListMode::Eventual,
198 Some(other) => ListMode::Other(other.to_string()),
199 };
200
201 let limit = Some(self.page.meta.limit);
202 let prefix = self.page.meta.prefix.to_owned();
203
204 let h_res = self.store_handle.list(mode, cursor, limit, prefix);
205 if let Err(e) = h_res {
206 return Some(Err(e));
207 }
208 let handle = h_res.unwrap();
209
210 let w_res = self.store_handle.pending_list_wait(handle);
211 if let Err(e) = w_res {
212 return Some(Err(e));
213 }
214
215 let out = w_res.unwrap().into_page();
216 let keys = out.keys();
217 if keys.is_empty() {
218 return None;
219 }
220
221 self.page.data = keys.to_vec();
222 self.page.meta.next_cursor = out.next_cursor();
223
224 Some(Ok(self.page.clone()))
225 }
226}
227
228/// A builder for KVStore insertions, supporting advanced configuration like [`InsertMode`], background_fetch, if_generation_match, metadata, ttl, and asynchronous execution.
229///
230/// ```no_run
231/// # use fastly::kv_store::{InsertMode, KVStore, KVStoreError};
232/// # fn f() -> Result<(), KVStoreError> {
233/// # let store = KVStore::open("my-kv-store")?.unwrap();
234/// store.build_insert()
235/// .mode(InsertMode::Prepend)
236/// .background_fetch()
237/// .if_generation_match(1337)
238/// .metadata("2000B of arbitrary data")
239/// .time_to_live(std::time::Duration::from_secs(5))
240/// .execute("key1", "value1")?;
241/// # Ok(()) }
242/// ```
243///
244/// # Synchronous and Asynchronous support
245///
246/// InsertBuilder also exposes the ability to issue an asynchronous insert. When finalizing the InsertBuilder,
247/// [`execute_async`][`Self::execute_async()`] will send the request, and unblock. There is currently no polling mechanism.
248/// To retrieve the result, use the blocking function [`pending_insert_wait`][`KVStore::pending_insert_wait()`].
249///
250/// # Examples
251///
252/// ```no_run
253/// # use fastly::kv_store::{KVStore, KVStoreError,PendingInsertHandle};
254/// # fn f() -> Result<(), KVStoreError> {
255/// # let store = KVStore::open("my-kv-store")?.unwrap();
256/// // synchronous non-builder insert
257/// let result: Result<(), KVStoreError> = store.insert("key", "value");
258/// // synchronous builder insert
259/// let result: Result<(), KVStoreError> = store.build_insert().execute("key", "value");
260/// // asynchronous builder insert
261/// let insert_handle: Result<PendingInsertHandle, KVStoreError> = store.build_insert().execute_async("key", "value");
262/// let result: Result<(), KVStoreError> = store.pending_insert_wait(insert_handle?);
263/// # Ok(()) }
264/// ```
265pub struct InsertBuilder<'a> {
266 store: &'a KVStore,
267 mode: InsertMode,
268 background_fetch: bool,
269 if_generation_match: Option<u64>,
270 metadata: String,
271 time_to_live_sec: Option<std::time::Duration>,
272}
273
274impl InsertBuilder<'_> {
275 /// Change the behavior in the case when the new key matches an existing key.
276 pub fn mode(self, mode: InsertMode) -> Self {
277 InsertBuilder { mode, ..self }
278 }
279
280 /// If set, allows fetching from the origin to occur in the background, enabling a faster response with stale content. The cache will be updated with fresh content after the request is completed.
281 pub fn background_fetch(self) -> Self {
282 InsertBuilder {
283 background_fetch: true,
284 ..self
285 }
286 }
287
288 /// Requests for keys will return a 'generation' header specific to the version of a key. The generation header is a unique, non-serial 64-bit unsigned integer that can be used for testing against a specific KV store value.
289 pub fn if_generation_match(self, gen: u64) -> Self {
290 InsertBuilder {
291 if_generation_match: Some(gen),
292 ..self
293 }
294 }
295
296 /// Sets an arbitrary data field which can contain up to 2000B of data.
297 pub fn metadata(self, data: &str) -> Self {
298 InsertBuilder {
299 metadata: data.to_string(),
300 ..self
301 }
302 }
303
304 /// Sets a time for the key to expire. Deletion will take place up to 24 hours after the ttl reaches 0.
305 pub fn time_to_live(self, ttl: std::time::Duration) -> Self {
306 InsertBuilder {
307 time_to_live_sec: Some(ttl),
308 ..self
309 }
310 }
311
312 /// Initiate and wait on an insert of a value into the KV Store.
313 ///
314 /// Returns `Ok(())` if the value is inserted, and `KVStoreError` if the insert failed.
315 pub fn execute(self, key: &str, value: impl Into<Body>) -> Result<(), KVStoreError> {
316 let handle = self.store.handle.insert(
317 key,
318 value.into().into_handle(),
319 self.mode,
320 self.background_fetch,
321 self.if_generation_match,
322 self.metadata.clone(),
323 self.time_to_live_sec,
324 )?;
325 self.store.pending_insert_wait(handle)
326 }
327
328 /// Initiate async insert of a value into the KV Store.
329 ///
330 /// Returns `Ok(Some(PendingInsertHandle))` if insert creation is successful.
331 pub fn execute_async(
332 &self,
333 key: &str,
334 value: impl Into<Body>,
335 ) -> Result<PendingInsertHandle, KVStoreError> {
336 self.store.handle.insert(
337 key,
338 value.into().into_handle(),
339 self.mode,
340 self.background_fetch,
341 self.if_generation_match,
342 self.metadata.clone(),
343 self.time_to_live_sec,
344 )
345 }
346}
347
348/// A builder for KVStore lookups, supporting advanced features like asynchronous execution.
349///
350/// ```no_run
351/// # use fastly::kv_store::{KVStore, KVStoreError};
352/// # fn f() -> Result<(), KVStoreError> {
353/// # let store = KVStore::open("my-kv-store")?.unwrap();
354/// store.build_lookup()
355/// .execute("key1")?;
356/// # Ok(()) }
357/// ```
358///
359/// # Synchronous and Asynchronous support
360///
361/// LookupBuilder exposes the ability to issue an asynchronous lookup. Using
362/// [`execute_async`][`Self::execute_async()`] will send the request, and unblock. There is currently no polling mechanism.
363/// To retrieve the result, use the blocking function [`pending_lookup_wait`][`KVStore::pending_lookup_wait()`].
364///
365/// # Examples
366///
367/// ```no_run
368/// # use fastly::kv_store::{KVStore, KVStoreError, LookupResponse, PendingLookupHandle};
369/// # fn f() -> Result<(), KVStoreError> {
370/// # let store = KVStore::open("my-kv-store")?.unwrap();
371/// // synchronous non-builder lookup
372/// let result: Result<LookupResponse, KVStoreError> = store.lookup("key");
373/// // synchronous builder lookup
374/// let result: Result<LookupResponse, KVStoreError> = store.build_lookup().execute("key");
375/// // asynchronous builder lookup
376/// let lookup_handle: Result<PendingLookupHandle, KVStoreError> = store.build_lookup().execute_async("key");
377/// let result: Result<LookupResponse, KVStoreError> = store.pending_lookup_wait(lookup_handle?);
378/// # Ok(()) }
379/// ```
380pub struct LookupBuilder<'a> {
381 store: &'a KVStore,
382}
383
384impl LookupBuilder<'_> {
385 /// Initiate and wait on an lookup of a value in the KV Store.
386 ///
387 /// Returns `Ok(LookupResponse)` if the value is retrieved without error, and `KVStoreError` if the lookup failed.
388 pub fn execute(&self, key: &str) -> Result<LookupResponse, KVStoreError> {
389 self.store
390 .handle
391 .pending_lookup_wait(self.store.handle.lookup(key.as_bytes())?)
392 }
393
394 /// Initiate async lookup of a value in the KV Store.
395 ///
396 /// Returns `Ok(Some(PendingLookupHandle))` if lookup creation is successful.
397 pub fn execute_async(&self, key: &str) -> Result<PendingLookupHandle, KVStoreError> {
398 self.store.handle.lookup(key.as_bytes())
399 }
400}
401
402/// A builder for KVStore deletes, supporting advanced features like asynchronous execution.
403///
404/// ```no_run
405/// # use fastly::kv_store::{KVStore, KVStoreError};
406/// # fn f() -> Result<(), KVStoreError> {
407/// # let store = KVStore::open("my-kv-store")?.unwrap();
408/// store.build_delete()
409/// .execute("key1")?;
410/// # Ok(()) }
411/// ```
412///
413/// # Synchronous and Asynchronous support
414///
415/// DeleteBuilder exposes the ability to issue an asynchronous delete. Using
416/// [`execute_async`][`Self::execute_async()`] will send the request, and unblock. There is currently no polling mechanism.
417/// To retrieve the result, use the blocking function [`pending_delete_wait`][`KVStore::pending_delete_wait()`].
418///
419/// # Examples
420///
421/// ```no_run
422/// # use fastly::kv_store::{KVStore, KVStoreError, PendingDeleteHandle};
423/// # fn f() -> Result<(), KVStoreError> {
424/// # let store = KVStore::open("my-kv-store")?.unwrap();
425/// // synchronous non-builder delete
426/// let result: Result<(), KVStoreError> = store.delete("key");
427/// // synchronous builder delete
428/// let result: Result<(), KVStoreError> = store.build_delete().execute("key");
429/// // asynchronous builder delete
430/// let delete_handle: Result<PendingDeleteHandle, KVStoreError> = store.build_delete().execute_async("key");
431/// let result: Result<(), KVStoreError> = store.pending_delete_wait(delete_handle?);
432/// # Ok(()) }
433/// ```
434pub struct DeleteBuilder<'a> {
435 store: &'a KVStore,
436}
437
438impl DeleteBuilder<'_> {
439 /// Initiate and wait on an delete of a value in the KV Store.
440 ///
441 /// Returns `Ok(()))` if the value is deleted without error, and `KVStoreError` if the delete failed.
442 pub fn execute(&self, key: &str) -> Result<(), KVStoreError> {
443 self.store
444 .pending_delete_wait(self.store.handle.delete(key)?)
445 }
446
447 /// Initiate async delete of a value in the KV Store.
448 ///
449 /// Returns `Ok(PendingDeleteHandle)` if delete creation is successful.
450 pub fn execute_async(&self, key: &str) -> Result<PendingDeleteHandle, KVStoreError> {
451 self.store.handle.delete(key)
452 }
453}
454
455/// A builder for KVStore key list operations, supporting advanced configuration like cursor, limit, prefix, and asynchronous execution.
456///
457/// ```no_run
458/// # use fastly::kv_store::{KVStore, KVStoreError};
459/// # fn f() -> Result<(), KVStoreError> {
460/// # let store = KVStore::open("my-kv-store")?.unwrap();
461/// store.build_list().execute()?;
462/// # Ok(()) }
463/// ```
464///
465/// # Synchronous and Asynchronous support
466///
467/// ListBuilder also exposes the ability to issue an asynchronous list. When finalizing the ListBuilder,
468/// [`execute_async`][`Self::execute_async()`] will send the request, and unblock. There is currently no polling mechanism.
469/// To retrieve the result, use the blocking function [`pending_list_wait`][`KVStore::pending_list_wait()`].
470///
471/// # Examples
472///
473/// ```no_run
474/// # use fastly::kv_store::{KVStore, KVStoreError,PendingListHandle,ListPage};
475/// # fn f() -> Result<(), KVStoreError> {
476/// # let store = KVStore::open("my-kv-store")?.unwrap();
477/// // synchronous non-builder list
478/// let result: Result<ListPage, KVStoreError> = store.list();
479/// // synchronous builder list
480/// let result: Result<ListPage, KVStoreError> = store.build_list().execute();
481/// // asynchronous builder list
482/// let list_handle: Result<PendingListHandle, KVStoreError> = store.build_list().execute_async();
483/// let result: Result<ListPage, KVStoreError> = store.pending_list_wait(list_handle?);
484/// # Ok(()) }
485/// ```
486pub struct ListBuilder<'a> {
487 store: &'a KVStore,
488 mode: ListMode,
489 cursor: Option<String>,
490 limit: Option<u32>,
491 prefix: Option<String>,
492}
493
494impl<'a> ListBuilder<'a> {
495 /// Rather than read data from the primary data source, which is slower but strongly consistent (`strong`, the default), instead read a local copy if available, which offers higher speed and may be a few seconds out of date (`eventual`).
496 pub fn eventual_consistency(self) -> Self {
497 ListBuilder {
498 mode: ListMode::Eventual,
499 ..self
500 }
501 }
502 /// Change the cursor of the request.
503 pub fn cursor(self, cursor: &str) -> Self {
504 ListBuilder {
505 cursor: Some(cursor.to_string()),
506 ..self
507 }
508 }
509
510 /// Set the maximum number of items included the response. The default limit is 100 items.
511 pub fn limit(self, limit: u32) -> Self {
512 ListBuilder {
513 limit: Some(limit),
514 ..self
515 }
516 }
517
518 /// Set the prefix match for items to include in the resultset.
519 pub fn prefix(self, prefix: &str) -> Self {
520 ListBuilder {
521 prefix: Some(prefix.to_string()),
522 ..self
523 }
524 }
525
526 /// Initiate and wait on the first page of a list of values in the KV Store.
527 ///
528 /// Returns `Ok(ListPage)` if the list is returned, and `KVStoreError` if the list failed.
529 pub fn execute(self) -> Result<ListPage, KVStoreError> {
530 let handle = self
531 .store
532 .handle
533 .list(self.mode, self.cursor, self.limit, self.prefix)?;
534 let res = self.store.pending_list_wait(handle)?;
535 Ok(res)
536 }
537
538 /// Produce a new ListResponse, which can be used as an iterator for
539 /// chunked list operations.
540 ///
541 /// Returns a new `ListResponse`, primed for iteration.
542 pub fn iter(self) -> ListResponse<'a> {
543 ListResponse::new(
544 &self.store.handle,
545 self.mode,
546 self.cursor,
547 self.limit,
548 self.prefix,
549 )
550 }
551
552 /// Initiate async list of values in the KV Store.
553 ///
554 /// Returns `Ok(Some(PendingListHandle))` if list creation is successful.
555 pub fn execute_async(&self) -> Result<PendingListHandle, KVStoreError> {
556 self.store.handle.list(
557 self.mode.clone(),
558 self.cursor.clone(),
559 self.limit,
560 self.prefix.clone(),
561 )
562 }
563}
564
565/// A [Compute KV Store][blog].
566///
567/// Keys in the KV Store must follow the following rules:
568///
569/// * Keys can contain any sequence of valid Unicode characters, of length 1-1024 bytes when
570/// UTF-8 encoded.
571/// * Keys cannot contain Carriage Return or Line Feed characters.
572/// * Keys cannot start with `.well-known/acme-challenge/`.
573/// * Keys cannot be named `.` or `..`.
574///
575/// [blog]: https://www.fastly.com/blog/introducing-the-compute-edge-kv-store-global-persistent-storage-for-compute-functions
576pub struct KVStore {
577 handle: StoreHandle,
578}
579
580impl KVStore {
581 // TODO ACF 2022-10-10: temporary method to support the large kv preview
582 #[doc(hidden)]
583 pub fn as_handle(&self) -> &StoreHandle {
584 &self.handle
585 }
586
587 /// Open the KV Store with the given name.
588 ///
589 /// If there is no store by that name, this returns `Ok(None)`.
590 pub fn open(name: &str) -> Result<Option<Self>, KVStoreError> {
591 match StoreHandle::open(name)? {
592 Some(handle) => Ok(Some(Self { handle })),
593 None => Ok(None),
594 }
595 }
596
597 /// Look up a value in the KV Store.
598 ///
599 /// Returns `Ok(LookupResponse)` if the value is retrieved without error, and `KVStoreError` if the lookup failed.
600 pub fn lookup(&self, key: &str) -> Result<LookupResponse, KVStoreError> {
601 self.handle
602 .pending_lookup_wait(self.handle.lookup(key.as_bytes())?)
603 }
604
605 /// Create a buildable lookup request for the KV Store.
606 ///
607 /// When the desired configuration has been set, execute the lookup and wait for the response.
608 pub fn build_lookup(&self) -> LookupBuilder<'_> {
609 LookupBuilder { store: self }
610 }
611
612 /// Look up a value in the KV Store.
613 ///
614 /// Returns `Ok(LookupResponse)` if the value is found, and `Err(KVStoreError)` if the lookup failed.
615 pub fn pending_lookup_wait(
616 &self,
617 pending_request_handle: PendingLookupHandle,
618 ) -> Result<LookupResponse, KVStoreError> {
619 self.handle.pending_lookup_wait(pending_request_handle)
620 }
621
622 /// Insert a value into the KV Store.
623 ///
624 /// If the store already contained a value for this key, it will be overwritten.
625 ///
626 /// The value may be provided as any type that can be converted to [`Body`], such as `&[u8]`,
627 /// `Vec<u8>`, `&str`, or `String`.
628 ///
629 /// # Value sizes
630 ///
631 /// The size of the value must be known when calling this method. In practice, that means that
632 /// if a [`Body`] value contains an external request or response, it must be encoded with
633 /// `Content-Length` rather than `Transfer-Encoding: chunked`.
634 pub fn insert(&self, key: &str, value: impl Into<Body>) -> Result<(), KVStoreError> {
635 self.pending_insert_wait(self.handle.insert(
636 key,
637 value.into().into_handle(),
638 InsertMode::Overwrite,
639 false,
640 None,
641 "",
642 None,
643 )?)
644 }
645
646 /// Create a buildable insert request for the KV Store.
647 ///
648 /// When the desired configuration has been set, execute the insert and wait for the response.
649 pub fn build_insert(&self) -> InsertBuilder<'_> {
650 InsertBuilder {
651 store: self,
652 mode: InsertMode::Overwrite,
653 background_fetch: false,
654 if_generation_match: None,
655 metadata: String::new(),
656 time_to_live_sec: None,
657 }
658 }
659
660 /// Insert a value in the KV Store.
661 ///
662 /// Returns `Ok(())` if the value is found, and `KVStoreError` if the key was not found.
663 pub fn pending_insert_wait(
664 &self,
665 pending_insert_handle: PendingInsertHandle,
666 ) -> Result<(), KVStoreError> {
667 self.handle.pending_insert_wait(pending_insert_handle)?;
668 Ok(())
669 }
670
671 /// Delete a value in the KV Store.
672 ///
673 /// Returns `Ok(())` if delete is successful.
674 pub fn delete(&self, key: &str) -> Result<(), KVStoreError> {
675 self.pending_delete_wait(self.handle.delete(key)?)
676 }
677
678 /// Create a buildable delete request for the KV Store.
679 ///
680 /// When the desired configuration has been set, execute the delete and wait for the response.
681 pub fn build_delete(&self) -> DeleteBuilder<'_> {
682 DeleteBuilder { store: self }
683 }
684
685 /// Delete a value in the KV Store.
686 ///
687 /// Returns `Ok(())` if the key is deleted, and `KVStoreError` if the key was not found.
688 pub fn pending_delete_wait(
689 &self,
690 pending_delete_handle: PendingDeleteHandle,
691 ) -> Result<(), KVStoreError> {
692 self.handle.pending_delete_wait(pending_delete_handle)?;
693 Ok(())
694 }
695
696 /// List keys in the KV Store.
697 ///
698 /// Returns an `Ok(ListPage)` on success, and `Err(KVStoreError)` if there was a failure. The
699 /// limit of items per page defaults to 100 items, but can be configured through [`ListBuilder`].
700 pub fn list(&self) -> Result<ListPage, KVStoreError> {
701 let lh = self.handle.list(ListMode::Strong, None, None, None);
702
703 self.pending_list_wait(lh?)
704 }
705
706 /// Create a buildable list request for the KV Store.
707 ///
708 /// When the desired configuration has been set, execute the list and wait for the response.
709 pub fn build_list(&self) -> ListBuilder<'_> {
710 ListBuilder {
711 store: self,
712 mode: ListMode::Strong,
713 cursor: None,
714 limit: None,
715 prefix: None,
716 }
717 }
718
719 /// Wait on a pending list operation.
720 ///
721 /// Returns `Ok(ListPage)` on success, and `Err(KVStoreError)` if there was a failure.
722 pub fn pending_list_wait(
723 &self,
724 pending_request_handle: PendingListHandle,
725 ) -> Result<ListPage, KVStoreError> {
726 let res = self.handle.pending_list_wait(pending_request_handle);
727 Ok(res?.into_page())
728 }
729}