use axum::{http::StatusCode, Json}; use serde_json::json; pub type Result = std::result::Result; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("IO error: {0}")] IO(#[from] std::io::Error), #[error("Env variable error: {0}")] Env(#[from] dotenvy::Error), #[error("Axum error: {0}")] Axum(#[from] axum::Error), #[error("Http error: {0}")] Http(#[from] axum::http::Error), #[error("Json error: {0}")] Json(#[from] serde_json::Error), #[error("JWT error: {0}")] JWT(#[from] jsonwebtoken::errors::Error), #[error("Database error: {0}")] Sqlx(#[from] sqlx::Error), #[error("Migration error: {0}")] Migration(#[from] sqlx::migrate::MigrateError), #[error("Failed to hash password: {0}")] PasswordHash(#[source] argon2::password_hash::Error), #[error("User not found")] UserNotFound, #[error("User with that email already exists")] EmailExists, #[error("Invalid email: {0}")] EmailInvalid(#[from] email_address::Error), #[error("Invalid email or password")] LoginInvalid, #[error("{0}")] Other(String), } impl From for Error { fn from(value: argon2::password_hash::Error) -> Self { match value { argon2::password_hash::Error::Password => Self::LoginInvalid, _ => Self::PasswordHash(value), } } } impl From<&Error> for StatusCode { fn from(value: &Error) -> Self { match value { Error::UserNotFound => StatusCode::NOT_FOUND, Error::EmailExists => StatusCode::CONFLICT, Error::EmailInvalid(_) => StatusCode::UNPROCESSABLE_ENTITY, Error::LoginInvalid => StatusCode::UNAUTHORIZED, _ => StatusCode::INTERNAL_SERVER_ERROR, } } } impl axum::response::IntoResponse for Error { fn into_response(self) -> axum::response::Response { // TODO: implement [rfc7807](https://www.rfc-editor.org/rfc/rfc7807.html) ( StatusCode::from(&self), Json(json!({ "status": StatusCode::from(&self).to_string(), "detail": self.to_string(), })), ) .into_response() } }