summary refs log tree commit diff stats
path: root/pkgs/by-name/ba/back/src/issues/mod.rs
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-12-23 18:29:35 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-12-23 18:29:35 +0100
commit4bcadcd2244989a8bb7d8dffdaf11f131b96c8ff (patch)
treea081c1c4216f4d1517940c2bc92dc7d8b3e15bb9 /pkgs/by-name/ba/back/src/issues/mod.rs
parentfix(modules/disko): Remove deprecated legacy type and migrate to `by-name` (diff)
downloadnixos-server-4bcadcd2244989a8bb7d8dffdaf11f131b96c8ff.tar.gz
nixos-server-4bcadcd2244989a8bb7d8dffdaf11f131b96c8ff.zip
feat(pkgs/back): Init
Other options, for example `git-bug webui --read-only` is just to bugged
to be useful.
Diffstat (limited to 'pkgs/by-name/ba/back/src/issues/mod.rs')
-rw-r--r--pkgs/by-name/ba/back/src/issues/mod.rs129
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())
+    }
+}