gitscope
← rgit / 0.1.0 / src/database/schema/repository.rs
https://dev.kronoz.win/r/rgit.git
src/database/schema/repository.rs · Rust
Blame Raw
  1use std::{borrow::Cow, collections::BTreeMap, ops::Deref, path::Path};
  2
  3use anyhow::{Context, Result};
  4use nom::AsBytes;
  5use serde::{Deserialize, Serialize};
  6use sled::IVec;
  7use time::OffsetDateTime;
  8use yoke::{Yoke, Yokeable};
  9
 10use crate::database::schema::{commit::CommitTree, prefixes::TreePrefix, tag::TagTree, Yoked};
 11
 12#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Yokeable)]
 13pub struct Repository<'a> {
 14    /// The ID of the repository, as stored in `sled`
 15    pub id: RepositoryId,
 16    /// The "clean name" of the repository (ie. `hello-world.git`)
 17    #[serde(borrow)]
 18    pub name: Cow<'a, str>,
 19    /// The description of the repository, as it is stored in the `description` file in the
 20    /// bare repo root
 21    #[serde(borrow)]
 22    pub description: Option<Cow<'a, str>>,
 23    /// The owner of the repository (`gitweb.owner` in the repository configuration)
 24    #[serde(borrow)]
 25    pub owner: Option<Cow<'a, str>>,
 26    /// The last time this repository was updated, currently read from the directory mtime
 27    pub last_modified: OffsetDateTime,
 28}
 29
 30pub type YokedRepository = Yoked<Repository<'static>>;
 31
 32impl Repository<'_> {
 33    pub fn fetch_all(database: &sled::Db) -> Result<BTreeMap<String, YokedRepository>> {
 34        database
 35            .scan_prefix([TreePrefix::Repository as u8])
 36            .filter_map(Result::ok)
 37            .map(|(key, value)| {
 38                // strip the prefix we've just scanned for
 39                let key = String::from_utf8_lossy(&key[1..]).to_string();
 40
 41                // internally value is an Arc so it should already be stablederef but because
 42                // of reasons unbeknownst to me, sled has its own Arc implementation so we need
 43                // to box the value as well to get a stablederef...
 44                let value = Box::new(value);
 45
 46                let value =
 47                    Yoke::try_attach_to_cart(value, |data: &IVec| bincode::deserialize(data))?;
 48
 49                Ok((key, value))
 50            })
 51            .collect()
 52    }
 53
 54    pub fn insert<P: AsRef<Path>>(&self, database: &sled::Db, path: P) {
 55        database
 56            .insert(
 57                TreePrefix::repository_id(path),
 58                bincode::serialize(self).unwrap(),
 59            )
 60            .unwrap();
 61    }
 62
 63    pub fn open<P: AsRef<Path>>(database: &sled::Db, path: P) -> Result<Option<YokedRepository>> {
 64        database
 65            .get(TreePrefix::repository_id(path))
 66            .context("Failed to open indexed repository")?
 67            .map(|value| {
 68                // internally value is an Arc so it should already be stablederef but because
 69                // of reasons unbeknownst to me, sled has its own Arc implementation so we need
 70                // to box the value as well to get a stablederef...
 71                let value = Box::new(value);
 72
 73                Yoke::try_attach_to_cart(value, |data: &IVec| bincode::deserialize(data))
 74                    .context("Failed to deserialise indexed repository")
 75            })
 76            .transpose()
 77    }
 78
 79    pub fn commit_tree(&self, database: &sled::Db, reference: &str) -> Result<CommitTree> {
 80        let tree = database
 81            .open_tree(TreePrefix::commit_id(self.id, reference))
 82            .context("Failed to open commit tree")?;
 83
 84        Ok(CommitTree::new(tree))
 85    }
 86
 87    pub fn tag_tree(&self, database: &sled::Db) -> Result<TagTree> {
 88        let tree = database
 89            .open_tree(TreePrefix::tag_id(self.id))
 90            .context("Failed to open tag tree")?;
 91
 92        Ok(TagTree::new(tree))
 93    }
 94
 95    pub fn heads(&self, database: &sled::Db) -> Vec<String> {
 96        let prefix = TreePrefix::commit_id(self.id, "");
 97
 98        database
 99            .tree_names()
100            .into_iter()
101            .filter_map(|v| {
102                v.strip_prefix(prefix.as_bytes())
103                    .map(|v| String::from_utf8_lossy(v).into_owned())
104            })
105            .collect()
106    }
107}
108
109#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)]
110pub struct RepositoryId(pub(super) u64);
111
112impl RepositoryId {
113    pub fn new(db: &sled::Db) -> Self {
114        Self(db.generate_id().unwrap())
115    }
116}
117
118impl Deref for RepositoryId {
119    type Target = u64;
120
121    fn deref(&self) -> &Self::Target {
122        &self.0
123    }
124}