Skip to main content

chorus_core/
error.rs

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