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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
use macro_pub;
/// Utility macro to help construct the bodies of functions that interface with
/// REST API endpoints.
///
/// # Overview
///
/// > *Please familiarize yourself with asynchrony in Rust before continuing.
/// > Assume that any return expression mentioned resolves to a future (which
/// > wraps the stated type), and therefore every return value must be polled
/// > either manually or with an executor.*
///
/// This macro handles the implementation of a function that constructs an
/// [`http::Request`]. The outer function signature must still be defined by
/// you; the resulting expression that an invocation expands to is intended to
/// be used as the body of your function. **To reiterate: invoking this macro
/// does not define a function, but the logic of an expression that generates
/// the return value of a signature that you create.**
///
/// The one exposed matching rule povides a simple syntax for defining your
/// inputs, such as the request body, query parameters and URI path components.
/// The syntax that this macro expects can be seen in the code block at the
/// beginning of this page. If you are unfamiliar with macros in Rust, please
/// refer to the ["Matching" section of *The Little Book of Rust Macros*].
///
/// > *Do not expect this macro's signature, output, or implementation to be
/// > stable until the first major release of this crate. After version 1.0.0,
/// > every minor increment is purported to be backwards and forwards-compatible
/// > until the next major release.*
///
/// # Function Signature
///
/// Typically your containing function will take in (at a minimum) a reference
/// to an instance of [`isahc::HttpClient`] and one to [`url::Url`]. Usually
/// these will be passed to the macro directly. You may also accept values for
/// `$params`, `$vars`, and `$body`, in any form of your choosing.
///
/// The expansion is an expression that resolves to a [`Result`], the generics
/// of which will conform to the types elided by your function signature. The
/// `Ok` variant will always be an [`ApiResponse`], whereas the `Err` variant
/// may contain any type that implements `From<DeserializeError>` and
/// `From<ResponseError>`. You may want to use the [`thiserror`] crate to wrap
/// [`DeserializeError`] and [`ResponseError`] into your own
/// [`std::error::Error`] type's variants. Conversion to your error type is
/// delegated by [`Into`].
///
/// [`thiserror`]: https://docs.rs/thiserror/latest/thiserror/
/// ["Matching" section of *The Little Book of Rust Macros*]: https://veykril.github.io/tlborm/decl-macros/macros-methodical.html#matching
///
/// **For examples of the intended usage, see the endpoint definitions for the
/// [`curseforge`] and [`modrinth`] crates.**
///
/// [`ApiResponse`]: crate::endpoints::ApiResponse
/// [`DeserializeError`]: crate::endpoints::DeserializeError
/// [`ResponseError`]: crate::endpoints::ResponseError
/// [`curseforge`]: https://docs.rs/curseforge/latest/src/curseforge/official/endpoints.rs.html
/// [`modrinth`]: https://docs.rs/modrinth/latest/src/modrinth/endpoints.rs.html
///
/// ## Input Tokens
///
/// #### `$client:ident`
///
/// Expected to be an identifier for an instance of [`isahc::HttpClient`], or
/// mimic the public API of the type at the very least. If you use a wrapper to
/// make another HTTP client compatible, be sure to review the source of this
/// macro to see which methods are used.
///
/// [`isahc::HttpClient`]: https://docs.rs/isahc/latest/isahc/struct.HttpClient.html
///
/// #### `$method:ident`
///
/// This is expecting an identifier item, but it will be converted to a string
/// and passed to [`http::request::Builder::method`]. **Currently only two
/// request methods are supported: `GET` and `POST`.** In the future this will
/// be expanded to support the full capabilities of the REST messaging paradigm.
///
/// #### `$base:ident`
///
/// Expected to be a reference to a [`url::Url`]. This value will be cloned and
/// mutated to add the URI path and query parameters. Results from mutable
/// operations here will be unwrapped; please make sure that
/// [`url::Url::cannot_be_a_base`] returns `false` at the very minimum. Do not
/// pass in values generated at runtime without validating them first.
///
/// #### `$path:literal`
///
/// Expected to be a string literal. If there are variadic components,
/// intersperse this literal with substitution placeholders (pairs of curly
/// braces, `{}`) in the style of [`format_args!`] (or [`println!`]). This gets
/// added to the end of the `$base`, and completes the URI that the request will
/// be made to.
///
/// #### `$($var:expr),+`
///
/// Expected to be a repeating pattern of valid expressions in the style of an
/// array (comma-delimited). The number of items inside the enclosing brackets
/// (`[]`) is expected to match the number of substitution placeholders (`{}`)
/// in the `$path` literal. Each expression's evaluation type must implement
/// [`ToString`] or have the `to_string` method. These will be formatted into
/// the `$path` string literal using [`format!`].
///
/// #### `$params:expr`
///
/// Expected to be an expression that resolves to a type implementing
/// [`serde::Serialize`], and compatible with [`serde_qs::to_string`]. The
/// result of that call will be unwrapped, you are responsible for validating
/// the serialization behavior.
///
/// #### `$body:expr`
///
/// Expected to be an expression that resolves to a type implementing
/// [`serde::Serialize`]. It must be compatible with [`serde_json::to_string`].
/// Just like `$params`, the result of serializing to a string will be
/// unwrapped. Validation is the responsibility of the caller.
///
/// # Disclaimer
///
/// This macro contains several calls to [`Option::unwrap`] and
/// [`Result::unwrap`] inside expressions where a value is always expected.
/// Unlike the memory safety afforded by the compiler's borrow checker, these
/// instances are not logically proven to be infallible operations; it is your
/// responsibility to ensure that the body, query parameters, and URI paths all
/// serialize correctly. There are comments in the source code that attempt to
/// justify these calls---it is highly recommended to view the source and read
/// these comments so that you can judge if your input is sufficiently robust.
///
/// It is especially recommended (as with any other nontrivial logic) to write
/// unit tests for every endpoint method. Please take care and validate the
/// serialization behavior of your input types, as incorrect implementations of
/// [`serde::Serialize`] that are likely fallible **will cause your software to
/// crash**.
///
/// **If this function body panics on a call to `unwrap`, double-check that your
/// inputs serialize properly. If you are fairly certain that something should
/// be valid (double-check with a tool like *Postman* or *[Hoppscotch]*) please
/// file a bug report on the [GitHub repository]. This may mean that an
/// assumption was incorrect and another error type is necessary.**
///
/// [Hoppscotch]: https://hoppscotch.io
/// [GitHub repository]: https://github.com/spikespaz/awaur
};
}
let deserializer = &mut from_slice;
let result = deserialize;
// Determine if the response's body bytes deserialized correctly into
// the inferred type (outside the macro), and if not, bubble the error
// to `Error::Deserialize`.
match result
}};
=> ;
=> ;
=> ;
=> ;
=> ;
=> ;
}