1use std::fmt;
2
3#[derive(Debug)]
5pub enum Error {
6 Http(reqwest::Error),
8 Api { status: u16, body: String },
10}
11
12impl fmt::Display for Error {
13 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14 match self {
15 Error::Http(e) => write!(f, "HTTP error: {e}"),
16 Error::Api { status, body } => write!(f, "API error (HTTP {status}): {body}"),
17 }
18 }
19}
20
21impl std::error::Error for Error {
22 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
23 match self {
24 Error::Http(e) => Some(e),
25 Error::Api { .. } => None,
26 }
27 }
28}
29
30impl From<reqwest::Error> for Error {
31 fn from(e: reqwest::Error) -> Self {
32 Error::Http(e)
33 }
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39 use std::error::Error as _;
40
41 #[test]
42 fn api_error_display_contains_status_and_body() {
43 let err = Error::Api {
44 status: 404,
45 body: "not found".to_string(),
46 };
47 let text = format!("{err}");
48 assert!(text.contains("404"));
49 assert!(text.contains("not found"));
50 }
51
52 #[test]
53 fn api_error_has_no_source() {
54 let err = Error::Api {
55 status: 500,
56 body: "oops".to_string(),
57 };
58 assert!(err.source().is_none());
59 }
60
61 #[test]
62 fn debug_impl_works() {
63 let err = Error::Api {
64 status: 400,
65 body: "bad".to_string(),
66 };
67 let d = format!("{err:?}");
68 assert!(d.contains("Api"));
69 }
70
71 #[tokio::test]
72 async fn http_error_display_includes_prefix() {
73 let resp = reqwest::get("http://127.0.0.1:1/nope").await;
74 let reqwest_err = resp.err().unwrap();
75 let err: Error = reqwest_err.into();
76 let text = format!("{err}");
77 assert!(text.starts_with("HTTP error:"));
78 assert!(err.source().is_some());
79 }
80}