use std::io::Cursor;
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use cid::{
multihash::{Code, MultihashDigest},
Cid,
};
use libipld_core::{
codec::References,
serde::{from_ipld, to_ipld},
};
use libipld_core::{
codec::{Codec, Decode, Encode},
ipld::Ipld,
};
use noosphere_common::{ConditionalSend, ConditionalSync};
use serde::{de::DeserializeOwned, Serialize};
#[cfg(doc)]
use serde::Deserialize;
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait BlockStore: Clone + ConditionalSync {
#[allow(unused_variables)]
async fn put_links<C>(&mut self, cid: &Cid, block: &[u8]) -> Result<()>
where
C: Codec + Default,
Ipld: References<C>,
{
Ok(())
}
async fn put_block(&mut self, cid: &Cid, block: &[u8]) -> Result<()>;
async fn get_block(&self, cid: &Cid) -> Result<Option<Vec<u8>>>;
async fn put<C, T>(&mut self, data: T) -> Result<Cid>
where
C: Codec + Default,
T: Encode<C> + ConditionalSend,
Ipld: References<C>,
{
let codec = C::default();
let block = codec.encode(&data)?;
let cid = Cid::new_v1(codec.into(), Code::Blake3_256.digest(&block));
self.put_block(&cid, &block).await?;
self.put_links::<C>(&cid, &block).await?;
Ok(cid)
}
async fn get<C, T>(&self, cid: &Cid) -> Result<Option<T>>
where
C: Codec + Default,
T: Decode<C>,
{
let codec = C::default();
let block = self.get_block(cid).await?;
Ok(match block {
Some(bytes) => Some(T::decode(codec, &mut Cursor::new(bytes))?),
None => None,
})
}
async fn save<C, T>(&mut self, data: T) -> Result<Cid>
where
C: Codec + Default,
T: Serialize + ConditionalSend,
Ipld: Encode<C> + References<C>,
{
self.put::<C, Ipld>(to_ipld(data)?).await
}
async fn load<C, T>(&self, cid: &Cid) -> Result<T>
where
C: Codec + Default,
T: DeserializeOwned + ConditionalSend,
u64: From<C>,
Ipld: Decode<C>,
{
let codec = u64::from(C::default());
if cid.codec() != codec {
return Err(anyhow!(
"Incorrect codec; expected {}, but CID refers to {}",
codec,
cid.codec()
));
}
Ok(match self.get::<C, Ipld>(cid).await? {
Some(ipld) => from_ipld(ipld)?,
None => return Err(anyhow!("No block found for {}", cid)),
})
}
async fn require_block(&self, cid: &Cid) -> Result<Vec<u8>> {
match self.get_block(cid).await? {
Some(block) => Ok(block),
None => Err(anyhow!("Block {cid} was required but not found")),
}
}
async fn flush(&self) -> Result<()> {
Ok(())
}
}