Use extension trait for resolving paths

This commit is contained in:
Joscha 2024-05-12 14:52:23 +02:00
parent 5409e176b1
commit cf96b72dfb
10 changed files with 88 additions and 86 deletions

View file

@ -4,6 +4,7 @@ mod base;
mod link; mod link;
mod pages; mod pages;
pub mod paths; pub mod paths;
mod server_config_ext;
mod r#static; mod r#static;
use axum::{extract::DefaultBodyLimit, routing::get, Router}; use axum::{extract::DefaultBodyLimit, routing::get, Router};

View file

@ -11,11 +11,11 @@ use time::OffsetDateTime;
use crate::{ use crate::{
config::ServerConfig, config::ServerConfig,
server::web::{ server::web::{
base::Base,
paths::{ paths::{
PathAdminQueueAdd, PathAdminQueueAddBatch, PathAdminQueueDecrease, PathAdminQueueAdd, PathAdminQueueAddBatch, PathAdminQueueDecrease,
PathAdminQueueDelete, PathAdminQueueIncrease, PathQueue, PathAdminQueueDelete, PathAdminQueueIncrease, PathQueue,
}, },
server_config_ext::ServerConfigExt,
}, },
somehow, somehow,
}; };
@ -52,8 +52,7 @@ pub async fn post_admin_queue_add(
form.hash, form.priority, form.hash, form.priority,
); );
let link = Base::link_with_config(config, PathQueue {}); Ok(Redirect::to(config.path(PathQueue {}).as_ref()))
Ok(Redirect::to(&format!("{link}")))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -95,8 +94,7 @@ pub async fn post_admin_queue_add_batch(
); );
} }
let link = Base::link_with_config(config, PathQueue {}); Ok(Redirect::to(config.path(PathQueue {}).as_ref()))
Ok(Redirect::to(&format!("{link}")))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -116,8 +114,7 @@ pub async fn post_admin_queue_delete(
info!("Admin deleted {} from queue", form.hash); info!("Admin deleted {} from queue", form.hash);
let link = Base::link_with_config(config, PathQueue {}); Ok(Redirect::to(config.path(PathQueue {}).as_ref()))
Ok(Redirect::to(&format!("{link}")))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -140,8 +137,7 @@ pub async fn post_admin_queue_increase(
info!("Admin increased queue priority of {} by one", form.hash); info!("Admin increased queue priority of {} by one", form.hash);
let link = Base::link_with_config(config, PathQueue {}); Ok(Redirect::to(config.path(PathQueue {}).as_ref()))
Ok(Redirect::to(&format!("{link}")))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -164,6 +160,5 @@ pub async fn post_admin_queue_decrease(
info!("Admin decreased queue priority of {} by one", form.hash); info!("Admin decreased queue priority of {} by one", form.hash);
let link = Base::link_with_config(config, PathQueue {}); Ok(Redirect::to(config.path(PathQueue {}).as_ref()))
Ok(Redirect::to(&format!("{link}")))
} }

View file

@ -13,8 +13,8 @@ use tokio::sync::mpsc;
use crate::{ use crate::{
config::ServerConfig, config::ServerConfig,
server::web::{ server::web::{
base::Base,
paths::{PathAdminRefsTrack, PathAdminRefsUntrack, PathAdminRefsUpdate, PathIndex}, paths::{PathAdminRefsTrack, PathAdminRefsUntrack, PathAdminRefsUpdate, PathIndex},
server_config_ext::ServerConfigExt,
}, },
somehow, somehow,
}; };
@ -38,8 +38,7 @@ pub async fn post_admin_refs_track(
info!("Admin tracked {}", form.r#ref); info!("Admin tracked {}", form.r#ref);
} }
let link = Base::link_with_config(config, PathIndex {}); Ok(Redirect::to(config.path(PathIndex {}).as_ref()))
Ok(Redirect::to(&link.to_string()))
} }
pub async fn post_admin_refs_untrack( pub async fn post_admin_refs_untrack(
@ -56,8 +55,7 @@ pub async fn post_admin_refs_untrack(
info!("Admin untracked {}", form.r#ref); info!("Admin untracked {}", form.r#ref);
} }
let link = Base::link_with_config(config, PathIndex {}); Ok(Redirect::to(config.path(PathIndex {}).as_ref()))
Ok(Redirect::to(&link.to_string()))
} }
pub async fn post_admin_repo_update( pub async fn post_admin_repo_update(
@ -68,6 +66,5 @@ pub async fn post_admin_repo_update(
let _ = recurring_tx.send(()); let _ = recurring_tx.send(());
info!("Admin updated repo"); info!("Admin updated repo");
let link = Base::link_with_config(config, PathIndex {}); Ok(Redirect::to(config.path(PathIndex {}).as_ref()))
Ok(Redirect::to(&link.to_string()))
} }

View file

@ -1,5 +1,3 @@
use std::fmt;
use maud::{html, Markup, DOCTYPE}; use maud::{html, Markup, DOCTYPE};
use crate::config::ServerConfig; use crate::config::ServerConfig;
@ -7,6 +5,7 @@ use crate::config::ServerConfig;
use super::{ use super::{
paths::{PathGraph, PathIndex, PathQueue}, paths::{PathGraph, PathIndex, PathQueue},
r#static::{BASE_CSS, LOGO_SVG}, r#static::{BASE_CSS, LOGO_SVG},
server_config_ext::{AbsPath, ServerConfigExt},
}; };
pub enum Tab { pub enum Tab {
@ -18,18 +17,17 @@ pub enum Tab {
#[derive(Clone)] #[derive(Clone)]
pub struct Base { pub struct Base {
pub link_logo_svg: Link, pub link_logo_svg: AbsPath,
pub link_base_css: Link, pub link_base_css: AbsPath,
pub link_index: Link, pub link_index: AbsPath,
pub link_graph: Link, pub link_graph: AbsPath,
pub link_queue: Link, pub link_queue: AbsPath,
pub web_base: String, pub config: &'static ServerConfig,
pub repo_name: String,
pub tab: &'static str, pub tab: &'static str,
} }
impl Base { impl Base {
pub fn new(config: &ServerConfig, tab: Tab) -> Self { pub fn new(config: &'static ServerConfig, tab: Tab) -> Self {
let tab = match tab { let tab = match tab {
Tab::None => "", Tab::None => "",
Tab::Index => "index", Tab::Index => "index",
@ -37,32 +35,16 @@ impl Base {
Tab::Queue => "queue", Tab::Queue => "queue",
}; };
Self { Self {
link_logo_svg: Self::link_with_config(config, LOGO_SVG), link_logo_svg: config.path(LOGO_SVG),
link_base_css: Self::link_with_config(config, BASE_CSS), link_base_css: config.path(BASE_CSS),
link_index: Self::link_with_config(config, PathIndex {}), link_index: config.path(PathIndex {}),
link_graph: Self::link_with_config(config, PathGraph {}), link_graph: config.path(PathGraph {}),
link_queue: Self::link_with_config(config, PathQueue {}), link_queue: config.path(PathQueue {}),
web_base: config.web_base.clone(), config,
repo_name: config.repo_name.clone(),
tab, tab,
} }
} }
fn link_with_base<P: fmt::Display>(base: &str, to: P) -> Link {
let to = format!("{to}");
assert!(!base.ends_with('/'));
assert!(to.starts_with('/'));
Link(format!("{base}{to}"))
}
pub fn link_with_config<P: fmt::Display>(config: &ServerConfig, to: P) -> Link {
Self::link_with_base(&config.web_base, to)
}
pub fn link<P: fmt::Display>(&self, to: P) -> Link {
Self::link_with_base(&self.web_base, to)
}
pub fn html(&self, title: &str, head: Markup, body: Markup) -> Markup { pub fn html(&self, title: &str, head: Markup, body: Markup) -> Markup {
html!( html!(
(DOCTYPE) (DOCTYPE)
@ -70,7 +52,7 @@ impl Base {
head { head {
meta charset="utf-8"; meta charset="utf-8";
meta name="viewport" content="width=device-width"; meta name="viewport" content="width=device-width";
title { (title) " - " (self.repo_name) } title { (title) " - " (self.config.repo_name) }
link rel="icon" href=(self.link_logo_svg); link rel="icon" href=(self.link_logo_svg);
link rel="stylesheet" href=(self.link_base_css); link rel="stylesheet" href=(self.link_base_css);
(head) (head)
@ -79,7 +61,7 @@ impl Base {
nav { nav {
a .current[self.tab == "index"] href=(self.link_index) { a .current[self.tab == "index"] href=(self.link_index) {
img src=(self.link_logo_svg) alt=""; img src=(self.link_logo_svg) alt="";
(self.repo_name) (self.config.repo_name)
} }
a .current[self.tab == "graph"] href=(self.link_graph) { "graph" } a .current[self.tab == "graph"] href=(self.link_graph) { "graph" }
a .current[self.tab == "queue"] href=(self.link_queue) { "queue" } a .current[self.tab == "queue"] href=(self.link_queue) { "queue" }
@ -90,12 +72,3 @@ impl Base {
) )
} }
} }
#[derive(Clone)]
pub struct Link(String);
impl fmt::Display for Link {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

View file

@ -4,12 +4,13 @@ use time::OffsetDateTime;
use crate::server::util; use crate::server::util;
use super::{ use super::{
base::{Base, Link}, base::Base,
paths::{PathCommitByHash, PathRunById, PathWorkerByName}, paths::{PathCommitByHash, PathRunById, PathWorkerByName},
server_config_ext::{AbsPath, ServerConfigExt},
}; };
pub struct LinkCommit { pub struct LinkCommit {
link: Link, link: AbsPath,
short: String, short: String,
reachable: i64, reachable: i64,
} }
@ -18,7 +19,7 @@ impl LinkCommit {
pub fn new(base: &Base, hash: String, message: &str, reachable: i64) -> Self { pub fn new(base: &Base, hash: String, message: &str, reachable: i64) -> Self {
Self { Self {
short: util::format_commit_short(&hash, message), short: util::format_commit_short(&hash, message),
link: base.link(PathCommitByHash { hash }), link: base.config.path(PathCommitByHash { hash }),
reachable, reachable,
} }
} }
@ -53,14 +54,14 @@ impl LinkCommit {
} }
pub struct LinkRunShort { pub struct LinkRunShort {
link: Link, link: AbsPath,
short: String, short: String,
} }
impl LinkRunShort { impl LinkRunShort {
pub fn new(base: &Base, id: String, hash: &str, message: &str) -> Self { pub fn new(base: &Base, id: String, hash: &str, message: &str) -> Self {
Self { Self {
link: base.link(PathRunById { id }), link: base.config.path(PathRunById { id }),
short: util::format_commit_short(hash, message), short: util::format_commit_short(hash, message),
} }
} }
@ -73,14 +74,14 @@ impl LinkRunShort {
} }
pub struct LinkRunDate { pub struct LinkRunDate {
link: Link, link: AbsPath,
date: String, // TODO base.date(...)? date: String, // TODO base.date(...)?
} }
impl LinkRunDate { impl LinkRunDate {
pub fn new(base: &Base, id: String, start: OffsetDateTime) -> Self { pub fn new(base: &Base, id: String, start: OffsetDateTime) -> Self {
Self { Self {
link: base.link(PathRunById { id }), link: base.config.path(PathRunById { id }),
date: util::format_time(start), date: util::format_time(start),
} }
} }
@ -93,14 +94,14 @@ impl LinkRunDate {
} }
pub struct LinkWorker { pub struct LinkWorker {
link: Link, link: AbsPath,
name: String, name: String,
} }
impl LinkWorker { impl LinkWorker {
pub fn new(base: &Base, name: String) -> Self { pub fn new(base: &Base, name: String) -> Self {
Self { Self {
link: base.link(PathWorkerByName { name: name.clone() }), link: base.config.path(PathWorkerByName { name: name.clone() }),
name, name,
} }
} }

View file

@ -15,6 +15,7 @@ use crate::{
base::{Base, Tab}, base::{Base, Tab},
link::{LinkCommit, LinkRunDate}, link::{LinkCommit, LinkRunDate},
paths::{PathAdminQueueAdd, PathCommitByHash}, paths::{PathAdminQueueAdd, PathCommitByHash},
server_config_ext::ServerConfigExt,
}, },
}, },
somehow, somehow,
@ -145,7 +146,7 @@ pub async fn get_commit_by_hash(
} }
} }
} }
form method="post" action=(base.link(PathAdminQueueAdd {})) { form method="post" action=(config.path(PathAdminQueueAdd {})) {
input type="hidden" name="hash" value=(commit.hash); input type="hidden" name="hash" value=(commit.hash);
button { "Add to queue" } " with a " button { "Add to queue" } " with a "
label for="priority" { "priority" } " of " label for="priority" { "priority" } " of "

View file

@ -15,6 +15,7 @@ use crate::{
base::{Base, Tab}, base::{Base, Tab},
paths::{PathGraph, PathGraphCommits, PathGraphMeasurements, PathGraphMetrics}, paths::{PathGraph, PathGraphCommits, PathGraphMeasurements, PathGraphMetrics},
r#static::{GRAPH_JS, UPLOT_CSS}, r#static::{GRAPH_JS, UPLOT_CSS},
server_config_ext::ServerConfigExt,
}, },
}, },
somehow, somehow,
@ -29,8 +30,8 @@ pub async fn get_graph(
Ok(base.html( Ok(base.html(
"graph", "graph",
html! { html! {
link rel="stylesheet" href=(base.link(UPLOT_CSS)); link rel="stylesheet" href=(config.path(UPLOT_CSS));
script type="module" src=(base.link(GRAPH_JS)) {} script type="module" src=(config.path(GRAPH_JS)) {}
}, },
html! { html! {
h2 { "Graph" } h2 { "Graph" }

View file

@ -9,6 +9,7 @@ use crate::{
base::{Base, Tab}, base::{Base, Tab},
link::LinkCommit, link::LinkCommit,
paths::{PathAdminRefsTrack, PathAdminRefsUntrack, PathAdminRefsUpdate, PathIndex}, paths::{PathAdminRefsTrack, PathAdminRefsUntrack, PathAdminRefsUpdate, PathIndex},
server_config_ext::ServerConfigExt,
}, },
somehow, somehow,
}; };
@ -60,7 +61,7 @@ pub async fn get_index(
h2 { "Refs" } h2 { "Refs" }
details .refs-list open { details .refs-list open {
summary { "Tracked (" (tracked_refs.len()) ")" } summary { "Tracked (" (tracked_refs.len()) ")" }
form method="post" action=(base.link(PathAdminRefsUntrack {})) { form method="post" action=(config.path(PathAdminRefsUntrack {})) {
dl { dl {
@for r#ref in tracked_refs { @for r#ref in tracked_refs {
dt { dt {
@ -75,7 +76,7 @@ pub async fn get_index(
} }
details .refs-list { details .refs-list {
summary { "Untracked (" (untracked_refs.len()) ")" } summary { "Untracked (" (untracked_refs.len()) ")" }
form method="post" action=(base.link(PathAdminRefsTrack {})) { form method="post" action=(config.path(PathAdminRefsTrack {})) {
dl { dl {
@for r#ref in untracked_refs { @for r#ref in untracked_refs {
dt { dt {
@ -88,7 +89,7 @@ pub async fn get_index(
} }
} }
} }
form method="post" action=(base.link(PathAdminRefsUpdate {})) { form method="post" action=(config.path(PathAdminRefsUpdate {})) {
button { "Update" } button { "Update" }
} }
}, },

View file

@ -17,13 +17,14 @@ use crate::{
server::{ server::{
util, util,
web::{ web::{
base::{Base, Link, Tab}, base::{Base, Tab},
link::{LinkCommit, LinkRunShort, LinkWorker}, link::{LinkCommit, LinkRunShort, LinkWorker},
paths::{ paths::{
PathAdminQueueAddBatch, PathAdminQueueDecrease, PathAdminQueueDelete, PathAdminQueueAddBatch, PathAdminQueueDecrease, PathAdminQueueDelete,
PathAdminQueueIncrease, PathQueue, PathQueueDelete, PathQueueInner, PathAdminQueueIncrease, PathQueue, PathQueueDelete, PathQueueInner,
}, },
r#static::QUEUE_JS, r#static::QUEUE_JS,
server_config_ext::{AbsPath, ServerConfigExt},
}, },
workers::{WorkerInfo, Workers}, workers::{WorkerInfo, Workers},
}, },
@ -43,9 +44,9 @@ struct Worker {
} }
struct Task { struct Task {
link_delete: Link, link_delete: AbsPath,
link_increase: Link, link_increase: AbsPath,
link_decrease: Link, link_decrease: AbsPath,
hash: String, hash: String,
commit: LinkCommit, commit: LinkCommit,
since: String, since: String,
@ -125,11 +126,11 @@ async fn get_queue_data(
.fetch(db) .fetch(db)
.map_ok(|r| Task { .map_ok(|r| Task {
workers: workers_by_commit.remove(&r.hash).unwrap_or_default(), workers: workers_by_commit.remove(&r.hash).unwrap_or_default(),
link_delete: base.link(PathQueueDelete { link_delete: base.config.path(PathQueueDelete {
hash: r.hash.clone(), hash: r.hash.clone(),
}), }),
link_increase: base.link(PathAdminQueueIncrease {}), link_increase: base.config.path(PathAdminQueueIncrease {}),
link_decrease: base.link(PathAdminQueueDecrease {}), link_decrease: base.config.path(PathAdminQueueDecrease {}),
hash: r.hash.clone(), hash: r.hash.clone(),
commit: LinkCommit::new(base, r.hash, &r.message, r.reachable), commit: LinkCommit::new(base, r.hash, &r.message, r.reachable),
since: util::format_delta_from_now(r.date), since: util::format_delta_from_now(r.date),
@ -246,11 +247,11 @@ pub async fn get_queue(
Ok(base.html( Ok(base.html(
&format!("queue ({})", tasks.len()), &format!("queue ({})", tasks.len()),
html! { html! {
script type="module" src=(base.link(QUEUE_JS)) {} script type="module" src=(config.path(QUEUE_JS)) {}
}, },
html! { html! {
div #inner { (page_inner(workers, tasks)) } div #inner { (page_inner(workers, tasks)) }
form method="post" action=(base.link(PathAdminQueueAddBatch {})) { form method="post" action=(config.path(PathAdminQueueAddBatch {})) {
label { label {
"Batch size: " "Batch size: "
input name="amount" type="number" value="10" min="1"; input name="amount" type="number" value="10" min="1";
@ -299,7 +300,7 @@ pub async fn get_queue_delete(
p { "You are about to delete this commit from the queue:" } p { "You are about to delete this commit from the queue:" }
p { (commit.html()) } p { (commit.html()) }
p { "All runs of this commit currently in progress will be aborted!" } p { "All runs of this commit currently in progress will be aborted!" }
form method="post" action=(base.link(PathAdminQueueDelete {})) { form method="post" action=(config.path(PathAdminQueueDelete {})) {
input name="hash" type="hidden" value=(r.hash); input name="hash" type="hidden" value=(r.hash);
button { "Delete commit and abort runs" } button { "Delete commit and abort runs" }
} }

View file

@ -0,0 +1,31 @@
use std::fmt;
use crate::config::ServerConfig;
pub trait ServerConfigExt {
fn path<T: fmt::Display>(&self, to: T) -> AbsPath;
}
impl ServerConfigExt for ServerConfig {
fn path<T: fmt::Display>(&self, to: T) -> AbsPath {
let to = to.to_string();
assert!(to.starts_with('/'));
AbsPath(format!("{}{to}", self.web_base))
}
}
/// An absolute path to a resource on the web server.
#[derive(Clone)]
pub struct AbsPath(String);
impl fmt::Display for AbsPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl AsRef<str> for AbsPath {
fn as_ref(&self) -> &str {
&self.0
}
}