-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathtraits.rs
More file actions
205 lines (196 loc) · 5.84 KB
/
traits.rs
File metadata and controls
205 lines (196 loc) · 5.84 KB
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
use std::fmt::Display;
use async_trait::async_trait;
use http::StatusCode;
use url::Url;
///An error with a ststus code
pub trait StatusError: std::error::Error {
///The status code, if available, of this error
fn status(&self) -> Option<StatusCode>;
}
///A HTTP error
#[derive(Debug)]
pub struct RequestError {
///The status code, if available, of this error
pub err: Box<dyn StatusError + Send + Sync>,
}
impl StatusError for RequestError {
fn status(&self) -> Option<StatusCode> {
self.err.status()
}
}
impl RequestError {
///The status code, if available, of this error
pub fn status(&self) -> Option<StatusCode> {
StatusError::status(self)
}
}
impl Display for RequestError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.err)
}
}
impl std::error::Error for RequestError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&*self.err)
}
}
#[async_trait]
///An HTTP client
pub trait Client {
///Create a GET request
fn get(&self, url: Url) -> Box<dyn ClientRequest + '_>;
///Create a POST request
fn post(&self, url: Url) -> Box<dyn ClientRequest + '_>;
///Create a PUT request
fn put(&self, url: Url) -> Box<dyn ClientRequest + '_>;
///Create a DELETE request
fn delete(&self, url: Url) -> Box<dyn ClientRequest + '_>;
}
#[async_trait]
///A response to a HTTP request
pub trait ClientResponse: Send + Sync {
///That response's status code
fn status(&self) -> StatusCode;
///Attept to catch error-signaling status codes and return them as errors
fn error_for_status<'a>(self: Box<Self>) -> Result<Box<dyn ClientResponse + 'a>, RequestError>
where
Self: 'a;
///Attept to catch error-signaling status codes and return them as errors
fn error_for_status_ref(&self) -> Result<&(dyn ClientResponse + '_), RequestError>;
///The text of the response
async fn text(self: Box<Self>) -> Result<String, RequestError>;
}
#[async_trait]
///A HTTP request
pub trait ClientRequest {
///Its authenticaton
fn bearer_auth<'a>(self: Box<Self>, x: &(dyn Display + '_)) -> Box<dyn ClientRequest + 'a>
where
Self: 'a;
///Adds ad JSON body
fn json<'a>(
self: Box<Self>,
x: &(dyn erased_serde::Serialize + '_),
) -> Box<dyn ClientRequest + 'a>
where
Self: 'a;
///Adds a query string
fn query<'a>(
self: Box<Self>,
x: &(dyn erased_serde::Serialize + '_),
) -> Box<dyn ClientRequest + 'a>
where
Self: 'a;
///Adds a form body
fn form<'a>(
self: Box<Self>,
x: &(dyn erased_serde::Serialize + '_),
) -> Box<dyn ClientRequest + 'a>
where
Self: 'a;
///Sends the request
async fn send<'a>(self: Box<Self>) -> Result<Box<dyn ClientResponse + 'a>, RequestError>
where
Self: 'a;
}
#[cfg(feature = "reqwest")]
const _: () = {
impl StatusError for reqwest::Error {
fn status(&self) -> Option<StatusCode> {
self.status()
}
}
impl From<reqwest::Error> for RequestError {
fn from(value: reqwest::Error) -> Self {
Self {
err: Box::new(value),
}
}
}
#[async_trait]
impl Client for reqwest::Client {
fn get(&self, url: Url) -> Box<dyn ClientRequest + '_> {
Box::new(self.get(url))
}
fn post(&self, url: Url) -> Box<dyn ClientRequest + '_> {
Box::new(self.post(url))
}
fn put(&self, url: Url) -> Box<dyn ClientRequest + '_> {
Box::new(self.put(url))
}
fn delete(&self, url: Url) -> Box<dyn ClientRequest + '_> {
Box::new(self.delete(url))
}
}
#[async_trait]
impl ClientResponse for reqwest::Response {
fn status(&self) -> StatusCode {
self.status()
}
fn error_for_status<'a>(
self: Box<Self>,
) -> Result<Box<dyn ClientResponse + 'a>, RequestError>
where
Self: 'a,
{
match (*self).error_for_status() {
Err(e) => Err(e.into()),
Ok(a) => Ok(Box::new(a)),
}
}
fn error_for_status_ref(&self) -> Result<&(dyn ClientResponse + '_), RequestError> {
match self.error_for_status_ref() {
Err(e) => Err(e.into()),
Ok(v) => Ok(v),
}
}
async fn text(self: Box<Self>) -> Result<String, RequestError> {
(*self).text().await.map_err(Into::into)
}
}
#[async_trait]
impl ClientRequest for reqwest::RequestBuilder {
async fn send<'a>(self: Box<Self>) -> Result<Box<dyn ClientResponse + 'a>, RequestError>
where
Self: 'a,
{
match reqwest::RequestBuilder::send(*self).await {
Err(e) => Err(e.into()),
Ok(a) => Ok(Box::new(a)),
}
}
fn bearer_auth<'a>(self: Box<Self>, x: &(dyn Display + '_)) -> Box<dyn ClientRequest + 'a>
where
Self: 'a,
{
Box::new((*self).bearer_auth(x))
}
fn json<'a>(
self: Box<Self>,
x: &(dyn erased_serde::Serialize + '_),
) -> Box<dyn ClientRequest + 'a>
where
Self: 'a,
{
Box::new((*self).json(x))
}
fn query<'a>(
self: Box<Self>,
x: &(dyn erased_serde::Serialize + '_),
) -> Box<dyn ClientRequest + 'a>
where
Self: 'a,
{
Box::new((*self).query(x))
}
fn form<'a>(
self: Box<Self>,
x: &(dyn erased_serde::Serialize + '_),
) -> Box<dyn ClientRequest + 'a>
where
Self: 'a,
{
Box::new((*self).form(x))
}
}
};