use std::{ env::{self, VarError}, error::Error, fmt::Display, }; use axum::{ http::StatusCode, response::IntoResponse, routing::{get, post}, Json, Router, }; use serde::Serialize; use sqlx::{migrate::MigrateError, PgPool}; use crate::endpoints; pub(crate) async fn routes() -> Result { Ok(Router::new() .route("/events", post(endpoints::create_event)) .route("/events/:event_id", get(endpoints::fetch_event)) .route( "/events/:event_id/availabilities", post(endpoints::create_availabilities), ) .route( "/events/:event_id/availabilities", get(endpoints::fetch_availabilities), ) .with_state(AppState::new().await?)) } pub(crate) fn ok(r: Result) -> UniversalResponseDto { match r { Ok(res) => UniversalResponseDto { status: StatusCode::OK, result: Some(res), error: None, }, Err(err) => internal_server_error(err), } } pub(crate) fn internal_server_error(e: E) -> UniversalResponseDto { UniversalResponseDto { status: StatusCode::INTERNAL_SERVER_ERROR, result: None, error: Some(ErrorDto { message: format!("{}", e), }), } } #[derive(Serialize)] pub struct UniversalResponseDto { #[serde(skip_serializing)] status: StatusCode, result: Option, error: Option, } impl IntoResponse for UniversalResponseDto { fn into_response(self) -> axum::response::Response { if self.error.is_some() { return (self.status, Json(self)).into_response(); } if self.result.is_some() { return (self.status, Json(self)).into_response(); } (self.status, Json(self)).into_response() } } #[derive(Serialize)] pub struct ErrorDto { message: String, } #[derive(Debug, Clone)] pub struct AppState { pub db_pool: PgPool, pub event_uid_size: usize, } impl AppState { pub async fn new() -> Result { Ok(Self { db_pool: { let pool = PgPool::connect(&env::var("DATABASE_URL")?).await?; // Run migrations sqlx::migrate!("./migrations").run(&pool).await?; pool }, event_uid_size: env::var("EVENT_UID_SIZE")? .parse() .expect("EVENT_UID_SIZE is undefined. Must be a number."), }) } } #[derive(Debug, Serialize)] pub struct ApplicationError { msg: String, } impl ApplicationError { pub fn new(msg: String) -> Self { Self { msg } } } impl From for ApplicationError { fn from(value: sqlx::Error) -> Self { Self { msg: value.to_string(), } } } impl From for ApplicationError { fn from(value: MigrateError) -> Self { Self { msg: value.to_string(), } } } impl From for ApplicationError { fn from(value: VarError) -> Self { Self { msg: value.to_string(), } } } impl Display for ApplicationError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.msg) } } impl Error for ApplicationError {}