noosphere_storage/
store.rs

1use std::io::Cursor;
2
3use crate::{block::BlockStore, key_value::KeyValueStore};
4use anyhow::Result;
5use async_trait::async_trait;
6use cid::Cid;
7use libipld_cbor::DagCborCodec;
8use libipld_core::{
9    codec::{Codec, Decode},
10    ipld::Ipld,
11    serde::{from_ipld, to_ipld},
12};
13use noosphere_common::{ConditionalSend, ConditionalSync};
14use serde::{de::DeserializeOwned, Serialize};
15
16/// A primitive interface for storage backends. A storage backend does not
17/// necessarily need to implement this trait to be used in Noosphere, but if it
18/// does it automatically benefits from trait implementations for [BlockStore]
19/// and [KeyValueStore], making a single [Store] implementation into a universal
20/// backend.
21#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
22#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
23pub trait Store: Clone + ConditionalSync {
24    /// Read the bytes stored against a given key
25    async fn read(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;
26
27    /// Writes bytes to local storage against a given key, and returns the previous
28    /// value stored against that key if any
29    async fn write(&mut self, key: &[u8], bytes: &[u8]) -> Result<Option<Vec<u8>>>;
30
31    /// Remove a value given a key, returning the removed value if any
32    async fn remove(&mut self, key: &[u8]) -> Result<Option<Vec<u8>>>;
33
34    /// Flushes pending writes if there are any
35    async fn flush(&self) -> Result<()> {
36        Ok(())
37    }
38}
39
40#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
41#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
42impl<S> BlockStore for S
43where
44    S: Store,
45{
46    async fn put_block(&mut self, cid: &Cid, block: &[u8]) -> Result<()> {
47        self.write(&cid.to_bytes(), block).await?;
48        Ok(())
49    }
50
51    async fn get_block(&self, cid: &Cid) -> Result<Option<Vec<u8>>> {
52        self.read(&cid.to_bytes()).await
53    }
54
55    async fn flush(&self) -> Result<()> {
56        Store::flush(self).await
57    }
58}
59
60#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
61#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
62impl<S> KeyValueStore for S
63where
64    S: Store,
65{
66    async fn set_key<K, V>(&mut self, key: K, value: V) -> Result<()>
67    where
68        K: AsRef<[u8]> + ConditionalSend,
69        V: Serialize + ConditionalSend,
70    {
71        let ipld = to_ipld(value)?;
72        let codec = DagCborCodec;
73        let cbor = codec.encode(&ipld)?;
74        let key_bytes = K::as_ref(&key);
75        self.write(key_bytes, &cbor).await?;
76        Ok(())
77    }
78
79    async fn unset_key<K>(&mut self, key: K) -> Result<()>
80    where
81        K: AsRef<[u8]> + ConditionalSend,
82    {
83        let key_bytes = K::as_ref(&key);
84        self.remove(key_bytes).await?;
85        Ok(())
86    }
87
88    async fn get_key<K, V>(&self, key: K) -> Result<Option<V>>
89    where
90        K: AsRef<[u8]> + ConditionalSend,
91        V: DeserializeOwned + ConditionalSend,
92    {
93        let key_bytes = K::as_ref(&key);
94        Ok(match self.read(key_bytes).await? {
95            Some(bytes) => Some(from_ipld(Ipld::decode(
96                DagCborCodec,
97                &mut Cursor::new(bytes),
98            )?)?),
99            None => None,
100        })
101    }
102
103    async fn flush(&self) -> Result<()> {
104        Store::flush(self).await
105    }
106}