traits for responses

This commit is contained in:
2023-09-16 00:45:51 +02:00
parent 7e62acf91a
commit 2221ee0412
6 changed files with 160 additions and 95 deletions

77
Cargo.lock generated
View File

@@ -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",
]

View File

@@ -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"

View File

@@ -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<crate::models::user::User>,
state: State<AppState>,
headers: HeaderMap,
path: Path<Self::UrlParams>,
form: Form<Self::FormX>,
) -> Result<Response<BoxBody>, crate::Error>;
}
}

View File

@@ -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<crate::models::user::User>,
axum::extract::State(state): axum::extract::State<AppState>,
headers: HeaderMap,
Path((trip_id,)): Path<(Uuid,)>,
Form(form): Form<TripTodoNew>,
) -> Result<Response<BoxBody>, crate::Error> {
let ctx = Context::build(current_user);
// method output is not required as we reload the whole trip todos anyway
let _todo_item = <Self as crud::Create>::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 {

View File

@@ -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(<crate::models::trips::todos::Todo as route::Create>::create),
)
.route("/:id/todo/:id/delete", post(trip_todo_delete)),
)
.nest(

View File

@@ -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<models::user::User>,
State(state): State<AppState>,
headers: HeaderMap,
Path(trip_id): Path<Uuid>,
Form(form): Form<TripTodoNew>,
) -> Result<impl IntoResponse, Error> {
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<models::user::User>,