Skip to main content

crux_kv/
lib.rs

1//! A basic Key-Value store for use with Crux
2//!
3//! `crux_kv` allows Crux apps to store and retrieve arbitrary data by asking the Shell to
4//! persist the data using platform native capabilities (e.g. disk or web localStorage)
5
6pub mod command;
7pub mod error;
8pub mod protocol;
9
10use std::{future::Future, marker::PhantomData};
11
12use crux_core::{Command, Request, command::RequestBuilder};
13
14pub use error::*;
15pub use protocol::*;
16
17pub struct KeyValue<Effect, Event> {
18    // Allow the impl to declare trait bounds once. Thanks rustc
19    effect: PhantomData<Effect>,
20    event: PhantomData<Event>,
21}
22
23impl<Effect, Event> KeyValue<Effect, Event>
24where
25    Effect: Send + From<Request<KeyValueOperation>> + 'static,
26    Event: Send + 'static,
27{
28    /// Read a value under `key`
29    pub fn get(
30        key: impl Into<String>,
31    ) -> RequestBuilder<Effect, Event, impl Future<Output = DataResult>> {
32        Command::request_from_shell(KeyValueOperation::Get { key: key.into() })
33            .map(KeyValueResult::unwrap_get)
34    }
35
36    /// Set `key` to be the provided `value`. Typically the bytes would be
37    /// a value serialized/deserialized by the app.
38    pub fn set(
39        key: impl Into<String>,
40        value: Vec<u8>,
41    ) -> RequestBuilder<Effect, Event, impl Future<Output = DataResult>> {
42        Command::request_from_shell(KeyValueOperation::Set {
43            key: key.into(),
44            value,
45        })
46        .map(KeyValueResult::unwrap_set)
47    }
48
49    /// Remove a `key` and its value, return previous value if it existed
50    pub fn delete(
51        key: impl Into<String>,
52    ) -> RequestBuilder<Effect, Event, impl Future<Output = DataResult>> {
53        Command::request_from_shell(KeyValueOperation::Delete { key: key.into() })
54            .map(KeyValueResult::unwrap_delete)
55    }
56
57    /// Check to see if a `key` exists
58    pub fn exists(
59        key: impl Into<String>,
60    ) -> RequestBuilder<Effect, Event, impl Future<Output = StatusResult>> {
61        Command::request_from_shell(KeyValueOperation::Exists { key: key.into() })
62            .map(KeyValueResult::unwrap_exists)
63    }
64
65    /// List keys that start with the provided `prefix`, starting from the provided `cursor`.
66    ///
67    /// A cursor is an opaque value that points to the first key in the next page of keys.
68    ///
69    /// If the cursor is not found for the specified prefix, the response will include
70    /// a `KeyValueError::CursorNotFound` error.
71    ///
72    /// If the cursor is found the result will be a tuple of the keys and the next cursor
73    /// (if there are more keys to list, the cursor will be non-zero, otherwise it will be zero)
74    pub fn list_keys(
75        prefix: impl Into<String>,
76        cursor: u64,
77    ) -> RequestBuilder<Effect, Event, impl Future<Output = ListResult>> {
78        Command::request_from_shell(KeyValueOperation::ListKeys {
79            prefix: prefix.into(),
80            cursor,
81        })
82        .map(KeyValueResult::unwrap_list_keys)
83    }
84}
85
86#[cfg(test)]
87mod tests;