gitscope
← rgit / 0.1.3 / 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 git2::Signature;
  5use serde::{Deserialize, Serialize};
  6use yoke::{Yoke, Yokeable};
  7
  8use crate::database::schema::{
  9    commit::Author, prefixes::TAG_FAMILY, repository::RepositoryId, Yoked,
 10};
 11
 12#[derive(Serialize, Deserialize, Debug, Yokeable)]
 13pub struct Tag<'a> {
 14    #[serde(borrow)]
 15    pub tagger: Option<Author<'a>>,
 16}
 17
 18impl<'a> Tag<'a> {
 19    pub fn new(tagger: Option<&'a Signature<'_>>) -> Self {
 20        Self {
 21            tagger: tagger.map(Into::into),
 22        }
 23    }
 24
 25    pub fn insert(&self, batch: &TagTree, name: &str) -> Result<(), anyhow::Error> {
 26        batch.insert(name, self)
 27    }
 28}
 29
 30pub struct TagTree {
 31    db: Arc<rocksdb::DB>,
 32    prefix: RepositoryId,
 33}
 34
 35pub type YokedTag = Yoked<Tag<'static>>;
 36
 37impl TagTree {
 38    pub(super) fn new(db: Arc<rocksdb::DB>, prefix: RepositoryId) -> Self {
 39        Self { db, prefix }
 40    }
 41
 42    pub fn insert(&self, name: &str, value: &Tag<'_>) -> anyhow::Result<()> {
 43        let cf = self
 44            .db
 45            .cf_handle(TAG_FAMILY)
 46            .context("missing tag column family")?;
 47
 48        let mut db_name = self.prefix.to_be_bytes().to_vec();
 49        db_name.extend_from_slice(name.as_ref());
 50
 51        self.db.put_cf(cf, db_name, bincode::serialize(value)?)?;
 52
 53        Ok(())
 54    }
 55
 56    pub fn remove(&self, name: &str) -> anyhow::Result<()> {
 57        let cf = self
 58            .db
 59            .cf_handle(TAG_FAMILY)
 60            .context("missing tag column family")?;
 61
 62        let mut db_name = self.prefix.to_be_bytes().to_vec();
 63        db_name.extend_from_slice(name.as_ref());
 64        self.db.delete_cf(cf, db_name)?;
 65
 66        Ok(())
 67    }
 68
 69    pub fn list(&self) -> anyhow::Result<HashSet<String>> {
 70        let cf = self
 71            .db
 72            .cf_handle(TAG_FAMILY)
 73            .context("missing tag column family")?;
 74
 75        Ok(self
 76            .db
 77            .prefix_iterator_cf(cf, self.prefix.to_be_bytes())
 78            .filter_map(Result::ok)
 79            .filter_map(|(k, _)| {
 80                Some(
 81                    String::from_utf8_lossy(k.strip_prefix(&self.prefix.to_be_bytes())?)
 82                        .to_string(),
 83                )
 84            })
 85            .collect())
 86    }
 87
 88    pub fn fetch_all(&self) -> anyhow::Result<Vec<(String, YokedTag)>> {
 89        let cf = self
 90            .db
 91            .cf_handle(TAG_FAMILY)
 92            .context("missing tag column family")?;
 93
 94        let mut res = self
 95            .db
 96            .prefix_iterator_cf(cf, self.prefix.to_be_bytes())
 97            .filter_map(Result::ok)
 98            .filter_map(|(name, value)| {
 99                let name = String::from_utf8_lossy(name.strip_prefix(&self.prefix.to_be_bytes())?)
100                    .strip_prefix("refs/tags/")?
101                    .to_string();
102
103                Some((name, value))
104            })
105            .map(|(name, value)| {
106                let value = Yoke::try_attach_to_cart(value, |data| bincode::deserialize(data))?;
107                Ok((name, value))
108            })
109            .collect::<anyhow::Result<Vec<(String, YokedTag)>>>()?;
110
111        res.sort_unstable_by(|a, b| {
112            let a_tagger = a.1.get().tagger.as_ref().map(|v| v.time);
113            let b_tagger = b.1.get().tagger.as_ref().map(|v| v.time);
114            b_tagger.cmp(&a_tagger)
115        });
116
117        Ok(res)
118    }
119}