wayver's git archive


a simple self-hosted git server
git clone https://git.wayver.dev/bile

src/git/core.rs@32c6bed79b26b918b19bcaa6d2f5eaf797bbeb92

raw
Date Commit Message Author Files + -
2026-02-23 00:23 make it so README.md is detected as a valid readme wayverd 1 1 1
...

1use std::path::Path;
2
3use git2::{Reference, Time};
4use syntect::parsing::SyntaxSet;
5
6use crate::{error::Context as _, error::Result, git::Repository, http::extractor::Ref};
7
8impl Repository {
9    #[must_use]
10    pub(crate) fn description(&self) -> String {
11        let content = std::fs::read_to_string(self.path().join("description")).unwrap_or_default();
12
13        let first = content.lines().next().unwrap_or_default();
14
15        first.to_string()
16    }
17
18    #[tracing::instrument(skip_all)]
19    pub(crate) fn head(&self) -> Result<Reference<'_>> {
20        let head = self.inner.head()?;
21
22        Ok(head)
23    }
24
25    #[tracing::instrument(skip_all)]
26    pub(crate) fn is_empty(&self) -> Result<bool> {
27        Ok(self.inner.is_empty()?)
28    }
29
30    #[must_use]
31    pub(crate) fn is_shallow(&self) -> bool {
32        self.inner.is_shallow()
33    }
34
35    #[tracing::instrument(skip_all)]
36    pub(crate) fn last_modified(&self) -> Result<Time> {
37        let head = self.head()?;
38        let commit = head.peel_to_commit()?;
39        let time = commit.committer().when();
40
41        Ok(time)
42    }
43
44    pub(crate) fn name(&self) -> Option<&str> {
45        self.inner
46            .workdir()
47            // use the path for bare repositories
48            .unwrap_or_else(|| self.inner.path())
49            .file_name()
50            .and_then(std::ffi::OsStr::to_str)
51    }
52
53    #[must_use]
54    pub(crate) fn owner(&self) -> String {
55        self.inner
56            .config()
57            .and_then(|config| config.get_string("gitweb.owner"))
58            .unwrap_or_default()
59    }
60
61    #[must_use]
62    pub(crate) fn path(&self) -> &Path {
63        self.inner.path()
64    }
65
66    #[must_use]
67    pub(crate) fn readme(&self, syntaxes: &SyntaxSet) -> String {
68        use askama::filters::Escaper as _;
69
70        enum ReadmeFormat {
71            Plaintext,
72            Html,
73            Markdown,
74        }
75
76        let mut format = ReadmeFormat::Plaintext;
77
78        self.inner
79            .revparse_single("HEAD:readme")
80            .or_else(|_| self.inner.revparse_single("HEAD:README.txt"))
81            .or_else(|_| self.inner.revparse_single("HEAD:readme.txt"))
82            .or_else(|_| self.inner.revparse_single("HEAD:README.txt"))
83            .or_else(|_| {
84                format = ReadmeFormat::Markdown;
85                self.inner.revparse_single("HEAD:readme.md")
86            })
87            .or_else(|_| self.inner.revparse_single("HEAD:README.md"))
88            .or_else(|_| self.inner.revparse_single("HEAD:readme.mdown"))
89            .or_else(|_| self.inner.revparse_single("HEAD:README.mdown"))
90            .or_else(|_| self.inner.revparse_single("HEAD:readme.markdown"))
91            .or_else(|_| self.inner.revparse_single("HEAD:README.markdown"))
92            .or_else(|_| {
93                format = ReadmeFormat::Html;
94                self.inner.revparse_single("HEAD:readme.html")
95            })
96            .or_else(|_| self.inner.revparse_single("HEAD:README.html"))
97            .or_else(|_| self.inner.revparse_single("HEAD:readme.htm"))
98            .or_else(|_| self.inner.revparse_single("HEAD:README.htm"))
99            .ok()
100            .and_then(|readme| readme.into_blob().ok())
101            .map(|blob| {
102                let text = str::from_utf8(blob.content()).unwrap_or_default();
103
104                // render the file contents to HTML
105                match format {
106                    // render plaintext as preformatted text
107                    ReadmeFormat::Plaintext => {
108                        let mut output = "<pre>".to_string();
109                        if let Err(err) = askama::filters::Html.write_escaped_str(&mut output, text)
110                        {
111                            tracing::error!(err=?err, "failed to write escaped plaintext readme");
112                        }
113                        output.push_str("</pre>");
114                        output
115                    }
116                    // already is HTML
117                    ReadmeFormat::Html => text.to_string(),
118                    // render Markdown to HTML
119                    ReadmeFormat::Markdown => crate::utils::markdown::render(syntaxes, text),
120                }
121            })
122            .unwrap_or_default()
123    }
124
125    #[tracing::instrument(skip_all)]
126    pub(crate) fn ref_or_head_shorthand(&self, r#ref: Option<&Ref>) -> Result<String> {
127        let head = self.head()?;
128
129        let spec = r#ref
130            .map(|r| r.0.clone())
131            .or_else(move || head.shorthand().map(str::to_string))
132            .context("failed to get repo ref spec")?;
133
134        Ok(spec)
135    }
136}
137