Files
gree-controller/src/error.rs
T
2026-09-14 16:32:28 +02:00

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())
}
}