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