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> { 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>, WithRejection(Json(input), _): WithRejection, AppError>, ) -> Result<(StatusCode, Json), AppError> { Ok(( StatusCode::CREATED, Json(ProjectModel::insert(&pool, input.title, input.color).await?), )) } pub async fn list_projects( State(pool): State>, ) -> Result<(StatusCode, Json>), AppError> { Ok((StatusCode::OK, Json(ProjectModel::list(&pool).await?))) } pub async fn get_project( State(pool): State>, WithRejection(Path(project_id), _): WithRejection, AppError>, ) -> Result<(StatusCode, Json), AppError> { Ok(( StatusCode::OK, Json(ProjectModel::get_by_id(&pool, project_id).await?), )) } #[derive(Deserialize)] #[serde(deny_unknown_fields)] pub struct UpdateProjectRequest { title: Option, color: Option, status: Option, } pub async fn update_project( State(pool): State>, WithRejection(Path(project_id), _): WithRejection, AppError>, WithRejection(Json(input), _): WithRejection, AppError>, ) -> Result<(StatusCode, Json), 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>, WithRejection(Path(project_id), _): WithRejection, AppError>, ) -> Result { ProjectModel::get_by_id(&pool, project_id).await?; ProjectModel::delete(&pool, project_id).await?; Ok(StatusCode::NO_CONTENT) }