Skip to main content

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}