diff options
author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-12-26 10:00:45 +0100 |
---|---|---|
committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-12-26 10:01:56 +0100 |
commit | a3111a5d214db66b7d676940b8f8bfda5074bd45 (patch) | |
tree | a68cc1458386052a7c97a07b69bd161866ad7046 /pkgs/by-name/ba/back/src | |
parent | fix(hosts/server2): Use correct path to `vhack.eu/nixos-server` repo (diff) | |
download | nixos-server-a3111a5d214db66b7d676940b8f8bfda5074bd45.tar.gz nixos-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.rs | 13 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/config/mod.rs | 58 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/error/mod.rs | 56 | ||||
-rw-r--r-- | pkgs/by-name/ba/back/src/main.rs | 73 | ||||
-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!")), } } |