1use thiserror::Error;
2
3#[derive(Debug, Error)]
5pub enum ChorusError {
6 #[error("provider error ({provider}): {message}")]
8 Provider { provider: String, message: String },
9
10 #[error("all providers failed")]
12 AllProvidersFailed,
13
14 #[error("validation error: {0}")]
16 Validation(String),
17
18 #[error("template not found: {0}")]
20 TemplateNotFound(String),
21
22 #[error("quota exceeded: {0}")]
24 QuotaExceeded(String),
25
26 #[error("invalid api key")]
28 InvalidApiKey,
29
30 #[error("rate limited")]
32 RateLimited { retry_after_secs: u64 },
33
34 #[error("internal error: {0}")]
36 Internal(#[from] anyhow::Error),
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn provider_error_display() {
45 let err = ChorusError::Provider {
46 provider: "twilio".into(),
47 message: "timeout".into(),
48 };
49 assert_eq!(err.to_string(), "provider error (twilio): timeout");
50 }
51
52 #[test]
53 fn all_providers_failed_display() {
54 let err = ChorusError::AllProvidersFailed;
55 assert_eq!(err.to_string(), "all providers failed");
56 }
57
58 #[test]
59 fn validation_error_display() {
60 let err = ChorusError::Validation("missing phone number".into());
61 assert_eq!(err.to_string(), "validation error: missing phone number");
62 }
63
64 #[test]
65 fn template_not_found_display() {
66 let err = ChorusError::TemplateNotFound("welcome".into());
67 assert_eq!(err.to_string(), "template not found: welcome");
68 }
69
70 #[test]
71 fn quota_exceeded_display() {
72 let err = ChorusError::QuotaExceeded("monthly limit reached".into());
73 assert_eq!(err.to_string(), "quota exceeded: monthly limit reached");
74 }
75
76 #[test]
77 fn invalid_api_key_display() {
78 let err = ChorusError::InvalidApiKey;
79 assert_eq!(err.to_string(), "invalid api key");
80 }
81
82 #[test]
83 fn rate_limited_display() {
84 let err = ChorusError::RateLimited {
85 retry_after_secs: 30,
86 };
87 assert_eq!(err.to_string(), "rate limited");
88 }
89
90 #[test]
91 fn internal_error_from_anyhow() {
92 let anyhow_err = anyhow::anyhow!("something broke");
93 let err = ChorusError::from(anyhow_err);
94 assert!(err.to_string().contains("something broke"));
95 }
96}