From 2221ee041200ac5ae9bff9330efcb8f2bf8e29c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20K=C3=B6rber?= Date: Sat, 16 Sep 2023 00:45:51 +0200 Subject: [PATCH] traits for responses --- Cargo.lock | 77 +++++++++++++++++++++++---------------- Cargo.toml | 9 +---- src/components/mod.rs | 35 +++++++++++++++++- src/models/trips/todos.rs | 77 +++++++++++++++++++++++++++++++++++++-- src/routing/mod.rs | 7 +++- src/routing/routes.rs | 50 +------------------------ 6 files changed, 160 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3481d32..955144f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,7 +106,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.35", ] [[package]] @@ -132,6 +132,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", + "axum-macros", "bitflags 1.3.2", "bytes", "futures-util", @@ -174,6 +175,18 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-macros" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.35", +] + [[package]] name = "axum-prometheus" version = "0.4.0" @@ -256,9 +269,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" @@ -289,9 +302,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.2" +version = "4.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" +checksum = "84ed82781cea27b43c9b106a979fe450a13a31aab0500595fb3fc06616de08e6" dependencies = [ "clap_builder", "clap_derive", @@ -318,7 +331,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.35", ] [[package]] @@ -667,7 +680,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.35", ] [[package]] @@ -1033,9 +1046,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libm" @@ -1185,7 +1198,7 @@ checksum = "ddece26afd34c31585c74a4db0630c376df271c285d682d1e55012197830b6df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.35", ] [[package]] @@ -1519,7 +1532,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.35", ] [[package]] @@ -1599,9 +1612,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -1833,9 +1846,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.4" +version = "0.101.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" +checksum = "45a27e3b59326c16e23d30aeb7a36a24cc0d29e71d68ff611cdfb4a01d013bed" dependencies = [ "ring", "untrusted", @@ -1886,14 +1899,14 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.35", ] [[package]] name = "serde_json" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -2005,9 +2018,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" dependencies = [ "libc", "windows-sys", @@ -2287,9 +2300,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.32" +version = "2.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "59bf04c28bee9043ed9ea1e41afc0552288d3aba9c6efdd78903b802926f4879" dependencies = [ "proc-macro2", "quote", @@ -2332,7 +2345,7 @@ checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.35", ] [[package]] @@ -2422,7 +2435,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.3", + "socket2 0.5.4", "tokio-macros", "tracing", "windows-sys", @@ -2446,7 +2459,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.35", ] [[package]] @@ -2575,7 +2588,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.35", ] [[package]] @@ -2654,9 +2667,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" @@ -2666,9 +2679,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -2784,7 +2797,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.35", "wasm-bindgen-shared", ] @@ -2806,7 +2819,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.35", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index 7e6290e..8d8a171 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,7 @@ name = "packager" path = "src/main.rs" [features] -jaeger = [ - "dep:opentelemetry", - "dep:tracing-opentelemetry", - "dep:opentelemetry-jaeger", - "tokio/tracing" -] +jaeger = ["dep:opentelemetry", "dep:tracing-opentelemetry", "dep:opentelemetry-jaeger", "tokio/tracing"] prometheus = ["dep:axum-prometheus"] tokio-console = ["dep:console-subscriber"] @@ -54,7 +49,7 @@ features = ["derive"] [dependencies.axum] version = "0.6" -features = ["headers"] +features = ["headers", "macros"] [dependencies.tokio] version = "1" diff --git a/src/components/mod.rs b/src/components/mod.rs index c1c27c5..5b37cfa 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -75,7 +75,6 @@ pub mod crud { T: sqlx::Acquire<'c, Database = sqlx::Sqlite> + Send + std::fmt::Debug; async fn delete_all<'c>( - // &self, ctx: &Context, pool: &'c sqlite::Pool, filter: Self::Filter, @@ -109,3 +108,37 @@ pub mod view { fn build(&self, input: Self::Input) -> Markup; } } + +pub mod route { + use async_trait::async_trait; + + use crate::AppState; + use axum::{ + body::BoxBody, + extract::{Path, State}, + http::HeaderMap, + response::Response, + Extension, Form, + }; + + pub enum Method { + Get, + Post, + } + + #[async_trait] + pub trait Create: super::crud::Create { + type FormX: Send + Sync + 'static; + type UrlParams: Send + Sync + 'static; + + const URL: &'static str; + + async fn create( + user: Extension, + state: State, + headers: HeaderMap, + path: Path, + form: Form, + ) -> Result, crate::Error>; + } +} diff --git a/src/models/trips/todos.rs b/src/models/trips/todos.rs index 7b1bb76..3cfcc5a 100644 --- a/src/models/trips/todos.rs +++ b/src/models/trips/todos.rs @@ -1,13 +1,26 @@ +use axum::{ + body::BoxBody, + extract::{Form, Path}, + http::HeaderMap, + response::{IntoResponse, Redirect, Response}, + Extension, +}; use maud::{html, Markup}; +use serde::Deserialize; use uuid::Uuid; -use crate::components::crud; -use crate::components::view::{self, *}; +use crate::{ + components::{ + crud, route, + view::{self, *}, + }, + htmx, + models::Error, + sqlite, AppState, Context, RequestError, +}; use async_trait::async_trait; -use crate::{models::Error, sqlite, Context}; - use super::Trip; #[derive(Debug, PartialEq, Eq)] @@ -545,6 +558,62 @@ impl view::View for Todo { } } +#[derive(Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct TripTodoNew { + #[serde(rename = "new-todo-description")] + description: String, +} + +#[async_trait] +impl route::Create for Todo { + type FormX = TripTodoNew; + type UrlParams = (Uuid,); + + const URL: &'static str = "/:id/todo/new"; + + #[tracing::instrument] + async fn create( + Extension(current_user): Extension, + axum::extract::State(state): axum::extract::State, + headers: HeaderMap, + Path((trip_id,)): Path<(Uuid,)>, + Form(form): Form, + ) -> Result, crate::Error> { + let ctx = Context::build(current_user); + // method output is not required as we reload the whole trip todos anyway + let _todo_item = ::create( + &ctx, + &state.database_pool, + TodoFilter { trip_id }, + TodoNew { + description: form.description, + }, + ) + .await?; + + if htmx::is_htmx(&headers) { + let trip = Trip::find(&ctx, &state.database_pool, trip_id).await?; + match trip { + None => Err(crate::Error::Request(RequestError::NotFound { + message: format!("trip with id {trip_id} not found"), + })), + Some(mut trip) => { + trip.load_todos(&ctx, &state.database_pool).await?; + Ok(crate::models::trips::todos::TodoList { + trip: &trip, + todos: &trip.todos(), + } + .build(None) + .into_response()) + } + } + } else { + Ok(Redirect::to(&format!("/trips/{trip_id}/")).into_response()) + } + } +} + pub struct NewTodo; impl NewTodo { diff --git a/src/routing/mod.rs b/src/routing/mod.rs index 21ce935..e4392f5 100644 --- a/src/routing/mod.rs +++ b/src/routing/mod.rs @@ -12,7 +12,7 @@ use uuid::Uuid; use std::{fmt, time::Duration}; use tower::{timeout::TimeoutLayer, ServiceBuilder}; -use crate::{AppState, Error, RequestError, TopLevelPage}; +use crate::{components::route, AppState, Error, RequestError, TopLevelPage}; use super::auth; @@ -151,7 +151,10 @@ pub fn router(state: AppState) -> Router { .route("/:id/todo/:id/edit", post(trip_todo_edit)) .route("/:id/todo/:id/edit/save", post(trip_todo_edit_save)) .route("/:id/todo/:id/edit/cancel", post(trip_todo_edit_cancel)) - .route("/:id/todo/new", post(trip_todo_new)) + .route( + "/:id/todo/new", + post(::create), + ) .route("/:id/todo/:id/delete", post(trip_todo_delete)), ) .nest( diff --git a/src/routing/routes.rs b/src/routing/routes.rs index abd9ccb..8fc7c41 100644 --- a/src/routing/routes.rs +++ b/src/routing/routes.rs @@ -7,7 +7,7 @@ use axum::{ use crate::components::crud::*; use crate::components::view::*; -use crate::models::trips::todos::{TodoBuildInput, TodoFilter, TodoNew, TodoUpdate}; +use crate::models::trips::todos::{TodoBuildInput, TodoFilter, TodoUpdate}; use crate::view::Component; @@ -1480,54 +1480,6 @@ pub async fn trip_todo_edit_cancel( } } -#[derive(Deserialize, Debug)] -#[serde(deny_unknown_fields)] -pub struct TripTodoNew { - #[serde(rename = "new-todo-description")] - description: String, -} - -#[tracing::instrument] -pub async fn trip_todo_new( - Extension(current_user): Extension, - State(state): State, - headers: HeaderMap, - Path(trip_id): Path, - Form(form): Form, -) -> Result { - let ctx = Context::build(current_user); - // method output is not required as we reload the whole trip todos anyway - let _todo_item = models::trips::todos::Todo::create( - &ctx, - &state.database_pool, - TodoFilter { trip_id }, - TodoNew { - description: form.description, - }, - ) - .await?; - - if htmx::is_htmx(&headers) { - let trip = models::trips::Trip::find(&ctx, &state.database_pool, trip_id).await?; - match trip { - None => Err(Error::Request(RequestError::NotFound { - message: format!("trip with id {trip_id} not found"), - })), - Some(mut trip) => { - trip.load_todos(&ctx, &state.database_pool).await?; - Ok(models::trips::todos::TodoList { - trip: &trip, - todos: &trip.todos(), - } - .build(None) - .into_response()) - } - } - } else { - Ok(Redirect::to(&format!("/trips/{trip_id}/")).into_response()) - } -} - #[tracing::instrument] pub async fn trip_todo_delete( Extension(current_user): Extension,