1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use std::collections::HashMap;
use log::trace;
use reqwest::{RequestBuilder, Response};
use serde::de::DeserializeOwned;
use serde::Deserialize;
use super::Error;
#[derive(Debug, Deserialize)]
struct Message {
message: String,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum ErrorResponse {
Map(HashMap<String, Message>),
Message(Message),
}
async fn extract_message(resp: Response) -> Result<String, Error> {
let text = resp.text().await?;
Ok(serde_json::from_str::<ErrorResponse>(&text)
.ok()
.and_then(|body| match body {
ErrorResponse::Map(map) => map.into_iter().next().map(|(_k, v)| v.message),
ErrorResponse::Message(msg) => Some(msg.message),
})
.unwrap_or(text))
}
pub async fn check(response: Response) -> Result<Response, Error> {
let status = response.status();
if status.is_client_error() || status.is_server_error() {
let message = extract_message(response).await?;
trace!("HTTP request returned {}; error: {:?}", status, message);
Err(Error::new(status.into(), message).with_status(status))
} else {
trace!(
"HTTP request to {} returned {}",
response.url(),
response.status()
);
Ok(response)
}
}
#[inline]
pub async fn send_checked(builder: RequestBuilder) -> Result<Response, Error> {
check(builder.send().await?).await
}
#[inline]
pub async fn to_json<T>(response: Response) -> Result<T, Error>
where
T: DeserializeOwned + Send,
{
check(response).await?.json::<T>().await.map_err(Into::into)
}
#[inline]
pub async fn fetch_json<T>(builder: RequestBuilder) -> Result<T, Error>
where
T: DeserializeOwned + Send,
{
send_checked(builder)
.await?
.json::<T>()
.await
.map_err(Into::into)
}
pub const NO_PATH: Option<&'static str> = None;