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.rs148
1 files changed, 72 insertions, 76 deletions
diff --git a/pkgs/by-name/ba/back/src/web/mod.rs b/pkgs/by-name/ba/back/src/web/mod.rs
index ed91e7e..1e6a5af 100644
--- a/pkgs/by-name/ba/back/src/web/mod.rs
+++ b/pkgs/by-name/ba/back/src/web/mod.rs
@@ -9,125 +9,121 @@
 // 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},
+    error::{self, Error},
+    git_bug::{
+        dag::issues_from_repository,
+        issue::{CollapsedIssue, Status},
+    },
 };
-use gix::{refs::Target, Repository};
-use issue_show::BackPrefix;
+use prefix::BackPrefix;
 use rocket::{
     get,
     response::content::{RawCss, RawHtml},
     State,
 };
 
-mod format;
-mod issue;
-mod issue_show;
+mod issue_html;
+pub mod prefix;
 
 #[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> {
+) -> error::Result<RawHtml<String>> {
     let repository = &config.repository;
 
-    let issue_list = list_all_issues(&repository.to_thread_local())
-        .iter()
+    let issue_list = issues_from_repository(&repository.to_thread_local())?
+        .into_iter()
         .fold(String::new(), |acc, val| {
-            format!("{}{}", acc, &issue_to_string(val, wanted_status).0)
+            let issue = val.collaps();
+
+            format!("{}{}", acc, {
+                if issue.status == wanted_status {
+                    let issue_entry = issue.to_list_entry();
+                    issue_entry.0
+                } else {
+                    String::new()
+                }
+            })
         });
 
     let counter_status_lower = counter_status.to_string().to_lowercase();
-    RawHtml(format!(
+    Ok(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>
-"#,
+    <!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> {
+pub fn open(config: &State<BackConfig>) -> error::Result<RawHtml<String>> {
     issue_list_boilerplate(config, Status::Open, Status::Closed)
 }
 #[get("/issues/closed")]
-pub fn closed(config: &State<BackConfig>) -> RawHtml<String> {
+pub fn closed(config: &State<BackConfig>) -> error::Result<RawHtml<String>> {
     issue_list_boilerplate(config, Status::Closed, Status::Open)
 }
 
 #[get("/issue/<prefix>")]
-pub fn show_issue(config: &State<BackConfig>, prefix: BackPrefix) -> RawHtml<String> {
+pub fn show_issue(
+    config: &State<BackConfig>,
+    prefix: Result<BackPrefix, gix::hash::prefix::from_hex::Error>,
+) -> error::Result<RawHtml<String>> {
+    // NOTE(@bpeetz): Explicitly unwrap the `prefix` here (instead of taking the unwrapped value as
+    // argument), to avoid triggering rockets "errors forward to the next route" feature.
+    // This ensures, that our error message actually reaches the user. <2024-12-26>
+    let prefix = prefix?;
+
     let repository = config.repository.to_thread_local();
 
-    let all_issues = list_all_issues(&repository);
+    let all_issues: Vec<CollapsedIssue> = issues_from_repository(&repository)?
+        .into_iter()
+        .map(|val| val.collapse())
+        .collect();
+
     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())
+        Some(issue) => Ok(issue.to_html(config)),
+        None => Err(Error::IssuesPrefixMissing { prefix }),
     }
 }