1use crate::models::ErrorResponse;
6use thiserror::Error;
7
8#[derive(Error, Debug)]
10pub enum TypecastError {
11 #[error("Bad Request - {detail}")]
13 BadRequest { detail: String },
14
15 #[error("Unauthorized - Invalid or missing API key")]
17 Unauthorized { detail: String },
18
19 #[error("Payment Required - Insufficient credits")]
21 PaymentRequired { detail: String },
22
23 #[error("Forbidden - Access denied")]
25 Forbidden { detail: String },
26
27 #[error("Not Found - {detail}")]
29 NotFound { detail: String },
30
31 #[error("Validation Error - {detail}")]
33 ValidationError { detail: String },
34
35 #[error("Too Many Requests - Rate limit exceeded")]
37 RateLimited { detail: String },
38
39 #[error("Internal Server Error - {detail}")]
41 ServerError { detail: String },
42
43 #[error("HTTP error: {0}")]
45 HttpError(#[from] reqwest::Error),
46
47 #[error("JSON error: {0}")]
49 JsonError(#[from] serde_json::Error),
50
51 #[error("API error (status {status_code}): {detail}")]
53 Unknown { status_code: u16, detail: String },
54
55 #[error("Decode error: {0}")]
57 DecodeError(String),
58
59 #[error("I/O error: {0}")]
61 IoError(String),
62
63 #[error("Captioning error: {0}")]
65 CaptioningError(String),
66}
67
68impl TypecastError {
69 pub fn from_response(status_code: u16, error_response: Option<ErrorResponse>) -> Self {
71 let detail = error_response
72 .map(|e| e.detail)
73 .unwrap_or_else(|| "Unknown error".to_string());
74
75 match status_code {
76 400 => TypecastError::BadRequest { detail },
77 401 => TypecastError::Unauthorized { detail },
78 402 => TypecastError::PaymentRequired { detail },
79 403 => TypecastError::Forbidden { detail },
80 404 => TypecastError::NotFound { detail },
81 422 => TypecastError::ValidationError { detail },
82 429 => TypecastError::RateLimited { detail },
83 500..=599 => TypecastError::ServerError { detail },
84 _ => TypecastError::Unknown { status_code, detail },
85 }
86 }
87
88 pub fn is_bad_request(&self) -> bool {
90 matches!(self, TypecastError::BadRequest { .. })
91 }
92
93 pub fn is_unauthorized(&self) -> bool {
95 matches!(self, TypecastError::Unauthorized { .. })
96 }
97
98 pub fn is_payment_required(&self) -> bool {
100 matches!(self, TypecastError::PaymentRequired { .. })
101 }
102
103 pub fn is_forbidden(&self) -> bool {
105 matches!(self, TypecastError::Forbidden { .. })
106 }
107
108 pub fn is_not_found(&self) -> bool {
110 matches!(self, TypecastError::NotFound { .. })
111 }
112
113 pub fn is_validation_error(&self) -> bool {
115 matches!(self, TypecastError::ValidationError { .. })
116 }
117
118 pub fn is_rate_limited(&self) -> bool {
120 matches!(self, TypecastError::RateLimited { .. })
121 }
122
123 pub fn is_server_error(&self) -> bool {
125 matches!(self, TypecastError::ServerError { .. })
126 }
127
128 pub fn status_code(&self) -> Option<u16> {
130 match self {
131 TypecastError::BadRequest { .. } => Some(400),
132 TypecastError::Unauthorized { .. } => Some(401),
133 TypecastError::PaymentRequired { .. } => Some(402),
134 TypecastError::Forbidden { .. } => Some(403),
135 TypecastError::NotFound { .. } => Some(404),
136 TypecastError::ValidationError { .. } => Some(422),
137 TypecastError::RateLimited { .. } => Some(429),
138 TypecastError::ServerError { .. } => Some(500),
139 TypecastError::Unknown { status_code, .. } => Some(*status_code),
140 _ => None,
141 }
142 }
143}
144
145pub type Result<T> = std::result::Result<T, TypecastError>;