summary refs log tree commit diff stats
path: root/pkgs/by-name/ba
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/by-name/ba')
-rw-r--r--pkgs/by-name/ba/back/Cargo.lock122
-rw-r--r--pkgs/by-name/ba/back/Cargo.toml4
-rw-r--r--pkgs/by-name/ba/back/contrib/config.json4
-rw-r--r--pkgs/by-name/ba/back/src/cli.rs13
-rw-r--r--pkgs/by-name/ba/back/src/config/mod.rs58
-rw-r--r--pkgs/by-name/ba/back/src/error/mod.rs56
-rw-r--r--pkgs/by-name/ba/back/src/main.rs73
-rw-r--r--pkgs/by-name/ba/back/src/web/format/mod.rs (renamed from pkgs/by-name/ba/back/src/issues/format/mod.rs)0
-rw-r--r--pkgs/by-name/ba/back/src/web/issue/mod.rs (renamed from pkgs/by-name/ba/back/src/issues/issue/mod.rs)15
-rw-r--r--pkgs/by-name/ba/back/src/web/issue/raw.rs (renamed from pkgs/by-name/ba/back/src/issues/issue/raw.rs)2
-rw-r--r--pkgs/by-name/ba/back/src/web/issue_show.rs (renamed from pkgs/by-name/ba/back/src/issues/issue_show.rs)0
-rw-r--r--pkgs/by-name/ba/back/src/web/mod.rs (renamed from pkgs/by-name/ba/back/src/issues/mod.rs)35
12 files changed, 305 insertions, 77 deletions
diff --git a/pkgs/by-name/ba/back/Cargo.lock b/pkgs/by-name/ba/back/Cargo.lock
index ed7f120..b72f23c 100644
--- a/pkgs/by-name/ba/back/Cargo.lock
+++ b/pkgs/by-name/ba/back/Cargo.lock
@@ -71,6 +71,55 @@ dependencies = [
 ]
 
 [[package]]
+name = "anstream"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
 name = "arc-swap"
 version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -141,11 +190,13 @@ name = "back"
 version = "0.1.0"
 dependencies = [
  "chrono",
+ "clap",
  "gix",
  "markdown",
  "rocket",
  "serde",
  "serde_json",
+ "thiserror",
  "url",
 ]
 
@@ -247,12 +298,58 @@ dependencies = [
 ]
 
 [[package]]
+name = "clap"
+version = "4.5.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+
+[[package]]
 name = "clru"
 version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59"
 
 [[package]]
+name = "colorchoice"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+
+[[package]]
 name = "cookie"
 version = "0.18.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1470,6 +1567,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
 
 [[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
 name = "hermit-abi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1776,6 +1879,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
 name = "itoa"
 version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2549,6 +2658,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
 
 [[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
 name = "syn"
 version = "2.0.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2898,6 +3013,7 @@ dependencies = [
  "form_urlencoded",
  "idna",
  "percent-encoding",
+ "serde",
 ]
 
 [[package]]
@@ -2913,6 +3029,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
 
 [[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
 name = "valuable"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/pkgs/by-name/ba/back/Cargo.toml b/pkgs/by-name/ba/back/Cargo.toml
index f697511..2fc0600 100644
--- a/pkgs/by-name/ba/back/Cargo.toml
+++ b/pkgs/by-name/ba/back/Cargo.toml
@@ -23,12 +23,14 @@ license = "AGPL-3.0-or-later"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [dependencies]
 chrono = "0.4.39"
+clap = { version = "4.5.23", features = ["derive"] }
 gix = "0.69.1"
 markdown = "1.0.0-alpha.21"
 rocket = "0.5.1"
 serde = "1.0.216"
 serde_json = "1.0.134"
-url = "2.5.4"
+thiserror = "2.0.9"
+url = { version = "2.5.4", features = ["serde"] }
 
 [profile.release]
 lto = true
diff --git a/pkgs/by-name/ba/back/contrib/config.json b/pkgs/by-name/ba/back/contrib/config.json
new file mode 100644
index 0000000..10173fb
--- /dev/null
+++ b/pkgs/by-name/ba/back/contrib/config.json
@@ -0,0 +1,4 @@
+{
+    "source_code_repository_url": "https://git.foss-syndicate.org/vhack.eu/nixos-server/tree/pkgs/by-name/ba/back",
+    "repository_path": "/path/to/your/repository"
+}
diff --git a/pkgs/by-name/ba/back/src/cli.rs b/pkgs/by-name/ba/back/src/cli.rs
new file mode 100644
index 0000000..1820bf3
--- /dev/null
+++ b/pkgs/by-name/ba/back/src/cli.rs
@@ -0,0 +1,13 @@
+use std::path::PathBuf;
+
+use clap::Parser;
+
+#[derive(Parser, Debug)]
+#[clap(author, version, about, long_about = None)]
+#[allow(clippy::module_name_repetitions)]
+/// An extremely simple git issue tracking system.
+/// Inspired by tvix's panettone
+pub struct Cli {
+    /// The path to the configuration file. The file should be written in JSON.
+    pub config_file: PathBuf,
+}
diff --git a/pkgs/by-name/ba/back/src/config/mod.rs b/pkgs/by-name/ba/back/src/config/mod.rs
new file mode 100644
index 0000000..4986a41
--- /dev/null
+++ b/pkgs/by-name/ba/back/src/config/mod.rs
@@ -0,0 +1,58 @@
+use std::{
+    fs,
+    path::{Path, PathBuf},
+};
+
+use gix::ThreadSafeRepository;
+use serde::Deserialize;
+use url::Url;
+
+use crate::error::{self, Error};
+
+pub struct BackConfig {
+    // NOTE(@bpeetz): We do not need to html escape this, as the value must be a valid url. As such
+    // `<tags>` of all kinds _should_ be invalid.  <2024-12-26>
+    pub source_code_repository_url: Url,
+    pub repository: ThreadSafeRepository,
+}
+
+#[derive(Deserialize)]
+struct RawBackConfig {
+    source_code_repository_url: Url,
+    repository_path: PathBuf,
+}
+
+impl BackConfig {
+    pub fn from_config_file(path: &Path) -> error::Result<Self> {
+        let value = fs::read_to_string(path).map_err(|err| Error::ConfigRead {
+            file: path.to_owned(),
+            error: err,
+        })?;
+
+        let raw: RawBackConfig =
+            serde_json::from_str(&value).map_err(|err| Error::ConfigParse {
+                file: path.to_owned(),
+                error: err,
+            })?;
+
+        Self::try_from(raw)
+    }
+}
+
+impl TryFrom<RawBackConfig> for BackConfig {
+    type Error = error::Error;
+
+    fn try_from(value: RawBackConfig) -> Result<Self, Self::Error> {
+        let repository = {
+            ThreadSafeRepository::open(&value.repository_path).map_err(|err| Error::RepoOpen {
+                repository_path: value.repository_path,
+                error: Box::new(err),
+            })
+        }?;
+
+        Ok(Self {
+            repository,
+            source_code_repository_url: value.source_code_repository_url,
+        })
+    }
+}
diff --git a/pkgs/by-name/ba/back/src/error/mod.rs b/pkgs/by-name/ba/back/src/error/mod.rs
new file mode 100644
index 0000000..7e1c9cf
--- /dev/null
+++ b/pkgs/by-name/ba/back/src/error/mod.rs
@@ -0,0 +1,56 @@
+use std::{fmt::Display, io, path::PathBuf};
+
+use thiserror::Error;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Error, Debug)]
+pub enum Error {
+    ConfigParse {
+        file: PathBuf,
+        error: serde_json::Error,
+    },
+    ConfigRead {
+        file: PathBuf,
+        error: io::Error,
+    },
+    RepoOpen {
+        repository_path: PathBuf,
+        error: Box<gix::open::Error>,
+    },
+    RocketLaunch(#[from] rocket::Error),
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Error::ConfigParse { file, error } => {
+                write!(
+                    f,
+                    "while trying to parse the config file ({}): {error}",
+                    file.display()
+                )
+            }
+            Error::ConfigRead { file, error } => {
+                write!(
+                    f,
+                    "while trying to read the config file ({}): {error}",
+                    file.display()
+                )
+            }
+            Error::RepoOpen {
+                repository_path,
+                error,
+            } => {
+                write!(
+                    f,
+                    "while trying to open the repository ({}): {error}",
+                    repository_path.display()
+                )
+            }
+            Error::RocketLaunch(error) => {
+                write!(f, "while trying to start back: {error}")
+            }
+        }
+    }
+}
diff --git a/pkgs/by-name/ba/back/src/main.rs b/pkgs/by-name/ba/back/src/main.rs
index 86fe196..e8f36d2 100644
--- a/pkgs/by-name/ba/back/src/main.rs
+++ b/pkgs/by-name/ba/back/src/main.rs
@@ -9,62 +9,31 @@
 // 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::{env::args, path::PathBuf, process, sync::OnceLock};
+use clap::Parser;
+use config::BackConfig;
+use rocket::routes;
 
-use gix::ThreadSafeRepository;
-use rocket::{launch, routes};
-use url::Url;
+use crate::web::{closed, open, show_issue, styles};
 
-use crate::issues::{closed, open, show_issue, styles};
+mod cli;
+pub mod config;
+mod error;
+mod web;
 
-mod issues;
+#[rocket::main]
+async fn main() -> Result<(), error::Error> {
+    let args = cli::Cli::parse();
 
-static REPOSITORY: OnceLock<ThreadSafeRepository> = OnceLock::new();
-static SOURCE_CODE_REPOSITORY: OnceLock<Url> = OnceLock::new();
+    let config = BackConfig::from_config_file(&args.config_file)?;
 
-#[launch]
-fn rocket() -> _ {
-    let repository_path = {
-        let maybe_path = args().skip(1).rev().last();
-        if let Some(path) = maybe_path {
-            PathBuf::from(path)
-        } else {
-            eprintln!("Usage: back <issue repoitory>");
-            process::exit(1);
-        }
-    };
-    let source_code_url = {
-        match std::env::var("BACK_SOURCE_CODE_REPOSITORY_URL") {
-            Ok(value) => match Url::parse(&value) {
-                Ok(url) => url,
-                Err(err) => {
-                    eprintln!("Can't parse `BACK_SOURCE_CODE_REPOSITORY_URL` value as url: {err}");
-                    process::exit(1);
-                }
-            },
-            Err(err) => {
-                eprintln!("back needs you to specify a source code repositiory as `BACK_SOURCE_CODE_REPOSITORY_URL`: {err}");
-                process::exit(1);
-            }
-        }
-    };
+    rocket::build()
+        .mount("/", routes![open, closed, show_issue, styles])
+        .manage(config)
+        .ignite()
+        .await
+        .expect("This error should only happen on a miss-configuration.")
+        .launch()
+        .await?;
 
-    SOURCE_CODE_REPOSITORY
-        .set(source_code_url)
-        .expect("This should be unset by this stage");
-
-    REPOSITORY
-        .set(
-            ThreadSafeRepository::open(&repository_path).unwrap_or_else(|err| {
-                eprintln!(
-                    "Error while opening repository ('{}'): {} ",
-                    repository_path.display(),
-                    err
-                );
-                process::exit(1);
-            }),
-        )
-        .expect("There should be only one thread accessing this right now");
-
-    rocket::build().mount("/", routes![open, closed, show_issue, styles])
+    Ok(())
 }
diff --git a/pkgs/by-name/ba/back/src/issues/format/mod.rs b/pkgs/by-name/ba/back/src/web/format/mod.rs
index f78d3b3..f78d3b3 100644
--- a/pkgs/by-name/ba/back/src/issues/format/mod.rs
+++ b/pkgs/by-name/ba/back/src/web/format/mod.rs
diff --git a/pkgs/by-name/ba/back/src/issues/issue/mod.rs b/pkgs/by-name/ba/back/src/web/issue/mod.rs
index b78f473..79ef70f 100644
--- a/pkgs/by-name/ba/back/src/issues/issue/mod.rs
+++ b/pkgs/by-name/ba/back/src/web/issue/mod.rs
@@ -16,7 +16,7 @@ use gix::{bstr::ByteSlice, Commit, Id, ObjectId, Repository};
 use raw::{Operation, RawIssue};
 use rocket::response::content::RawHtml;
 
-use crate::SOURCE_CODE_REPOSITORY;
+use crate::config::BackConfig;
 
 use super::format::{BackString, Markdown};
 
@@ -114,12 +114,17 @@ impl<'a> Issue<'a> {
     pub fn from_commit_id(repo: &'a Repository, commit_id: ObjectId) -> Self {
         fn unwrap_id<'b>(repo: &Repository, id: &Commit<'b>) -> (RawIssue, Id<'b>) {
             let tree_obj = repo
-                .find_object(id.tree_id().unwrap())
+                .find_object(
+                    id.tree_id()
+                        .expect("All of git-bug's commits should have trees attached to them'"),
+                )
                 .expect("The object with this id should exist.")
                 .try_into_tree()
                 .expect("The git-bug's data model enforces this.");
 
-            let ops_ref = tree_obj.find_entry("ops").unwrap();
+            let ops_ref = tree_obj
+                .find_entry("ops")
+                .expect("All of git-bug's trees should contain a 'ops' json file");
 
             let issue_data = repo
                 .find_object(ops_ref.object_id())
@@ -237,7 +242,7 @@ impl<'a> Issue<'a> {
         ))
     }
 
-    pub fn to_html(&self) -> RawHtml<String> {
+    pub fn to_html(&self, config: &BackConfig) -> RawHtml<String> {
         let fmt_comments: String = self
             .comments
             .iter()
@@ -325,7 +330,7 @@ impl<'a> Issue<'a> {
    </body>
 </html>
 "#,
-                SOURCE_CODE_REPOSITORY.get().expect("This should be set")
+                config.source_code_repository_url
             ))
         }
     }
diff --git a/pkgs/by-name/ba/back/src/issues/issue/raw.rs b/pkgs/by-name/ba/back/src/web/issue/raw.rs
index 48d2a9f..bb447ec 100644
--- a/pkgs/by-name/ba/back/src/issues/issue/raw.rs
+++ b/pkgs/by-name/ba/back/src/web/issue/raw.rs
@@ -13,7 +13,7 @@ use gix::{bstr::ByteSlice, Repository};
 use serde::Deserialize;
 use serde_json::Value;
 
-use crate::issues::format::BackString;
+use crate::web::format::BackString;
 
 use super::{Author, Status};
 
diff --git a/pkgs/by-name/ba/back/src/issues/issue_show.rs b/pkgs/by-name/ba/back/src/web/issue_show.rs
index 638840e..638840e 100644
--- a/pkgs/by-name/ba/back/src/issues/issue_show.rs
+++ b/pkgs/by-name/ba/back/src/web/issue_show.rs
diff --git a/pkgs/by-name/ba/back/src/issues/mod.rs b/pkgs/by-name/ba/back/src/web/mod.rs
index 744d5ba..ed91e7e 100644
--- a/pkgs/by-name/ba/back/src/issues/mod.rs
+++ b/pkgs/by-name/ba/back/src/web/mod.rs
@@ -12,19 +12,17 @@
 use std::path::Path;
 
 use crate::{
-    issues::issue::{Issue, Status},
-    SOURCE_CODE_REPOSITORY,
+    config::BackConfig,
+    web::issue::{Issue, Status},
 };
-use format::BackString;
 use gix::{refs::Target, Repository};
 use issue_show::BackPrefix;
 use rocket::{
     get,
     response::content::{RawCss, RawHtml},
+    State,
 };
 
-use crate::REPOSITORY;
-
 mod format;
 mod issue;
 mod issue_show;
@@ -51,8 +49,12 @@ fn list_all_issues(repo: &'_ Repository) -> Vec<Issue<'_>> {
         .collect()
 }
 
-pub fn issue_list_boilerplate(wanted_status: Status, counter_status: Status) -> RawHtml<String> {
-    let repository = REPOSITORY.get().expect("This should have been set");
+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()
@@ -94,25 +96,22 @@ pub fn issue_list_boilerplate(wanted_status: Status, counter_status: Status) ->
    </body>
 </html>
 "#,
-        SOURCE_CODE_REPOSITORY.get().expect("This should be set")
+        config.source_code_repository_url
     ))
 }
 
 #[get("/issues/open")]
-pub fn open() -> RawHtml<String> {
-    issue_list_boilerplate(Status::Open, Status::Closed)
+pub fn open(config: &State<BackConfig>) -> RawHtml<String> {
+    issue_list_boilerplate(config, Status::Open, Status::Closed)
 }
 #[get("/issues/closed")]
-pub fn closed() -> RawHtml<String> {
-    issue_list_boilerplate(Status::Closed, Status::Open)
+pub fn closed(config: &State<BackConfig>) -> RawHtml<String> {
+    issue_list_boilerplate(config, 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();
+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
@@ -120,7 +119,7 @@ pub fn show_issue(prefix: BackPrefix) -> RawHtml<String> {
         .find(|issue| issue.id.to_string().starts_with(&prefix.to_string()));
 
     match maybe_issue {
-        Some(issue) => issue.to_html(),
+        Some(issue) => issue.to_html(config),
         None => RawHtml(format!("Issue with id '{prefix}' not found!")),
     }
 }