106 lines
3 KiB
Rust
106 lines
3 KiB
Rust
use axum::{
|
|
Json, Router,
|
|
extract::{Path, State},
|
|
http::StatusCode,
|
|
routing::{get, post},
|
|
};
|
|
use axum_extra::extract::WithRejection;
|
|
use serde::Deserialize;
|
|
use sqlx::{Pool, Sqlite};
|
|
use uuid::Uuid;
|
|
|
|
use crate::models::{ProjectModel, ProjectStatus};
|
|
|
|
use super::AppError;
|
|
|
|
pub fn create_project_router() -> Router<Pool<Sqlite>> {
|
|
Router::new()
|
|
.route("/", post(create_project).get(list_projects))
|
|
.route(
|
|
"/{project_id}",
|
|
get(get_project).put(update_project).delete(delete_project),
|
|
)
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
pub struct CreateProjectRequest {
|
|
title: String,
|
|
color: String,
|
|
}
|
|
|
|
pub async fn create_project(
|
|
State(pool): State<Pool<Sqlite>>,
|
|
WithRejection(Json(input), _): WithRejection<Json<CreateProjectRequest>, AppError>,
|
|
) -> Result<(StatusCode, Json<ProjectModel>), AppError> {
|
|
Ok((
|
|
StatusCode::CREATED,
|
|
Json(ProjectModel::insert(&pool, input.title, input.color).await?),
|
|
))
|
|
}
|
|
|
|
pub async fn list_projects(
|
|
State(pool): State<Pool<Sqlite>>,
|
|
) -> Result<(StatusCode, Json<Vec<ProjectModel>>), AppError> {
|
|
Ok((StatusCode::OK, Json(ProjectModel::list(&pool).await?)))
|
|
}
|
|
|
|
pub async fn get_project(
|
|
State(pool): State<Pool<Sqlite>>,
|
|
WithRejection(Path(project_id), _): WithRejection<Path<Uuid>, AppError>,
|
|
) -> Result<(StatusCode, Json<ProjectModel>), AppError> {
|
|
Ok((
|
|
StatusCode::OK,
|
|
Json(ProjectModel::get_by_id(&pool, project_id).await?),
|
|
))
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
pub struct UpdateProjectRequest {
|
|
title: Option<String>,
|
|
color: Option<String>,
|
|
status: Option<ProjectStatus>,
|
|
}
|
|
|
|
pub async fn update_project(
|
|
State(pool): State<Pool<Sqlite>>,
|
|
WithRejection(Path(project_id), _): WithRejection<Path<Uuid>, AppError>,
|
|
WithRejection(Json(input), _): WithRejection<Json<UpdateProjectRequest>, AppError>,
|
|
) -> Result<(StatusCode, Json<ProjectModel>), AppError> {
|
|
let mut project = ProjectModel::get_by_id(&pool, project_id).await?;
|
|
|
|
if let Some(new_title) = input.title {
|
|
if new_title.len() == 0 {
|
|
return Err(AppError::Unprocessable(
|
|
"Title must not be empty".to_string(),
|
|
));
|
|
}
|
|
project.title = new_title;
|
|
}
|
|
|
|
if let Some(color) = input.color {
|
|
if color.len() != 7 || color.chars().next() != Some('#') {
|
|
return Err(AppError::Unprocessable(
|
|
"Color is not a valid hex".to_string(),
|
|
));
|
|
}
|
|
project.color = color;
|
|
}
|
|
|
|
if let Some(status) = input.status {
|
|
project.status = status;
|
|
}
|
|
|
|
Ok((StatusCode::OK, Json(project.update(&pool).await?)))
|
|
}
|
|
|
|
pub async fn delete_project(
|
|
State(pool): State<Pool<Sqlite>>,
|
|
WithRejection(Path(project_id), _): WithRejection<Path<Uuid>, AppError>,
|
|
) -> Result<StatusCode, AppError> {
|
|
ProjectModel::get_by_id(&pool, project_id).await?;
|
|
ProjectModel::delete(&pool, project_id).await?;
|
|
|
|
Ok(StatusCode::NO_CONTENT)
|
|
}
|