61 lines
1.9 KiB
Rust
61 lines
1.9 KiB
Rust
use axum::{
|
|
http::StatusCode,
|
|
response::{IntoResponse, Response},
|
|
Json,
|
|
};
|
|
use serde_json::json;
|
|
use thiserror::Error;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum AppError {
|
|
#[error("not found: {0}")]
|
|
NotFound(String),
|
|
#[error("invalid request: {0}")]
|
|
BadRequest(String),
|
|
#[error("conflict: {0}")]
|
|
Conflict(String),
|
|
#[error("unauthorized")]
|
|
Unauthorized,
|
|
#[error("device communication failed: {0}")]
|
|
Device(String),
|
|
#[error("external dependency failed: {0}")]
|
|
Dependency(String),
|
|
#[error(transparent)]
|
|
Internal(#[from] anyhow::Error),
|
|
}
|
|
|
|
impl IntoResponse for AppError {
|
|
fn into_response(self) -> Response {
|
|
let (status, message) = match &self {
|
|
Self::NotFound(v) => (StatusCode::NOT_FOUND, v.clone()),
|
|
Self::BadRequest(v) => (StatusCode::BAD_REQUEST, v.clone()),
|
|
Self::Conflict(v) => (StatusCode::CONFLICT, v.clone()),
|
|
Self::Unauthorized => (StatusCode::UNAUTHORIZED, "unauthorized".into()),
|
|
Self::Device(v) => (StatusCode::BAD_GATEWAY, v.clone()),
|
|
// Cloud/provider outages are an expected external-dependency failure, not a
|
|
// controller-side 502. Keep Local's historical Device -> 502 mapping unchanged.
|
|
Self::Dependency(v) => (StatusCode::FAILED_DEPENDENCY, v.clone()),
|
|
Self::Internal(v) => {
|
|
tracing::error!(error = ?v, "internal error");
|
|
(
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
"internal server error".into(),
|
|
)
|
|
}
|
|
};
|
|
(status, Json(json!({"error": message}))).into_response()
|
|
}
|
|
}
|
|
|
|
impl From<rusqlite::Error> for AppError {
|
|
fn from(value: rusqlite::Error) -> Self {
|
|
Self::Internal(value.into())
|
|
}
|
|
}
|
|
|
|
impl From<serde_json::Error> for AppError {
|
|
fn from(value: serde_json::Error) -> Self {
|
|
Self::Internal(value.into())
|
|
}
|
|
}
|