summary refs log tree commit diff stats
path: root/pkgs/by-name/ba/back/src/web/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/by-name/ba/back/src/web/mod.rs')
-rw-r--r--pkgs/by-name/ba/back/src/web/mod.rs133
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())
+    }
+}