gitscope
← rgit / 0.1.4 / src/database/schema/tag.rs
https://dev.kronoz.win/r/rgit.git
src/database/schema/tag.rs · Rust
Blame Raw
  1use std::{collections::HashSet, sync::Arc};
  2
  3use anyhow::Context;
  4use gix::actor::SignatureRef;
  5use rkyv::{Archive, Serialize};
  6use yoke::{Yoke, Yokeable};
  7
  8use crate::database::schema::{
  9    commit::{ArchivedAuthor, Author},
 10    prefixes::TAG_FAMILY,
 11    repository::RepositoryId,
 12    Yoked,
 13};
 14
 15#[derive(Serialize, Archive, Debug, Yokeable)]
 16pub struct Tag {
 17    pub tagger: Option<Author>,
 18}
 19
 20impl Tag {
 21    pub fn new(tagger: Option<SignatureRef<'_>>) -> Result<Self, anyhow::Error> {
 22        Ok(Self {
 23            tagger: tagger.map(TryFrom::try_from).transpose()?,
 24        })
 25    }
 26
 27    pub fn insert(&self, batch: &TagTree, name: &str) -> Result<(), anyhow::Error> {
 28        batch.insert(name, self)
 29    }
 30}
 31
 32pub struct TagTree {
 33    db: Arc<rocksdb::DB>,
 34    prefix: RepositoryId,
 35}
 36
 37pub type YokedString = Yoked<&'static str>;
 38pub type YokedTag = Yoked<&'static <Tag as Archive>::Archived>;
 39
 40impl TagTree {
 41    pub(super) fn new(db: Arc<rocksdb::DB>, prefix: RepositoryId) -> Self {
 42        Self { db, prefix }
 43    }
 44
 45    pub fn insert(&self, name: &str, value: &Tag) -> anyhow::Result<()> {
 46        let cf = self
 47            .db
 48            .cf_handle(TAG_FAMILY)
 49            .context("missing tag column family")?;
 50
 51        let mut db_name = self.prefix.to_be_bytes().to_vec();
 52        db_name.extend_from_slice(name.as_ref());
 53
 54        self.db
 55            .put_cf(cf, db_name, rkyv::to_bytes::<rkyv::rancor::Error>(value)?)?;
 56
 57        Ok(())
 58    }
 59
 60    pub fn remove(&self, name: &str) -> anyhow::Result<()> {
 61        let cf = self
 62            .db
 63            .cf_handle(TAG_FAMILY)
 64            .context("missing tag column family")?;
 65
 66        let mut db_name = self.prefix.to_be_bytes().to_vec();
 67        db_name.extend_from_slice(name.as_ref());
 68        self.db.delete_cf(cf, db_name)?;
 69
 70        Ok(())
 71    }
 72
 73    pub fn list(&self) -> anyhow::Result<HashSet<String>> {
 74        let cf = self
 75            .db
 76            .cf_handle(TAG_FAMILY)
 77            .context("missing tag column family")?;
 78
 79        Ok(self
 80            .db
 81            .prefix_iterator_cf(cf, self.prefix.to_be_bytes())
 82            .filter_map(Result::ok)
 83            .filter_map(|(k, _)| {
 84                Some(
 85                    String::from_utf8_lossy(k.strip_prefix(&self.prefix.to_be_bytes())?)
 86                        .to_string(),
 87                )
 88            })
 89            .collect())
 90    }
 91
 92    pub fn fetch_all(&self) -> anyhow::Result<Vec<(YokedString, YokedTag)>> {
 93        let cf = self
 94            .db
 95            .cf_handle(TAG_FAMILY)
 96            .context("missing tag column family")?;
 97
 98        let mut res = self
 99            .db
100            .prefix_iterator_cf(cf, self.prefix.to_be_bytes())
101            .filter_map(Result::ok)
102            .filter_map(|(name, value)| {
103                let name = Yoke::try_attach_to_cart(name, |data| {
104                    let data = data
105                        .strip_prefix(&self.prefix.to_be_bytes())
106                        .ok_or(())?
107                        .strip_prefix(b"refs/tags/")
108                        .ok_or(())?;
109                    simdutf8::basic::from_utf8(data).map_err(|_| ())
110                })
111                .ok()?;
112
113                Some((name, value))
114            })
115            .map(|(name, value)| {
116                let value = Yoke::try_attach_to_cart(value, |data| {
117                    rkyv::access::<_, rkyv::rancor::Error>(data)
118                })?;
119                Ok((name, value))
120            })
121            .collect::<anyhow::Result<Vec<(YokedString, YokedTag)>>>()?;
122
123        res.sort_unstable_by(|a, b| {
124            let a_tagger = a.1.get().tagger.as_ref().map(ArchivedAuthor::time);
125            let b_tagger = b.1.get().tagger.as_ref().map(ArchivedAuthor::time);
126            b_tagger.cmp(&a_tagger)
127        });
128
129        Ok(res)
130    }
131}