diff options
Diffstat (limited to 'pkgs/by-name/ba/back/src/web/mod.rs')
-rw-r--r-- | pkgs/by-name/ba/back/src/web/mod.rs | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/pkgs/by-name/ba/back/src/web/mod.rs b/pkgs/by-name/ba/back/src/web/mod.rs new file mode 100644 index 0000000..ed91e7e --- /dev/null +++ b/pkgs/by-name/ba/back/src/web/mod.rs @@ -0,0 +1,133 @@ +// 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::{ + config::BackConfig, + web::issue::{Issue, Status}, +}; +use gix::{refs::Target, Repository}; +use issue_show::BackPrefix; +use rocket::{ + get, + response::content::{RawCss, RawHtml}, + State, +}; + +mod format; +mod issue; +mod issue_show; + +#[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( + config: &State<BackConfig>, + wanted_status: Status, + counter_status: Status, +) -> RawHtml<String> { + let repository = &config.repository; + + 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> + <a href="{}">Source code</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"> + {issue_list} + </ol> + </main> + </div> + </body> +</html> +"#, + config.source_code_repository_url + )) +} + +#[get("/issues/open")] +pub fn open(config: &State<BackConfig>) -> RawHtml<String> { + issue_list_boilerplate(config, Status::Open, Status::Closed) +} +#[get("/issues/closed")] +pub fn closed(config: &State<BackConfig>) -> RawHtml<String> { + issue_list_boilerplate(config, Status::Closed, Status::Open) +} + +#[get("/issue/<prefix>")] +pub fn show_issue(config: &State<BackConfig>, prefix: BackPrefix) -> RawHtml<String> { + let repository = config.repository.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(config), + 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()) + } +} |