Skip to main content

wi_api/
error.rs

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        // Current shape: {"error":{"code","message"}}; the legacy flat shape
24        // {"error":"...","message":"...","code":"..."} is still read.
25        #[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}