use std::{collections::HashSet, sync::Arc}; use anyhow::Context; use gix::actor::SignatureRef; use rkyv::{Archive, Serialize}; use yoke::{Yoke, Yokeable}; use crate::database::schema::{ commit::{ArchivedAuthor, Author}, prefixes::TAG_FAMILY, repository::RepositoryId, Yoked, }; #[derive(Serialize, Archive, Debug, Yokeable)] pub struct Tag { pub tagger: Option, } impl Tag { pub fn new(tagger: Option>) -> Result { Ok(Self { tagger: tagger.map(TryFrom::try_from).transpose()?, }) } pub fn insert(&self, batch: &TagTree, name: &str) -> Result<(), anyhow::Error> { batch.insert(name, self) } } pub struct TagTree { db: Arc, prefix: RepositoryId, } pub type YokedString = Yoked<&'static str>; pub type YokedTag = Yoked<&'static ::Archived>; impl TagTree { pub(super) fn new(db: Arc, prefix: RepositoryId) -> Self { Self { db, prefix } } pub fn insert(&self, name: &str, value: &Tag) -> anyhow::Result<()> { let cf = self .db .cf_handle(TAG_FAMILY) .context("missing tag column family")?; let mut db_name = self.prefix.to_be_bytes().to_vec(); db_name.extend_from_slice(name.as_ref()); self.db .put_cf(cf, db_name, rkyv::to_bytes::(value)?)?; Ok(()) } pub fn remove(&self, name: &str) -> anyhow::Result<()> { let cf = self .db .cf_handle(TAG_FAMILY) .context("missing tag column family")?; let mut db_name = self.prefix.to_be_bytes().to_vec(); db_name.extend_from_slice(name.as_ref()); self.db.delete_cf(cf, db_name)?; Ok(()) } pub fn list(&self) -> anyhow::Result> { let cf = self .db .cf_handle(TAG_FAMILY) .context("missing tag column family")?; Ok(self .db .prefix_iterator_cf(cf, self.prefix.to_be_bytes()) .filter_map(Result::ok) .filter_map(|(k, _)| { Some( String::from_utf8_lossy(k.strip_prefix(&self.prefix.to_be_bytes())?) .to_string(), ) }) .collect()) } pub fn fetch_all(&self) -> anyhow::Result> { let cf = self .db .cf_handle(TAG_FAMILY) .context("missing tag column family")?; let mut res = self .db .prefix_iterator_cf(cf, self.prefix.to_be_bytes()) .filter_map(Result::ok) .filter_map(|(name, value)| { let name = Yoke::try_attach_to_cart(name, |data| { let data = data .strip_prefix(&self.prefix.to_be_bytes()) .ok_or(())? .strip_prefix(b"refs/tags/") .ok_or(())?; simdutf8::basic::from_utf8(data).map_err(|_| ()) }) .ok()?; Some((name, value)) }) .map(|(name, value)| { let value = Yoke::try_attach_to_cart(value, |data| { rkyv::access::<_, rkyv::rancor::Error>(data) })?; Ok((name, value)) }) .collect::>>()?; res.sort_unstable_by(|a, b| { let a_tagger = a.1.get().tagger.as_ref().map(ArchivedAuthor::time); let b_tagger = b.1.get().tagger.as_ref().map(ArchivedAuthor::time); b_tagger.cmp(&a_tagger) }); Ok(res) } }