use crate::{
BlockStore, CODEC_DAG_CBOR,
utils::{Arc, CondSend, CondSync},
};
use anyhow::{Result, bail};
use async_once_cell::OnceCell;
use bytes::Bytes;
use cid::Cid;
use futures::Future;
use serde::{Serialize, de::DeserializeOwned};
#[macro_export]
macro_rules! impl_storable_from_serde {
( $( $ty:ty $( : < $( $generics:ident ),+ > )? ),+ ) => {
$(
impl $( < $( $generics ),+ > )? $crate::Storable for $ty $( where $( $generics: ::serde::Serialize + ::serde::de::DeserializeOwned + Clone + $crate::utils::CondSync ),+ )?{
type Serializable = $ty;
async fn to_serializable(&self, _store: &impl $crate::BlockStore) -> ::anyhow::Result<Self::Serializable> {
Ok(self.clone())
}
async fn from_serializable(_cid: Option<&$crate::Cid>, serializable: Self::Serializable) -> ::anyhow::Result<Self> {
Ok(serializable)
}
}
)+
};
}
pub trait Storable: Sized {
type Serializable: StoreIpld + LoadIpld + CondSync;
fn to_serializable(
&self,
store: &impl BlockStore,
) -> impl Future<Output = Result<Self::Serializable>> + CondSend;
fn from_serializable(
cid: Option<&Cid>,
serializable: Self::Serializable,
) -> impl Future<Output = Result<Self>> + CondSend;
fn persisted_as(&self) -> Option<&OnceCell<Cid>> {
None
}
fn store(&self, store: &impl BlockStore) -> impl Future<Output = Result<Cid>> + CondSend
where
Self: CondSync,
{
let store_future = async {
let (bytes, codec) = self.to_serializable(store).await?.encode_ipld()?;
Ok(store.put_block(bytes, codec).await?)
};
async {
if let Some(persisted_as) = self.persisted_as() {
persisted_as.get_or_try_init(store_future).await.cloned()
} else {
store_future.await
}
}
}
fn load(cid: &Cid, store: &impl BlockStore) -> impl Future<Output = Result<Self>> + CondSend {
async {
let bytes = store.get_block(cid).await?;
let serializable = Self::Serializable::decode_ipld(cid, bytes)?;
Self::from_serializable(Some(cid), serializable).await
}
}
}
pub trait StoreIpld {
fn encode_ipld(&self) -> Result<(Bytes, u64)>;
}
pub trait LoadIpld: Sized {
fn decode_ipld(cid: &Cid, bytes: Bytes) -> Result<Self>;
}
impl<T: Serialize> StoreIpld for T {
fn encode_ipld(&self) -> Result<(Bytes, u64)> {
let bytes = serde_ipld_dagcbor::to_vec(self)?;
Ok((bytes.into(), CODEC_DAG_CBOR))
}
}
impl<T: DeserializeOwned + Sized> LoadIpld for T {
fn decode_ipld(cid: &Cid, bytes: Bytes) -> Result<Self> {
let codec = cid.codec();
let dag_cbor: u64 = CODEC_DAG_CBOR;
if codec != dag_cbor {
bail!("Expected dag-cbor codec, but got {codec:X} in CID {cid}");
}
Ok(serde_ipld_dagcbor::from_slice(bytes.as_ref())?)
}
}
impl<T: Storable + CondSync> Storable for Arc<T> {
type Serializable = T::Serializable;
async fn to_serializable(&self, store: &impl BlockStore) -> Result<Self::Serializable> {
self.as_ref().to_serializable(store).await
}
async fn from_serializable(
cid: Option<&Cid>,
serializable: Self::Serializable,
) -> Result<Self> {
Ok(Arc::new(T::from_serializable(cid, serializable).await?))
}
fn persisted_as(&self) -> Option<&OnceCell<Cid>> {
self.as_ref().persisted_as()
}
}
impl_storable_from_serde! { [u8; 0], [u8; 1], [u8; 2], [u8; 4], [u8; 8], [u8; 16], [u8; 32] }
impl_storable_from_serde! { usize, u128, u64, u32, u16, u8, isize, i128, i64, i32, i16, i8 }
impl_storable_from_serde! { String }
impl_storable_from_serde! {
(A,): <A>,
(A, B): <A, B>,
(A, B, C): <A, B, C>,
(A, B, C, D): <A, B, C, D>,
(A, B, C, D, E): <A, B, C, D, E>,
(A, B, C, D, E, F): <A, B, C, D, E, F>,
(A, B, C, D, E, F, G): <A, B, C, D, E, F, G>,
(A, B, C, D, E, F, G, H): <A, B, C, D, E, F, G, H>,
(A, B, C, D, E, F, G, H, I): <A, B, C, D, E, F, G, H, I>,
(A, B, C, D, E, F, G, H, I, J): <A, B, C, D, E, F, G, H, I, J>,
(A, B, C, D, E, F, G, H, I, J, K): <A, B, C, D, E, F, G, H, I, J, K>
}