summaryrefslogtreecommitdiffstats
path: root/adenosine-pds/src/repo.rs
diff options
context:
space:
mode:
Diffstat (limited to 'adenosine-pds/src/repo.rs')
-rw-r--r--adenosine-pds/src/repo.rs156
1 files changed, 156 insertions, 0 deletions
diff --git a/adenosine-pds/src/repo.rs b/adenosine-pds/src/repo.rs
new file mode 100644
index 0000000..e5de504
--- /dev/null
+++ b/adenosine-pds/src/repo.rs
@@ -0,0 +1,156 @@
+use crate::mst::{collect_mst_keys, CommitNode, MetadataNode, RootNode};
+use anyhow::{anyhow, Result};
+use ipfs_sqlite_block_store::BlockStore;
+use libipld::cbor::DagCborCodec;
+use libipld::multihash::Code;
+use libipld::prelude::Codec;
+use libipld::store::DefaultParams;
+use libipld::{Block, Cid, DagCbor, Ipld};
+use std::borrow::Cow;
+use std::collections::BTreeMap;
+use std::path::PathBuf;
+use std::str::FromStr;
+
+pub struct RepoCommit {
+ pub sig: Box<[u8]>,
+ pub did: String,
+ pub prev: Option<String>,
+ pub mst_cid: String,
+}
+
+pub struct RepoStore {
+ db: BlockStore<libipld::DefaultParams>,
+}
+
+impl RepoStore {
+ pub fn open(db_path: &PathBuf) -> Result<Self> {
+ Ok(RepoStore {
+ db: BlockStore::open(db_path, Default::default())?,
+ })
+ }
+
+ pub fn open_ephemeral() -> Result<Self> {
+ Ok(RepoStore {
+ db: BlockStore::open_path(ipfs_sqlite_block_store::DbPath::Memory, Default::default())?,
+ })
+ }
+
+ pub fn get_ipld(&mut self, cid: &str) -> Result<Ipld> {
+ let ipld_cid = Cid::from_str(cid)?;
+ if let Some(b) = self.db.get_block(&ipld_cid)? {
+ let block: Block<DefaultParams> = Block::new(ipld_cid, b)?;
+ block.ipld()
+ } else {
+ Err(anyhow!("missing IPLD CID: {}", cid))
+ }
+ }
+
+ pub fn get_blob(&mut self, cid: &str) -> Result<Option<Vec<u8>>> {
+ let cid = Cid::from_str(cid)?;
+ Ok(self.db.get_block(&cid)?)
+ }
+
+ /// Returns CID that was inserted
+ pub fn put_ipld(&mut self, record: &Ipld) -> Result<String> {
+ let block = Block::<DefaultParams>::encode(DagCborCodec, Code::Sha2_256, record)?;
+ let cid = block.cid().clone();
+ self.db.put_block(block, None)?;
+ Ok(cid.to_string())
+ }
+
+ /// Returns CID that was inserted
+ pub fn put_blob(&mut self, data: Vec<u8>) -> Result<String> {
+ let block = Block::<DefaultParams>::encode(libipld::raw::RawCodec, Code::Sha2_256, &data)?;
+ let cid = block.cid().clone();
+ self.db.put_block(block, None)?;
+ Ok(cid.to_string())
+ }
+
+ /// Quick alias lookup
+ pub fn get_root(&mut self, did: &str) -> Result<Option<String>> {
+ Ok(self
+ .db
+ .resolve(Cow::from(did.as_bytes()))?
+ .map(|cid| cid.to_string()))
+ }
+
+ pub fn get_commit(&mut self, commit_cid: &str) -> Result<RepoCommit> {
+ // read records by CID: commit, root, meta
+ let commit_node: CommitNode = DagCborCodec.decode(
+ &self
+ .db
+ .get_block(&Cid::from_str(commit_cid)?)?
+ .ok_or(anyhow!("expected commit block in store"))?,
+ )?;
+ let root_node: RootNode = DagCborCodec.decode(
+ &self
+ .db
+ .get_block(&commit_node.root)?
+ .ok_or(anyhow!("expected root block in store"))?,
+ )?;
+ let metadata_node: MetadataNode = DagCborCodec.decode(
+ &self
+ .db
+ .get_block(&root_node.meta)?
+ .ok_or(anyhow!("expected metadata block in store"))?,
+ )?;
+ assert_eq!(metadata_node.datastore, "mst");
+ assert_eq!(metadata_node.version, 1);
+ Ok(RepoCommit {
+ sig: commit_node.sig,
+ did: metadata_node.did,
+ prev: root_node.prev.map(|cid| cid.to_string()),
+ mst_cid: root_node.data.to_string(),
+ })
+ }
+
+ pub fn get_record_by_key(&mut self, commit_cid: &str, key: &str) -> Result<Option<Ipld>> {
+ let map = self.as_map(commit_cid)?;
+ if let Some(cid) = map.get(key) {
+ self.get_ipld(&cid.to_string()).map(|v| Some(v))
+ } else {
+ Ok(None)
+ }
+ }
+
+ pub fn write_root(&mut self, did: &str, mst_cid: &str, prev: Option<&str>) -> Result<String> {
+ unimplemented!()
+ }
+
+ pub fn write_commit(&mut self, did: &str, root_cid: &str, sig: &str) -> Result<String> {
+ // TODO: also update alias to point to new commit
+ unimplemented!()
+ }
+
+ pub fn write_map(&self, map: Result<BTreeMap<String, String>>) -> Result<String> {
+ unimplemented!()
+ }
+
+ fn as_cid_map(&mut self, commit_cid: &str) -> Result<BTreeMap<String, Cid>> {
+ let commit = self.get_commit(commit_cid)?;
+ let mut cid_map: BTreeMap<String, Cid> = Default::default();
+ let mst_cid = Cid::from_str(&commit.mst_cid)?;
+ collect_mst_keys(&mut self.db, &mst_cid, &mut cid_map)?;
+ Ok(cid_map)
+ }
+
+ /// Returns all the keys for a directory, as a sorted vec of strings
+ pub fn as_map(&mut self, commit_cid: &str) -> Result<BTreeMap<String, String>> {
+ let cid_map = self.as_cid_map(commit_cid)?;
+ let ret_map: BTreeMap<String, String> =
+ BTreeMap::from_iter(cid_map.into_iter().map(|(k, v)| (k, v.to_string())));
+ Ok(ret_map)
+ }
+
+ /// Exports in CAR format to a Writer
+ ///
+ /// The "from" commit CID feature is not implemented.
+ pub fn write_car<W: std::io::Write>(
+ &mut self,
+ did: &str,
+ _from_commit_cid: Option<&str>,
+ out: &mut W,
+ ) -> Result<()> {
+ unimplemented!()
+ }
+}