-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathreqres2.rs
114 lines (106 loc) · 4.2 KB
/
reqres2.rs
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
use std::str::FromStr;
use derive_builder::Builder;
use rustify::{errors::ClientError, Client, Endpoint, MiddleWare};
use rustify_derive::Endpoint;
use serde::{Deserialize, Serialize};
// With this endpoint we are actually giving the struct some fields that will be
// used to construct the JSON body of the request. When building a request body,
// rustify performs a few checks to determine how the body should be
// constructed. You can tag a field with `#[endpoint(raw)]` to use that field
// directly as the request body (it must be a `Vec<u8>`), you can tag one or
// more fields with #[endpoint(body)] to serialize them together into the
// request body, or as in the case below, if neither of the above tags are found
// then rustify automatically serializes all "untagged" fields as the request
// body.
//
// The actual API doesn't include an `opt` argument, however, it's included here
// for demonstration purposes. Using `setter(into)` here makes creating the
// request easier since we can pass string slices, as an example. Using
// `setter(strip_option)` allows passing in optional arguments without wrapping
// them in `Some`. By default, when rustify serializes the request body, any
// `Option` fields that have their value set to `None` will be skipped. This
// prevents sending something like {"opt": ""} which in some cases could
// actually overwrite an existing value.
//
// The reqres API doesn't specify which arguments are required, however, for the
// sake of this example we assume `name` and `job` are required and we therefore
// do not wrap them in an `Option`.
#[derive(Builder, Default, Endpoint, Serialize)]
#[endpoint(
path = "users",
method = "POST",
response = "CreateUserResponse",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
struct CreateUserRequest {
pub name: String,
pub job: String,
pub opt: Option<String>,
}
// The API returns an ID and timestamp.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct CreateUserResponse {
pub id: String,
pub created_at: String,
}
// Rustify allows passing Middleware when executing an endpoint. Implementations
// of this trait contain two methods for operating on outgoing requests and
// incoming responses. In our case, all of the paths in our API calls share a
// common trait of needed to be prepended with "/api". Wouldn't it be nice to
// automatically do this instead of having to specify it for every endpoint?
//
// The below implementation modifies all outgoing requests by automatically
// prepending "/api" to the URL path.
struct Middle {}
impl MiddleWare for Middle {
fn request<E: Endpoint>(
&self,
_: &E,
req: &mut http::Request<Vec<u8>>,
) -> Result<(), ClientError> {
// Prepending to the path of a URL is not a trivial task. Here we use
// the `url` crate which offers better support for mutating a URL. We
// parse the final result back into an `http::Uri`.
let url = url::Url::parse(req.uri().to_string().as_str()).unwrap();
let mut url_c = url.clone();
let mut segs: Vec<&str> = url.path_segments().unwrap().collect();
segs.insert(0, "api");
url_c.path_segments_mut().unwrap().clear().extend(segs);
*req.uri_mut() = http::Uri::from_str(url_c.as_str()).unwrap();
Ok(())
}
fn response<E: Endpoint>(
&self,
_: &E,
_: &mut http::Response<Vec<u8>>,
) -> Result<(), ClientError> {
Ok(())
}
}
#[tokio::main]
async fn main() {
// Just like in the first example we must first create a client.
let client = Client::default("https://reqres.in/");
// Then we can construct our endpoint
let endpoint = CreateUserRequest::builder()
.name("John")
.job("Programmer")
.build()
.unwrap();
// Here we use `exec_mut` which allows us to pass the Wrapper we created
// earlier for mutating our outgoing requests.
let name = endpoint.name.clone();
let result = endpoint
.with_middleware(&Middle {})
.exec(&client)
.await
.unwrap()
.parse()
.unwrap();
println!(
"Created user {} with ID {} at {}",
name, result.id, result.created_at
);
}