1//! Application state and error types. 2 3use std::sync::Arc; 4 5use axum::{ 6 http::StatusCode, 7 response::{IntoResponse, Response}, 8 Json, 9}; 10use tokio::sync::broadcast; 11use tracing::error; 12 13use crate::db::LabelDb; 14use crate::labels::{Label, LabelError, LabelSigner}; 15 16/// Shared application state. 17#[derive(Clone)] 18pub struct AppState { 19 pub audd_api_token: String, 20 pub audd_api_url: String, 21 pub db: Option<Arc<LabelDb>>, 22 pub signer: Option<Arc<LabelSigner>>, 23 pub label_tx: Option<broadcast::Sender<(i64, Label)>>, 24} 25 26/// Application error type. 27#[derive(Debug, thiserror::Error)] 28pub enum AppError { 29 #[error("audd error: {0}")] 30 Audd(String), 31 32 #[error("labeler not configured")] 33 LabelerNotConfigured, 34 35 #[error("bad request: {0}")] 36 BadRequest(String), 37 38 #[error("not found: {0}")] 39 NotFound(String), 40 41 #[error("label error: {0}")] 42 Label(#[from] LabelError), 43 44 #[error("database error: {0}")] 45 Database(#[from] sqlx::Error), 46 47 #[error("io error: {0}")] 48 Io(#[from] std::io::Error), 49} 50 51impl IntoResponse for AppError { 52 fn into_response(self) -> Response { 53 error!(error = %self, "request failed"); 54 let (status, error_type) = match &self { 55 AppError::Audd(_) => (StatusCode::BAD_GATEWAY, "AuddError"), 56 AppError::LabelerNotConfigured => { 57 (StatusCode::SERVICE_UNAVAILABLE, "LabelerNotConfigured") 58 } 59 AppError::BadRequest(_) => (StatusCode::BAD_REQUEST, "BadRequest"), 60 AppError::NotFound(_) => (StatusCode::NOT_FOUND, "NotFound"), 61 AppError::Label(_) => (StatusCode::INTERNAL_SERVER_ERROR, "LabelError"), 62 AppError::Database(_) => (StatusCode::INTERNAL_SERVER_ERROR, "DatabaseError"), 63 AppError::Io(_) => (StatusCode::INTERNAL_SERVER_ERROR, "IoError"), 64 }; 65 let body = serde_json::json!({ 66 "error": error_type, 67 "message": self.to_string() 68 }); 69 (status, Json(body)).into_response() 70 } 71}