diff options
Diffstat (limited to '')
-rw-r--r-- | pkgs/by-name/ba/back/src/issues/mod.rs | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/pkgs/by-name/ba/back/src/issues/mod.rs b/pkgs/by-name/ba/back/src/issues/mod.rs new file mode 100644 index 0000000..e2af5de --- /dev/null +++ b/pkgs/by-name/ba/back/src/issues/mod.rs @@ -0,0 +1,129 @@ +// Back - An extremely simple git issue tracking system. Inspired by tvix's +// panettone +// +// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This file is part of Back. +// +// You should have received a copy of the License along with this program. +// If not, see <https://www.gnu.org/licenses/agpl.txt>. + +use std::path::Path; + +use crate::issues::issue::{Issue, Status}; +use issue_show::BackPrefix; +use gix::{refs::Target, Repository}; +use rocket::{ + get, + response::content::{RawCss, RawHtml}, +}; + +use crate::REPOSITORY; + +mod issue; +mod issue_show; +mod format; + +#[get("/style.css")] +pub fn styles() -> RawCss<String> { + RawCss(include_str!("../../assets/style.css").to_owned()) +} + +fn list_all_issues(repo: &'_ Repository) -> Vec<Issue<'_>> { + repo.refs + .iter() + .expect("We should be able to iterate over references") + .prefixed(Path::new("refs/bugs/")) + .expect("The 'refs/bugs/' namespace should exist") + .map(|val| { + let reference = val.expect("'val' should be an object?"); + if let Target::Object(commit_id) = reference.target { + Issue::from_commit_id(repo, commit_id) + } else { + unreachable!("All 'refs/bugs/' should contain a clear target."); + } + }) + .collect() +} + +pub fn issue_list_boilerplate(wanted_status: Status, counter_status: Status) -> RawHtml<String> { + let repository = REPOSITORY.get().expect("This should have been set"); + + let issue_list = list_all_issues(&repository.to_thread_local()) + .iter() + .fold(String::new(), |acc, val| { + format!("{}{}", acc, &issue_to_string(val, wanted_status).0) + }); + + let counter_status_lower = counter_status.to_string().to_lowercase(); + RawHtml(format!( + r#" +<!DOCTYPE html> +<html lang="en"> + <head> + <title>Back</title> + <link href="/style.css" rel="stylesheet" type="text/css"> + <meta content="width=device-width,initial-scale=1" name="viewport"> + </head> + <body> + <div class="content"> + <header> + <h1>{wanted_status} Issues</h1> + </header> + <main> + <div class="issue-links"> + <a href="/issues/{counter_status_lower}/">View {counter_status} issues</a> + <!-- + <form class="issue-search" method="get"> + <input name="search" title="Issue search query" type="search"> + <input class="sr-only" type="submit" value="Search Issues"> + </form> + --> + </div> + <ol class="issue-list"> + {} + </ol> + </main> + </div> + </body> +</html> +"#, + issue_list + )) +} + +#[get("/issues/open")] +pub fn open() -> RawHtml<String> { + issue_list_boilerplate(Status::Open, Status::Closed) +} +#[get("/issues/closed")] +pub fn closed() -> RawHtml<String> { + issue_list_boilerplate(Status::Closed, Status::Open) +} + +#[get("/issue/<prefix>")] +pub fn show_issue(prefix: BackPrefix) -> RawHtml<String> { + let repository = REPOSITORY + .get() + .expect("This should have been set") + .to_thread_local(); + + let all_issues = list_all_issues(&repository); + let maybe_issue = all_issues + .iter() + .find(|issue| issue.id.to_string().starts_with(&prefix.to_string())); + + match maybe_issue { + Some(issue) => issue.to_html(), + None => RawHtml(format!("Issue with id '{prefix}' not found!")), + } +} + +fn issue_to_string(issue: &Issue<'_>, status: Status) -> RawHtml<String> { + if issue.status == status { + issue.to_list_entry() + } else { + RawHtml(String::default()) + } +} |