1use thiserror::Error;
2
3#[derive(Debug, Error)]
4pub enum ProviderError {
5 #[error("HTTP error: {0}")]
6 Http(#[from] reqwest::Error),
7
8 #[error("JSON serialization error: {0}")]
9 Json(#[from] serde_json::Error),
10
11 #[error("API error ({status}): {message}")]
12 Api { status: u16, message: String },
13
14 #[error("Stream error: {0}")]
15 Stream(String),
16
17 #[error("Unsupported operation: {0}")]
18 Unsupported(String),
19
20 #[error("{0}")]
21 Other(String),
22}
23
24#[cfg(test)]
25mod tests {
26 use super::*;
27
28 #[test]
29 fn api_error_display() {
30 let e = ProviderError::Api {
31 status: 429,
32 message: "rate limit".into(),
33 };
34 let s = e.to_string();
35 assert!(s.contains("429"));
36 assert!(s.contains("rate limit"));
37 }
38
39 #[test]
40 fn stream_error_display() {
41 let e = ProviderError::Stream("broken pipe".into());
42 assert!(e.to_string().contains("broken pipe"));
43 }
44
45 #[test]
46 fn unsupported_display() {
47 let e = ProviderError::Unsupported("embed".into());
48 assert!(e.to_string().contains("embed"));
49 }
50
51 #[test]
52 fn other_display() {
53 let e = ProviderError::Other("oops".into());
54 assert_eq!(e.to_string(), "oops");
55 }
56
57 #[test]
58 fn json_error_from_serde() {
59 let err = serde_json::from_str::<serde_json::Value>("not json").unwrap_err();
60 let pe = ProviderError::from(err);
61 assert!(pe.to_string().contains("JSON serialization error"));
62 }
63
64 #[test]
65 fn http_error_from_reqwest() {
66 let rt = tokio::runtime::Runtime::new().unwrap();
68 let err = rt.block_on(async { reqwest::get("http://[::0]:0/invalid").await.unwrap_err() });
69 let pe = ProviderError::from(err);
70 assert!(pe.to_string().contains("HTTP error"));
71 }
72
73 #[test]
74 fn error_is_std_error() {
75 let e: Box<dyn std::error::Error> = Box::new(ProviderError::Other("x".into()));
76 assert!(e.source().is_none());
77
78 let json_err = serde_json::from_str::<serde_json::Value>("bad").unwrap_err();
79 let e: Box<dyn std::error::Error> = Box::new(ProviderError::Json(json_err));
80 assert!(e.source().is_some());
81 }
82}