1use thiserror::Error;
2
3#[derive(Debug, Error)]
4pub enum WiError {
5 #[error("HTTP {status}: {message}")]
6 Api {
7 status: u16,
8 message: String,
9 code: Option<String>,
10 },
11
12 #[error("request error: {0}")]
13 Request(#[from] reqwest::Error),
14
15 #[error("serialize error: {0}")]
16 Serialize(#[from] serde_json::Error),
17}
18
19impl WiError {
20 pub(crate) async fn from_response(res: reqwest::Response) -> WiError {
21 let status = res.status().as_u16();
22
23 #[derive(serde::Deserialize)]
26 #[serde(untagged)]
27 enum ErrorField {
28 Nested {
29 code: Option<String>,
30 message: Option<String>,
31 },
32 Flat(String),
33 }
34
35 #[derive(serde::Deserialize)]
36 struct Body {
37 message: Option<String>,
38 error: Option<ErrorField>,
39 code: Option<String>,
40 }
41
42 let fallback = format!("HTTP {status}");
43 let (message, code) = match res.json::<Body>().await {
44 Ok(Body {
45 error: Some(ErrorField::Nested { code, message }),
46 ..
47 }) => (message.unwrap_or(fallback), code),
48 Ok(b) => {
49 let flat = match b.error {
50 Some(ErrorField::Flat(s)) => Some(s),
51 _ => None,
52 };
53 (b.message.or(flat).unwrap_or(fallback), b.code)
54 }
55 Err(_) => (fallback, None),
56 };
57
58 WiError::Api {
59 status,
60 message,
61 code,
62 }
63 }
64}