2m2d/src/tasks.rs

181 lines
4.5 KiB
Rust

use crate::ctx;
use crate::login_or_redirect;
use crate::Error;
use axum::extract::Extension;
use axum::extract::Form;
use axum::extract::OriginalUri;
use axum::extract::Path;
use axum::http::StatusCode;
use axum::response::Html;
use axum::response::Redirect;
use axum_sessions::extractors::ReadableSession;
use num_enum::FromPrimitive;
use serde::Deserialize;
use serde::Serialize;
use sqlx::FromRow;
use sqlx::{Pool, Postgres};
use std::sync::Arc;
use tera::Tera;
#[derive(FromRow, Serialize, Deserialize)]
pub struct RawTask {
pub id: i32,
pub title: String,
pub description: String,
pub status: i32,
}
#[derive(FromPrimitive, Serialize)]
#[repr(i32)]
pub enum Status {
#[default]
#[serde(rename = "To Do")]
ToDo,
#[serde(rename = "In Progress")]
InProgress,
Done,
}
#[derive(Serialize)]
pub struct Task {
pub id: i32,
pub title: String,
pub description: String,
pub status: Status,
}
impl From<RawTask> for Task {
fn from(t: RawTask) -> Self {
Self {
id: t.id,
title: t.title,
description: t.description,
status: Status::from(t.status),
}
}
}
pub async fn update_form(
Path(id): Path<i32>,
Extension(tera): Extension<Arc<Tera>>,
Extension(pool): Extension<Arc<Pool<Postgres>>>,
session: ReadableSession,
OriginalUri(uri): OriginalUri,
) -> Result<Html<String>, Error> {
let username = login_or_redirect!(session, "/login");
let (user_id,): (i32,) = sqlx::query_as("select id from users where username=$1")
.bind(&username)
.fetch_one(&*pool)
.await?;
let task: Option<RawTask> =
sqlx::query_as("select id,title,description,status from tasks where id=$1 and owner=$2")
.bind(id)
.bind(user_id)
.fetch_optional(&*pool)
.await?;
if let Some(task) = task {
let c = ctx! {
"task" => task,
"url" => uri.to_string()
};
let rendered = tera.render("tasks/update.html", &c)?;
#[allow(clippy::needless_return)] // it's a compile error, clippy
return Ok(Html(rendered));
}
Err(StatusCode::NOT_FOUND.into())
}
pub async fn create_form(
Extension(tera): Extension<Arc<Tera>>,
OriginalUri(uri): OriginalUri,
session: ReadableSession,
) -> Result<Html<String>, Error> {
let _username = login_or_redirect!(session, "/login");
let c = ctx! {
"url" => uri.to_string()
};
let rendered = tera.render("tasks/create.html", &c)?;
Ok(Html(rendered))
}
pub async fn update_backend(
Path(id): Path<i32>,
Extension(pool): Extension<Arc<Pool<Postgres>>>,
Form(data): Form<RawTask>,
) -> Result<Redirect, Error> {
sqlx::query("update tasks set title=$1,description=$2,status=$3 where id=$4")
.bind(&data.title)
.bind(&data.description)
.bind(data.status)
.bind(id)
.execute(&*pool)
.await?;
Ok(Redirect::to("/"))
}
pub async fn create_backend(
Extension(pool): Extension<Arc<Pool<Postgres>>>,
Form(data): Form<RawTask>,
session: ReadableSession,
) -> Result<Redirect, Error> {
let username = login_or_redirect!(session, "/login");
let (user_id,): (i32,) = sqlx::query_as("select id from users where username=$1")
.bind(&username)
.fetch_one(&*pool)
.await?;
sqlx::query("insert into tasks (owner,title, description, status) values ($1, $2, $3, $4)")
.bind(user_id)
.bind(&data.title)
.bind(&data.description)
.bind(data.status)
.execute(&*pool)
.await?;
Ok(Redirect::to("/"))
}
pub async fn task_detail(
Path(id): Path<i32>,
Extension(tera): Extension<Arc<Tera>>,
Extension(pool): Extension<Arc<Pool<Postgres>>>,
session: ReadableSession,
) -> Result<Html<String>, Error> {
let username = login_or_redirect!(session, "/login");
let (user_id,): (i32,) = sqlx::query_as("select id from users where username=$1")
.bind(&username)
.fetch_one(&*pool)
.await?;
let task: Option<RawTask> =
sqlx::query_as("select id,title,description,status from tasks where id=$1 and owner=$2")
.bind(id)
.bind(user_id)
.fetch_optional(&*pool)
.await?;
if let Some(task) = task.map(Task::from) {
let c = ctx! {
"task" => task
};
let rendered = tera.render("tasks/detail.html", &c)?;
return Ok(Html(rendered));
}
Err(StatusCode::NOT_FOUND.into())
}