html out of main

This commit is contained in:
2023-08-29 21:34:00 +02:00
parent 3a50f3e9f0
commit 0f66ec80ac
2 changed files with 92 additions and 135 deletions

5
rust/src/html.rs Normal file
View File

@@ -0,0 +1,5 @@
use maud::{html, Markup};
pub fn concat(a: Markup, b: Markup) -> Markup {
html!((a)(b))
}

View File

@@ -1,19 +1,11 @@
use axum::{ use axum::{
extract::{Path, Query, State}, extract::{Path, Query, State},
http::{ http::header::{self, HeaderMap, HeaderName, HeaderValue},
header,
header::{HeaderMap, HeaderName, HeaderValue},
StatusCode,
},
response::{IntoResponse, Redirect}, response::{IntoResponse, Redirect},
routing::{get, post}, routing::{get, post},
Form, Router, Form, Router,
}; };
use maud::html;
use maud::Markup;
use serde::Deserialize; use serde::Deserialize;
use uuid::Uuid; use uuid::Uuid;
@@ -22,6 +14,7 @@ use std::net::SocketAddr;
mod components; mod components;
mod error; mod error;
mod html;
mod models; mod models;
mod sqlite; mod sqlite;
@@ -203,12 +196,18 @@ async fn main() -> Result<(), StartError> {
.route("/item/:id/edit", post(inventory_item_edit)) .route("/item/:id/edit", post(inventory_item_edit))
.route("/item/name/validate", post(inventory_item_validate_name)), .route("/item/name/validate", post(inventory_item_validate_name)),
) )
.fallback(|| async { (StatusCode::NOT_FOUND, "no route found") }) .fallback(|| async {
Error::Request(RequestError::NotFound {
message: "no route found".to_string(),
})
})
.with_state(state); .with_state(state);
let addr = SocketAddr::from(([127, 0, 0, 1], args.port)); let addr = SocketAddr::from(([127, 0, 0, 1], args.port));
tracing::debug!("listening on {}", addr); tracing::debug!("listening on {}", addr);
axum::Server::bind(&addr) axum::Server::try_bind(&addr)
.map_err(|error| format!("error binding to {}: {}", addr, error))
.unwrap()
.serve(app.into_make_service()) .serve(app.into_make_service())
.await .await
.unwrap(); .unwrap();
@@ -216,13 +215,10 @@ async fn main() -> Result<(), StartError> {
Ok(()) Ok(())
} }
async fn root() -> (StatusCode, Markup) { async fn root() -> impl IntoResponse {
( components::Root::build(
StatusCode::OK, &components::home::Home::build(),
components::Root::build( &components::TopLevelPage::None,
&components::home::Home::build(),
&components::TopLevelPage::None,
),
) )
} }
@@ -235,7 +231,7 @@ async fn inventory_active(
State(mut state): State<AppState>, State(mut state): State<AppState>,
Path(id): Path<Uuid>, Path(id): Path<Uuid>,
Query(inventory_query): Query<InventoryQuery>, Query(inventory_query): Query<InventoryQuery>,
) -> Result<(StatusCode, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
state.client_state.edit_item = inventory_query.edit_item; state.client_state.edit_item = inventory_query.edit_item;
state.client_state.active_category_id = Some(id); state.client_state.active_category_id = Some(id);
@@ -255,38 +251,32 @@ async fn inventory_active(
}) })
.transpose()?; .transpose()?;
Ok(( Ok(components::Root::build(
StatusCode::OK, &components::inventory::Inventory::build(
components::Root::build( active_category,
&components::inventory::Inventory::build( &inventory.categories,
active_category, state.client_state.edit_item,
&inventory.categories,
state.client_state.edit_item,
),
&components::TopLevelPage::Inventory,
), ),
&components::TopLevelPage::Inventory,
)) ))
} }
async fn inventory_inactive( async fn inventory_inactive(
State(mut state): State<AppState>, State(mut state): State<AppState>,
Query(inventory_query): Query<InventoryQuery>, Query(inventory_query): Query<InventoryQuery>,
) -> Result<(StatusCode, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
state.client_state.edit_item = inventory_query.edit_item; state.client_state.edit_item = inventory_query.edit_item;
state.client_state.active_category_id = None; state.client_state.active_category_id = None;
let inventory = models::Inventory::load(&state.database_pool).await?; let inventory = models::Inventory::load(&state.database_pool).await?;
Ok(( Ok(components::Root::build(
StatusCode::OK, &components::inventory::Inventory::build(
components::Root::build( None,
&components::inventory::Inventory::build( &inventory.categories,
None, state.client_state.edit_item,
&inventory.categories,
state.client_state.edit_item,
),
&components::TopLevelPage::Inventory,
), ),
&components::TopLevelPage::Inventory,
)) ))
} }
@@ -311,12 +301,12 @@ struct NewItemName {
async fn inventory_item_validate_name( async fn inventory_item_validate_name(
State(state): State<AppState>, State(state): State<AppState>,
Form(new_item): Form<NewItemName>, Form(new_item): Form<NewItemName>,
) -> Result<(StatusCode, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
let exists = models::InventoryItem::name_exists(&state.database_pool, &new_item.name).await?; let exists = models::InventoryItem::name_exists(&state.database_pool, &new_item.name).await?;
Ok(( Ok(components::inventory::InventoryNewItemFormName::build(
StatusCode::OK, Some(&new_item.name),
components::inventory::InventoryNewItemFormName::build(Some(&new_item.name), exists), exists,
)) ))
} }
@@ -352,15 +342,12 @@ async fn inventory_item_create(
.unwrap(), .unwrap(),
); );
Ok(( Ok(components::inventory::Inventory::build(
StatusCode::OK, active_category,
components::inventory::Inventory::build( &inventory.categories,
active_category, state.client_state.edit_item,
&inventory.categories,
state.client_state.edit_item,
),
) )
.into_response()) .into_response())
} else { } else {
Ok(Redirect::to(&format!( Ok(Redirect::to(&format!(
"/inventory/category/{id}/", "/inventory/category/{id}/",
@@ -470,15 +457,12 @@ async fn trip_create(
Ok(Redirect::to(&format!("/trips/{new_id}/"))) Ok(Redirect::to(&format!("/trips/{new_id}/")))
} }
async fn trips(State(state): State<AppState>) -> Result<(StatusCode, Markup), Error> { async fn trips(State(state): State<AppState>) -> Result<impl IntoResponse, Error> {
let trips = models::Trip::all(&state.database_pool).await?; let trips = models::Trip::all(&state.database_pool).await?;
Ok(( Ok(components::Root::build(
StatusCode::OK, &components::trip::TripManager::build(trips),
components::Root::build( &components::TopLevelPage::Trips,
&components::trip::TripManager::build(trips),
&components::TopLevelPage::Trips,
),
)) ))
} }
@@ -492,7 +476,7 @@ async fn trip(
State(mut state): State<AppState>, State(mut state): State<AppState>,
Path(id): Path<Uuid>, Path(id): Path<Uuid>,
Query(trip_query): Query<TripQuery>, Query(trip_query): Query<TripQuery>,
) -> Result<(StatusCode, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
state.client_state.trip_edit_attribute = trip_query.edit; state.client_state.trip_edit_attribute = trip_query.edit;
state.client_state.active_category_id = trip_query.category; state.client_state.active_category_id = trip_query.category;
@@ -523,16 +507,13 @@ async fn trip(
}) })
.transpose()?; .transpose()?;
Ok(( Ok(components::Root::build(
StatusCode::OK, &components::trip::Trip::build(
components::Root::build( &trip,
&components::trip::Trip::build( state.client_state.trip_edit_attribute,
&trip, active_category,
state.client_state.trip_edit_attribute,
active_category,
),
&components::TopLevelPage::Trips,
), ),
&components::TopLevelPage::Trips,
)) ))
} }
@@ -624,7 +605,11 @@ async fn trip_item_set_state(
Ok(()) Ok(())
} }
async fn trip_row(state: &AppState, trip_id: Uuid, item_id: Uuid) -> Result<Markup, Error> { async fn trip_row(
state: &AppState,
trip_id: Uuid,
item_id: Uuid,
) -> Result<impl IntoResponse, Error> {
let item = models::TripItem::find(&state.database_pool, trip_id, item_id) let item = models::TripItem::find(&state.database_pool, trip_id, item_id)
.await? .await?
.ok_or_else(|| { .ok_or_else(|| {
@@ -651,7 +636,7 @@ async fn trip_row(state: &AppState, trip_id: Uuid, item_id: Uuid) -> Result<Mark
let category_row = let category_row =
components::trip::TripCategoryListRow::build(trip_id, &category, true, 0, true); components::trip::TripCategoryListRow::build(trip_id, &category, true, 0, true);
Ok(html!((item_row)(category_row))) Ok(html::concat(item_row, category_row))
} }
async fn trip_item_set_pick( async fn trip_item_set_pick(
@@ -675,7 +660,7 @@ async fn trip_item_set_pick(
async fn trip_item_set_pick_htmx( async fn trip_item_set_pick_htmx(
State(state): State<AppState>, State(state): State<AppState>,
Path((trip_id, item_id)): Path<(Uuid, Uuid)>, Path((trip_id, item_id)): Path<(Uuid, Uuid)>,
) -> Result<(StatusCode, HeaderMap, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
trip_item_set_state( trip_item_set_state(
&state, &state,
trip_id, trip_id,
@@ -689,11 +674,7 @@ async fn trip_item_set_pick_htmx(
HtmxResponseHeaders::Trigger.into(), HtmxResponseHeaders::Trigger.into(),
HtmxEvents::TripItemEdited.into(), HtmxEvents::TripItemEdited.into(),
); );
Ok(( Ok((headers, trip_row(&state, trip_id, item_id).await?))
StatusCode::OK,
headers,
trip_row(&state, trip_id, item_id).await?,
))
} }
async fn trip_item_set_unpick( async fn trip_item_set_unpick(
@@ -717,7 +698,7 @@ async fn trip_item_set_unpick(
async fn trip_item_set_unpick_htmx( async fn trip_item_set_unpick_htmx(
State(state): State<AppState>, State(state): State<AppState>,
Path((trip_id, item_id)): Path<(Uuid, Uuid)>, Path((trip_id, item_id)): Path<(Uuid, Uuid)>,
) -> Result<(StatusCode, HeaderMap, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
trip_item_set_state( trip_item_set_state(
&state, &state,
trip_id, trip_id,
@@ -731,11 +712,7 @@ async fn trip_item_set_unpick_htmx(
HtmxResponseHeaders::Trigger.into(), HtmxResponseHeaders::Trigger.into(),
HtmxEvents::TripItemEdited.into(), HtmxEvents::TripItemEdited.into(),
); );
Ok(( Ok((headers, trip_row(&state, trip_id, item_id).await?))
StatusCode::OK,
headers,
trip_row(&state, trip_id, item_id).await?,
))
} }
async fn trip_item_set_pack( async fn trip_item_set_pack(
@@ -759,7 +736,7 @@ async fn trip_item_set_pack(
async fn trip_item_set_pack_htmx( async fn trip_item_set_pack_htmx(
State(state): State<AppState>, State(state): State<AppState>,
Path((trip_id, item_id)): Path<(Uuid, Uuid)>, Path((trip_id, item_id)): Path<(Uuid, Uuid)>,
) -> Result<(StatusCode, HeaderMap, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
trip_item_set_state( trip_item_set_state(
&state, &state,
trip_id, trip_id,
@@ -773,11 +750,7 @@ async fn trip_item_set_pack_htmx(
HtmxResponseHeaders::Trigger.into(), HtmxResponseHeaders::Trigger.into(),
HtmxEvents::TripItemEdited.into(), HtmxEvents::TripItemEdited.into(),
); );
Ok(( Ok((headers, trip_row(&state, trip_id, item_id).await?))
StatusCode::OK,
headers,
trip_row(&state, trip_id, item_id).await?,
))
} }
async fn trip_item_set_unpack( async fn trip_item_set_unpack(
@@ -801,7 +774,7 @@ async fn trip_item_set_unpack(
async fn trip_item_set_unpack_htmx( async fn trip_item_set_unpack_htmx(
State(state): State<AppState>, State(state): State<AppState>,
Path((trip_id, item_id)): Path<(Uuid, Uuid)>, Path((trip_id, item_id)): Path<(Uuid, Uuid)>,
) -> Result<(StatusCode, HeaderMap, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
trip_item_set_state( trip_item_set_state(
&state, &state,
trip_id, trip_id,
@@ -815,22 +788,18 @@ async fn trip_item_set_unpack_htmx(
HtmxResponseHeaders::Trigger.into(), HtmxResponseHeaders::Trigger.into(),
HtmxEvents::TripItemEdited.into(), HtmxEvents::TripItemEdited.into(),
); );
Ok(( Ok((headers, trip_row(&state, trip_id, item_id).await?))
StatusCode::OK,
headers,
trip_row(&state, trip_id, item_id).await?,
))
} }
async fn trip_total_weight_htmx( async fn trip_total_weight_htmx(
State(state): State<AppState>, State(state): State<AppState>,
Path(trip_id): Path<Uuid>, Path(trip_id): Path<Uuid>,
) -> Result<(StatusCode, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
let total_weight = let total_weight =
models::Trip::find_total_picked_weight(&state.database_pool, trip_id).await?; models::Trip::find_total_picked_weight(&state.database_pool, trip_id).await?;
Ok(( Ok(components::trip::TripInfoTotalWeightRow::build(
StatusCode::OK, trip_id,
components::trip::TripInfoTotalWeightRow::build(trip_id, total_weight), total_weight,
)) ))
} }
@@ -869,11 +838,7 @@ async fn trip_state_set(
} }
if is_htmx(&headers) { if is_htmx(&headers) {
Ok(( Ok(components::trip::TripInfoStateRow::build(&new_state).into_response())
StatusCode::OK,
components::trip::TripInfoStateRow::build(&new_state),
)
.into_response())
} else { } else {
Ok(Redirect::to(&format!("/trips/{id}/", id = trip_id)).into_response()) Ok(Redirect::to(&format!("/trips/{id}/", id = trip_id)).into_response())
} }
@@ -894,17 +859,14 @@ struct TripTypeQuery {
async fn trips_types( async fn trips_types(
State(mut state): State<AppState>, State(mut state): State<AppState>,
Query(trip_type_query): Query<TripTypeQuery>, Query(trip_type_query): Query<TripTypeQuery>,
) -> Result<(StatusCode, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
state.client_state.trip_type_edit = trip_type_query.edit; state.client_state.trip_type_edit = trip_type_query.edit;
let trip_types: Vec<models::TripsType> = models::TripsType::all(&state.database_pool).await?; let trip_types: Vec<models::TripsType> = models::TripsType::all(&state.database_pool).await?;
Ok(( Ok(components::Root::build(
StatusCode::OK, &components::trip::types::TypeList::build(&state.client_state, trip_types),
components::Root::build( &components::TopLevelPage::Trips,
&components::trip::types::TypeList::build(&state.client_state, trip_types),
&components::TopLevelPage::Trips,
),
)) ))
} }
@@ -962,26 +924,23 @@ async fn trips_types_edit_name(
async fn inventory_item( async fn inventory_item(
State(state): State<AppState>, State(state): State<AppState>,
Path(id): Path<Uuid>, Path(id): Path<Uuid>,
) -> Result<(StatusCode, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
let item = models::InventoryItem::find(&state.database_pool, id) let item = models::InventoryItem::find(&state.database_pool, id)
.await? .await?
.ok_or(Error::Request(RequestError::NotFound { .ok_or(Error::Request(RequestError::NotFound {
message: format!("inventory item with id {id} not found"), message: format!("inventory item with id {id} not found"),
}))?; }))?;
Ok(( Ok(components::Root::build(
StatusCode::OK, &components::inventory::InventoryItem::build(&state.client_state, &item),
components::Root::build( &components::TopLevelPage::Inventory,
&components::inventory::InventoryItem::build(&state.client_state, &item),
&components::TopLevelPage::Inventory,
),
)) ))
} }
async fn trip_category_select( async fn trip_category_select(
State(state): State<AppState>, State(state): State<AppState>,
Path((trip_id, category_id)): Path<(Uuid, Uuid)>, Path((trip_id, category_id)): Path<(Uuid, Uuid)>,
) -> Result<(StatusCode, HeaderMap, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
let mut trip = models::Trip::find(&state.database_pool, trip_id) let mut trip = models::Trip::find(&state.database_pool, trip_id)
.await? .await?
.ok_or(Error::Request(RequestError::NotFound { .ok_or(Error::Request(RequestError::NotFound {
@@ -1005,7 +964,6 @@ async fn trip_category_select(
); );
Ok(( Ok((
StatusCode::OK,
headers, headers,
components::trip::TripItems::build(Some(active_category), &trip), components::trip::TripItems::build(Some(active_category), &trip),
)) ))
@@ -1014,7 +972,7 @@ async fn trip_category_select(
async fn inventory_category_select( async fn inventory_category_select(
State(state): State<AppState>, State(state): State<AppState>,
Path(category_id): Path<Uuid>, Path(category_id): Path<Uuid>,
) -> Result<(StatusCode, HeaderMap, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
let inventory = models::Inventory::load(&state.database_pool).await?; let inventory = models::Inventory::load(&state.database_pool).await?;
let active_category: Option<&models::Category> = Some( let active_category: Option<&models::Category> = Some(
@@ -1036,7 +994,6 @@ async fn inventory_category_select(
); );
Ok(( Ok((
StatusCode::OK,
headers, headers,
components::inventory::Inventory::build( components::inventory::Inventory::build(
active_category, active_category,
@@ -1049,7 +1006,7 @@ async fn inventory_category_select(
async fn trip_packagelist( async fn trip_packagelist(
State(state): State<AppState>, State(state): State<AppState>,
Path(trip_id): Path<Uuid>, Path(trip_id): Path<Uuid>,
) -> Result<(StatusCode, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
let mut trip = models::Trip::find(&state.database_pool, trip_id) let mut trip = models::Trip::find(&state.database_pool, trip_id)
.await? .await?
.ok_or(Error::Request(RequestError::NotFound { .ok_or(Error::Request(RequestError::NotFound {
@@ -1058,19 +1015,16 @@ async fn trip_packagelist(
trip.load_categories(&state.database_pool).await?; trip.load_categories(&state.database_pool).await?;
Ok(( Ok(components::Root::build(
StatusCode::OK, &components::trip::packagelist::TripPackageList::build(&trip),
components::Root::build( &components::TopLevelPage::Trips,
&components::trip::packagelist::TripPackageList::build(&trip),
&components::TopLevelPage::Trips,
),
)) ))
} }
async fn trip_item_packagelist_set_pack_htmx( async fn trip_item_packagelist_set_pack_htmx(
State(state): State<AppState>, State(state): State<AppState>,
Path((trip_id, item_id)): Path<(Uuid, Uuid)>, Path((trip_id, item_id)): Path<(Uuid, Uuid)>,
) -> Result<(StatusCode, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
trip_item_set_state( trip_item_set_state(
&state, &state,
trip_id, trip_id,
@@ -1086,16 +1040,15 @@ async fn trip_item_packagelist_set_pack_htmx(
message: format!("an item with id {item_id} does not exist"), message: format!("an item with id {item_id} does not exist"),
}))?; }))?;
Ok(( Ok(components::trip::packagelist::TripPackageListRow::build(
StatusCode::OK, trip_id, &item,
components::trip::packagelist::TripPackageListRow::build(trip_id, &item),
)) ))
} }
async fn trip_item_packagelist_set_unpack_htmx( async fn trip_item_packagelist_set_unpack_htmx(
State(state): State<AppState>, State(state): State<AppState>,
Path((trip_id, item_id)): Path<(Uuid, Uuid)>, Path((trip_id, item_id)): Path<(Uuid, Uuid)>,
) -> Result<(StatusCode, Markup), Error> { ) -> Result<impl IntoResponse, Error> {
trip_item_set_state( trip_item_set_state(
&state, &state,
trip_id, trip_id,
@@ -1113,8 +1066,7 @@ async fn trip_item_packagelist_set_unpack_htmx(
message: format!("an item with id {item_id} does not exist"), message: format!("an item with id {item_id} does not exist"),
}))?; }))?;
Ok(( Ok(components::trip::packagelist::TripPackageListRow::build(
StatusCode::OK, trip_id, &item,
components::trip::packagelist::TripPackageListRow::build(trip_id, &item),
)) ))
} }