1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use std::io::Cursor;

use anyhow::Result;
use async_trait::async_trait;
use cid::Cid;
use libipld_cbor::DagCborCodec;
use libipld_core::{
    codec::{Codec, Decode},
    ipld::Ipld,
    serde::{from_ipld, to_ipld},
};
use serde::{de::DeserializeOwned, Serialize};

use crate::{
    block::BlockStore,
    key_value::{KeyValueStore, KeyValueStoreSend},
};

#[cfg(not(target_arch = "wasm32"))]
pub trait StoreConditionalSendSync: Send + Sync {}

#[cfg(not(target_arch = "wasm32"))]
impl<S> StoreConditionalSendSync for S where S: Send + Sync {}

#[cfg(target_arch = "wasm32")]
pub trait StoreConditionalSendSync {}

#[cfg(target_arch = "wasm32")]
impl<S> StoreConditionalSendSync for S {}

/// A primitive interface for storage backends. A storage backend does not
/// necessarily need to implement this trait to be used in Noosphere, but if it
/// does it automatically benefits from trait implementations for [BlockStore]
/// and [KeyValueStore], making a single [Store] implementation into a universal
/// backend.
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait Store: Clone + StoreConditionalSendSync {
    /// Read the bytes stored against a given key
    async fn read(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;

    /// Writes bytes to local storage against a given key, and returns the previous
    /// value stored against that key if any
    async fn write(&mut self, key: &[u8], bytes: &[u8]) -> Result<Option<Vec<u8>>>;

    /// Remove a value given a key, returning the removed value if any
    async fn remove(&mut self, key: &[u8]) -> Result<Option<Vec<u8>>>;

    /// Flushes pending writes if there are any
    async fn flush(&self) -> Result<()> {
        Ok(())
    }
}

#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<S> BlockStore for S
where
    S: Store,
{
    async fn put_block(&mut self, cid: &Cid, block: &[u8]) -> Result<()> {
        self.write(&cid.to_bytes(), block).await?;
        Ok(())
    }

    async fn get_block(&self, cid: &Cid) -> Result<Option<Vec<u8>>> {
        self.read(&cid.to_bytes()).await
    }
}

#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<S> KeyValueStore for S
where
    S: Store,
{
    async fn set_key<K, V>(&mut self, key: K, value: V) -> Result<()>
    where
        K: AsRef<[u8]> + KeyValueStoreSend,
        V: Serialize + KeyValueStoreSend,
    {
        let ipld = to_ipld(value)?;
        let codec = DagCborCodec;
        let cbor = codec.encode(&ipld)?;
        let key_bytes = K::as_ref(&key);
        self.write(key_bytes, &cbor).await?;
        Ok(())
    }

    async fn unset_key<K>(&mut self, key: K) -> Result<()>
    where
        K: AsRef<[u8]> + KeyValueStoreSend,
    {
        let key_bytes = K::as_ref(&key);
        self.remove(key_bytes).await?;
        Ok(())
    }

    async fn get_key<K, V>(&self, key: K) -> Result<Option<V>>
    where
        K: AsRef<[u8]> + KeyValueStoreSend,
        V: DeserializeOwned + KeyValueStoreSend,
    {
        let key_bytes = K::as_ref(&key);
        Ok(match self.read(key_bytes).await? {
            Some(bytes) => Some(from_ipld(Ipld::decode(
                DagCborCodec,
                &mut Cursor::new(bytes),
            )?)?),
            None => None,
        })
    }
}