1use std::{collections::HashSet, ops::Deref};
2
3use git2::Signature;
4use serde::{Deserialize, Serialize};
5use sled::IVec;
6use yoke::{Yoke, Yokeable};
7
8use crate::database::schema::{commit::Author, Yoked};
9
10#[derive(Serialize, Deserialize, Debug, Yokeable)]
11pub struct Tag<'a> {
12 #[serde(borrow)]
13 pub tagger: Option<Author<'a>>,
14}
15
16impl<'a> Tag<'a> {
17 pub fn new(tagger: Option<&'a Signature<'_>>) -> Self {
18 Self {
19 tagger: tagger.map(Into::into),
20 }
21 }
22
23 pub fn insert(&self, batch: &TagTree, name: &str) {
24 batch
25 .insert(name.as_bytes(), bincode::serialize(self).unwrap())
26 .unwrap();
27 }
28}
29
30pub struct TagTree(sled::Tree);
31
32impl Deref for TagTree {
33 type Target = sled::Tree;
34
35 fn deref(&self) -> &Self::Target {
36 &self.0
37 }
38}
39
40pub type YokedTag = Yoked<Tag<'static>>;
41
42impl TagTree {
43 pub(super) fn new(tree: sled::Tree) -> Self {
44 Self(tree)
45 }
46
47 pub fn remove(&self, name: &str) -> bool {
48 self.0.remove(name).unwrap().is_some()
49 }
50
51 pub fn list(&self) -> HashSet<String> {
52 self.iter()
53 .keys()
54 .filter_map(Result::ok)
55 .map(|v| String::from_utf8_lossy(&v).into_owned())
56 .collect()
57 }
58
59 pub fn fetch_all(&self) -> Vec<(String, YokedTag)> {
60 let mut res = self
61 .iter()
62 .map(|res| {
63 let (name, value) = res?;
64
65 let name = String::from_utf8_lossy(&name)
66 .strip_prefix("refs/tags/")
67 .unwrap()
68 .to_string();
69
70 // internally value is an Arc so it should already be stablederef but because
71 // of reasons unbeknownst to me, sled has its own Arc implementation so we need
72 // to box the value as well to get a stablederef...
73 let value = Box::new(value);
74
75 Ok((
76 name,
77 Yoke::try_attach_to_cart(value, |data: &IVec| bincode::deserialize(data))
78 .unwrap(),
79 ))
80 })
81 .collect::<Result<Vec<(String, YokedTag)>, sled::Error>>()
82 .unwrap();
83
84 res.sort_unstable_by(|a, b| {
85 let a_tagger = a.1.get().tagger.as_ref().map(|v| v.time);
86 let b_tagger = b.1.get().tagger.as_ref().map(|v| v.time);
87 b_tagger.cmp(&a_tagger)
88 });
89
90 res
91 }
92}