use std::rc::Rc;
use crate::cbor::{Cid, Codec};
use crate::crypto::SigningKey;
use crate::mst::{BlockStore, MemBlockStore, MstError, Tree};
use crate::syntax::{Did, Nsid, RecordKey, TidClock};
use crate::repo::RepoError;
use crate::repo::commit::Commit;
struct SharedStore(Rc<MemBlockStore>);
impl BlockStore for SharedStore {
fn get_block(&self, cid: &Cid) -> Result<Vec<u8>, MstError> {
self.0.get_block(cid)
}
fn put_block(&self, cid: Cid, data: Vec<u8>) -> Result<(), MstError> {
self.0.put_block(cid, data)
}
fn has_block(&self, cid: &Cid) -> Result<bool, MstError> {
self.0.has_block(cid)
}
}
pub struct Repo {
did: Did,
clock: TidClock,
store: Rc<MemBlockStore>,
tree: Tree,
prev_commit: Option<Cid>,
}
impl Repo {
pub fn new(did: Did, clock: TidClock) -> Self {
let store = Rc::new(MemBlockStore::new());
let tree = Tree::new(Box::new(SharedStore(Rc::clone(&store))));
Repo {
did,
clock,
store,
tree,
prev_commit: None,
}
}
#[inline]
pub fn get(
&mut self,
collection: &Nsid,
rkey: &RecordKey,
) -> Result<Option<(Cid, Vec<u8>)>, RepoError> {
let key = mst_key(collection, rkey);
let cid = match self.tree.get(&key)? {
Some(c) => c,
None => return Ok(None),
};
let data = self.store.get_block(&cid)?;
Ok(Some((cid, data)))
}
#[inline]
pub fn create(
&mut self,
collection: &Nsid,
rkey: &RecordKey,
record: &[u8],
) -> Result<Cid, RepoError> {
let key = mst_key(collection, rkey);
if self.tree.get(&key)?.is_some() {
return Err(RepoError::RecordExists(key));
}
let cid = Cid::compute(Codec::Drisl, record);
self.store.put_block(cid, record.to_vec())?;
self.tree.insert(key, cid)?;
Ok(cid)
}
pub fn update(
&mut self,
collection: &Nsid,
rkey: &RecordKey,
record: &[u8],
) -> Result<Cid, RepoError> {
let key = mst_key(collection, rkey);
if self.tree.get(&key)?.is_none() {
return Err(RepoError::RecordNotFound(key));
}
let cid = Cid::compute(Codec::Drisl, record);
self.store.put_block(cid, record.to_vec())?;
self.tree.insert(key, cid)?;
Ok(cid)
}
pub fn delete(&mut self, collection: &Nsid, rkey: &RecordKey) -> Result<(), RepoError> {
let key = mst_key(collection, rkey);
self.tree.remove(&key)?;
Ok(())
}
pub fn commit(&mut self, key: &dyn SigningKey) -> Result<Commit, RepoError> {
let root_cid = self.tree.root_cid()?;
let rev = self.clock.next();
let mut commit = Commit {
did: self.did.clone(),
version: 3,
rev,
prev: self.prev_commit,
data: root_cid,
sig: None,
};
commit.sign(key)?;
let commit_data = commit.to_cbor()?;
let commit_cid = Cid::compute(Codec::Drisl, &commit_data);
self.store.put_block(commit_cid, commit_data)?;
self.prev_commit = Some(commit_cid);
Ok(commit)
}
pub fn list(&mut self, collection: &Nsid) -> Result<Vec<(RecordKey, Cid)>, RepoError> {
let col_str = collection.as_str();
let prefix_len = col_str.len() + 1; let mut results = Vec::new();
self.tree.walk(|key, cid| {
if key.len() > prefix_len
&& key.as_bytes()[col_str.len()] == b'/'
&& key.as_bytes()[..col_str.len()] == *col_str.as_bytes()
{
let rkey = RecordKey::try_from(&key[prefix_len..])
.map_err(|e| MstError::Internal(format!("invalid record key in MST: {e}")))?;
results.push((rkey, cid));
}
Ok(())
})?;
Ok(results)
}
}
#[inline]
fn mst_key(collection: &Nsid, rkey: &RecordKey) -> String {
let col = collection.as_str();
let rk = rkey.as_str();
let mut key = String::with_capacity(col.len() + 1 + rk.len());
key.push_str(col);
key.push('/');
key.push_str(rk);
key
}