Skip to main content

ping_core/
storage.rs

1//! Storage trait — implemented by hosts. A namespaced, async key-value interface.
2//!
3//! See `docs/ARCHITECTURE.md` for the namespace contract. Hosts are responsible for at-rest
4//! encryption of the `keystore/` namespace.
5
6use crate::Result;
7use std::fmt::Debug;
8use std::future::Future;
9use std::pin::Pin;
10
11// On native targets the future must be `Send` so it can move across tokio worker threads.
12// On wasm32 the runtime is single-threaded and `JsFuture` is `!Send`, so we can't and don't
13// need to require it.
14#[cfg(not(target_arch = "wasm32"))]
15pub type StorageFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T>> + Send + 'a>>;
16#[cfg(target_arch = "wasm32")]
17pub type StorageFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T>> + 'a>>;
18
19pub trait Storage: Send + Sync + Debug {
20    fn get<'a>(&'a self, namespace: &'a str, key: &'a str) -> StorageFuture<'a, Option<Vec<u8>>>;
21    fn put<'a>(&'a self, namespace: &'a str, key: &'a str, value: Vec<u8>)
22        -> StorageFuture<'a, ()>;
23    fn delete<'a>(&'a self, namespace: &'a str, key: &'a str) -> StorageFuture<'a, ()>;
24    fn list_keys<'a>(
25        &'a self,
26        namespace: &'a str,
27        prefix: &'a str,
28    ) -> StorageFuture<'a, Vec<String>>;
29}
30
31#[cfg(feature = "test-utils")]
32pub mod memory {
33    use super::*;
34    use parking_lot::RwLock;
35    use std::collections::BTreeMap;
36
37    #[derive(Debug, Default)]
38    pub struct MemStorage {
39        inner: RwLock<BTreeMap<(String, String), Vec<u8>>>,
40    }
41
42    impl Storage for MemStorage {
43        fn get<'a>(&'a self, ns: &'a str, k: &'a str) -> StorageFuture<'a, Option<Vec<u8>>> {
44            Box::pin(async move { Ok(self.inner.read().get(&(ns.into(), k.into())).cloned()) })
45        }
46        fn put<'a>(&'a self, ns: &'a str, k: &'a str, v: Vec<u8>) -> StorageFuture<'a, ()> {
47            Box::pin(async move {
48                self.inner.write().insert((ns.into(), k.into()), v);
49                Ok(())
50            })
51        }
52        fn delete<'a>(&'a self, ns: &'a str, k: &'a str) -> StorageFuture<'a, ()> {
53            Box::pin(async move {
54                self.inner.write().remove(&(ns.into(), k.into()));
55                Ok(())
56            })
57        }
58        fn list_keys<'a>(&'a self, ns: &'a str, prefix: &'a str) -> StorageFuture<'a, Vec<String>> {
59            Box::pin(async move {
60                let g = self.inner.read();
61                Ok(g.keys()
62                    .filter(|(n, k)| n == ns && k.starts_with(prefix))
63                    .map(|(_, k)| k.clone())
64                    .collect())
65            })
66        }
67    }
68}