aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/ba/back/src
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-12-26 10:00:45 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-12-26 10:01:56 +0100
commita3111a5d214db66b7d676940b8f8bfda5074bd45 (patch)
treea68cc1458386052a7c97a07b69bd161866ad7046 /pkgs/by-name/ba/back/src
parentfix(hosts/server2): Use correct path to `vhack.eu/nixos-server` repo (diff)
downloadnixos-server-a3111a5d214db66b7d676940b8f8bfda5074bd45.zip
fix(pkgs/back): Use rocket to manage the configuration values
This reduces the amount of needed `unwraps`/`expects` and allows us to streamline the parsing processes.
Diffstat (limited to 'pkgs/by-name/ba/back/src')
-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
9 files changed, 176 insertions, 76 deletions
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!")),
}
}