music on atproto
plyr.fm
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}