traits for responses
This commit is contained in:
@@ -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>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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>,
|
||||
|
||||
Reference in New Issue
Block a user