use crate::block::Block;
use crate::cid::Cid;
use crate::codec::{Codec, Decode};
use crate::error::Result;
use crate::ipld::Ipld;
use crate::multihash::{MultihashDigest, U64};
use crate::path::DagPath;
use async_trait::async_trait;
pub trait StoreParams: std::fmt::Debug + Clone + Send + Sync + Unpin + 'static {
type Hashes: MultihashDigest<AllocSize = U64>;
type Codecs: Codec;
const MAX_BLOCK_SIZE: usize;
}
#[derive(Clone, Debug, Default)]
pub struct DefaultParams;
impl StoreParams for DefaultParams {
const MAX_BLOCK_SIZE: usize = 1_048_576;
type Codecs = crate::IpldCodec;
type Hashes = crate::multihash::Code;
}
#[async_trait]
pub trait Store: Clone + Send + Sync {
type Params: StoreParams;
type TempPin: Clone + Send + Sync;
fn create_temp_pin(&self) -> Result<Self::TempPin>;
fn temp_pin(&self, tmp: &Self::TempPin, cid: &Cid) -> Result<()>;
fn contains(&self, cid: &Cid) -> Result<bool>;
fn get(&self, cid: &Cid) -> Result<Block<Self::Params>>;
fn insert(&self, block: &Block<Self::Params>) -> Result<()>;
fn alias<T: AsRef<[u8]> + Send + Sync>(&self, alias: T, cid: Option<&Cid>) -> Result<()>;
fn resolve<T: AsRef<[u8]> + Send + Sync>(&self, alias: T) -> Result<Option<Cid>>;
fn reverse_alias(&self, cid: &Cid) -> Result<Option<Vec<Vec<u8>>>>;
async fn flush(&self) -> Result<()>;
async fn fetch(&self, cid: &Cid) -> Result<Block<Self::Params>>;
async fn sync(&self, cid: &Cid) -> Result<()>;
async fn query(&self, path: &DagPath<'_>) -> Result<Ipld>
where
Ipld: Decode<<Self::Params as StoreParams>::Codecs>,
{
let mut ipld = self.fetch(path.root()).await?.ipld()?;
for segment in path.path().iter() {
ipld = ipld.take(segment)?;
if let Ipld::Link(cid) = ipld {
ipld = self.fetch(&cid).await?.ipld()?;
}
}
Ok(ipld)
}
}
#[macro_export]
macro_rules! alias {
($name:ident) => {
concat!(module_path!(), "::", stringify!($name))
};
}
pub fn dyn_alias(alias: &'static str, id: u64) -> String {
let mut alias = alias.to_string();
alias.push_str("::");
alias.push_str(&id.to_string());
alias
}
#[cfg(test)]
mod tests {
use super::*;
use libipld_macro::ipld;
mod aliases {
pub const CHAIN_ALIAS: &str = alias!(CHAIN_ALIAS);
}
#[test]
fn test_alias() {
assert_eq!(alias!(test_alias), "libipld::store::tests::test_alias");
assert_eq!(
aliases::CHAIN_ALIAS,
"libipld::store::tests::aliases::CHAIN_ALIAS"
);
assert_eq!(
dyn_alias(aliases::CHAIN_ALIAS, 3).as_str(),
"libipld::store::tests::aliases::CHAIN_ALIAS::3"
);
}
#[async_std::test]
async fn test_query() -> Result<()> {
use crate::cbor::DagCborCodec;
use crate::mem::MemStore;
use crate::multihash::Code;
let store = MemStore::<DefaultParams>::default();
let leaf = ipld!({"name": "John Doe"});
let leaf_block = Block::encode(DagCborCodec, Code::Blake3_256, &leaf).unwrap();
let root = ipld!({ "list": [leaf_block.cid()] });
store.insert(&leaf_block).unwrap();
let root_block = Block::encode(DagCborCodec, Code::Blake3_256, &root).unwrap();
store.insert(&root_block).unwrap();
let path = DagPath::new(root_block.cid(), "list/0/name");
assert_eq!(
store.query(&path).await.unwrap(),
Ipld::String("John Doe".to_string())
);
Ok(())
}
}