raw
1mod branch;
2mod commit;
3mod core;
4mod tag;
5mod tree;
6
7use std::path::Path;
8
9use git2::{Object, Signature};
10
11use crate::utils::error::{Context as _, Result};
12
13pub struct TagEntry {
14 pub link: String,
15 pub tag: String,
16 pub message: String,
17 pub signature: Signature<'static>,
18}
19
20impl TagEntry {
21 #[tracing::instrument(skip_all)]
22 fn try_from_commit(name: String, obj: &Object<'_>) -> Result<Self> {
23 Ok(Self {
24 link: format!("refs/{name}"),
25 tag: name,
26 message: String::new(),
27 signature: obj
28 .as_commit()
29 .context("git object is not a commit")?
30 .committer()
31 .to_owned(),
32 })
33 }
34
35 #[tracing::instrument(skip_all)]
36 fn try_from_tag(name: String, obj: &Object<'_>) -> Result<Self> {
37 let tag = obj.as_tag().context("git object is not a tag")?;
38
39 Ok(Self {
40 link: format!("refs/{name}"),
41 tag: name,
42 message: tag.message().unwrap_or("").to_string(),
43 signature: tag
44 .tagger()
45 .context("git tag does not have a tagger")
46 .or_else(|_| -> Result<Signature<'_>> {
47 let signature = obj
48 .peel_to_commit()
49 .context("failed to peel object to commit")?
50 .committer()
51 .to_owned();
52
53 Ok(signature)
54 })?
55 .to_owned(),
56 })
57 }
58}
59
60pub struct Repository {
61 inner: git2::Repository,
62}
63
64impl Repository {
65 #[tracing::instrument(skip_all)]
66 pub fn open<P>(path: P) -> Result<Option<Self>>
67 where
68 P: AsRef<Path>,
69 {
70 let config = crate::config();
71
72 let path = path.as_ref();
73
74 let path = config.project_root.join(path);
75
76 if !path.exists() {
77 return Ok(None);
78 }
79
80 let path = path.canonicalize().context("failed to canonicalize path")?;
81
82 if !path.starts_with(&config.project_root) {
83 tracing::warn!(
84 root=?config.project_root.display(),
85 requested=?path.display(),
86 "attempted path traversal",
87 );
88
89 return Ok(None);
90 }
91
92 if path == config.project_root {
93 return Ok(None);
94 }
95
96 if !path.exists() {
97 return Ok(None);
98 }
99
100 let inner = git2::Repository::open(path).context("failed to actually open the repo")?;
101
102 if !inner.path().join(&config.export_ok).exists() {
103 tracing::warn!("tried to access private repo");
104
105 return Ok(None);
106 }
107
108 Ok(Some(Self { inner }))
109 }
110
111 #[must_use]
112 pub const fn as_inner(&self) -> &git2::Repository {
113 &self.inner
114 }
115}
116