use std::{borrow::Cow, collections::BTreeMap, ops::Deref, path::Path};
use anyhow::{Context, Result};
use nom::AsBytes;
use serde::{Deserialize, Serialize};
use sled::IVec;
use time::OffsetDateTime;
use yoke::{Yoke, Yokeable};
use crate::database::schema::{commit::CommitTree, prefixes::TreePrefix, tag::TagTree, Yoked};
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Yokeable)]
pub struct Repository<'a> {
pub id: RepositoryId,
#[serde(borrow)]
pub name: Cow<'a, str>,
#[serde(borrow)]
pub description: Option<Cow<'a, str>>,
#[serde(borrow)]
pub owner: Option<Cow<'a, str>>,
pub last_modified: OffsetDateTime,
}
pub type YokedRepository = Yoked<Repository<'static>>;
impl Repository<'_> {
pub fn fetch_all(database: &sled::Db) -> Result<BTreeMap<String, YokedRepository>> {
database
.scan_prefix([TreePrefix::Repository as u8])
.filter_map(Result::ok)
.map(|(key, value)| {
let key = String::from_utf8_lossy(&key[1..]).to_string();
let value = Box::new(value);
let value =
Yoke::try_attach_to_cart(value, |data: &IVec| bincode::deserialize(data))?;
Ok((key, value))
})
.collect()
}
pub fn insert<P: AsRef<Path>>(&self, database: &sled::Db, path: P) {
database
.insert(
TreePrefix::repository_id(path),
bincode::serialize(self).unwrap(),
)
.unwrap();
}
pub fn open<P: AsRef<Path>>(database: &sled::Db, path: P) -> Result<Option<YokedRepository>> {
database
.get(TreePrefix::repository_id(path))
.context("Failed to open indexed repository")?
.map(|value| {
let value = Box::new(value);
Yoke::try_attach_to_cart(value, |data: &IVec| bincode::deserialize(data))
.context("Failed to deserialise indexed repository")
})
.transpose()
}
pub fn commit_tree(&self, database: &sled::Db, reference: &str) -> Result<CommitTree> {
let tree = database
.open_tree(TreePrefix::commit_id(self.id, reference))
.context("Failed to open commit tree")?;
Ok(CommitTree::new(tree))
}
pub fn tag_tree(&self, database: &sled::Db) -> Result<TagTree> {
let tree = database
.open_tree(TreePrefix::tag_id(self.id))
.context("Failed to open tag tree")?;
Ok(TagTree::new(tree))
}
pub fn heads(&self, database: &sled::Db) -> Vec<String> {
let prefix = TreePrefix::commit_id(self.id, "");
database
.tree_names()
.into_iter()
.filter_map(|v| {
v.strip_prefix(prefix.as_bytes())
.map(|v| String::from_utf8_lossy(v).into_owned())
})
.collect()
}
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct RepositoryId(pub(super) u64);
impl RepositoryId {
pub fn new(db: &sled::Db) -> Self {
Self(db.generate_id().unwrap())
}
}
impl Deref for RepositoryId {
type Target = u64;
fn deref(&self) -> &Self::Target {
&self.0
}
}