audio streaming app
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::claude::ClaudeClient;
14use crate::db::LabelDb;
15use crate::labels::{Label, LabelError, LabelSigner};
16
17/// Shared application state.
18#[derive(Clone)]
19pub struct AppState {
20 pub audd_api_token: String,
21 pub audd_api_url: String,
22 pub db: Option<Arc<LabelDb>>,
23 pub signer: Option<Arc<LabelSigner>>,
24 pub label_tx: Option<broadcast::Sender<(i64, Label)>>,
25 /// Claude client for image moderation (if configured)
26 pub claude: Option<Arc<ClaudeClient>>,
27}
28
29/// Application error type.
30#[derive(Debug, thiserror::Error)]
31pub enum AppError {
32 #[error("audd error: {0}")]
33 Audd(String),
34
35 #[error("claude error: {0}")]
36 Claude(String),
37
38 #[error("image moderation not configured")]
39 ImageModerationNotConfigured,
40
41 #[error("labeler not configured")]
42 LabelerNotConfigured,
43
44 #[error("bad request: {0}")]
45 BadRequest(String),
46
47 #[error("not found: {0}")]
48 NotFound(String),
49
50 #[error("label error: {0}")]
51 Label(#[from] LabelError),
52
53 #[error("database error: {0}")]
54 Database(#[from] sqlx::Error),
55
56 #[error("io error: {0}")]
57 Io(#[from] std::io::Error),
58}
59
60impl IntoResponse for AppError {
61 fn into_response(self) -> Response {
62 error!(error = %self, "request failed");
63 let (status, error_type) = match &self {
64 AppError::Audd(_) => (StatusCode::BAD_GATEWAY, "AuddError"),
65 AppError::Claude(_) => (StatusCode::BAD_GATEWAY, "ClaudeError"),
66 AppError::ImageModerationNotConfigured => {
67 (StatusCode::SERVICE_UNAVAILABLE, "ImageModerationNotConfigured")
68 }
69 AppError::LabelerNotConfigured => {
70 (StatusCode::SERVICE_UNAVAILABLE, "LabelerNotConfigured")
71 }
72 AppError::BadRequest(_) => (StatusCode::BAD_REQUEST, "BadRequest"),
73 AppError::NotFound(_) => (StatusCode::NOT_FOUND, "NotFound"),
74 AppError::Label(_) => (StatusCode::INTERNAL_SERVER_ERROR, "LabelError"),
75 AppError::Database(_) => (StatusCode::INTERNAL_SERVER_ERROR, "DatabaseError"),
76 AppError::Io(_) => (StatusCode::INTERNAL_SERVER_ERROR, "IoError"),
77 };
78 let body = serde_json::json!({
79 "error": error_type,
80 "message": self.to_string()
81 });
82 (status, Json(body)).into_response()
83 }
84}