esi_openapi/macros.rs
1/// Create a function for calling a single endpoint
2/// with a GET request.
3///
4/// # Example
5/// ```rust,no_run
6/// # use esi_openapi::prelude::*;
7/// # use esi_openapi::api_get;
8/// pub struct SomeGroup<'a> {
9/// pub(crate) esi: &'a Esi,
10/// }
11///
12/// impl SomeGroup<'_> {
13///
14/// api_get!(
15/// /// Docs for the generated function
16/// function_name,
17/// "some_operation_id",
18/// RequestType::Public,
19/// Vec<u64>,
20/// );
21///
22/// }
23/// # fn main() {}
24/// ```
25///
26/// ## Result:
27///
28/// ```rust,ignore
29/// /// Docs for the generated function
30/// pub async fn function_name(&self) -> EsiResult<Vec<u64>> {
31/// let path = self.esi.get_endpoint_for_op_id("some_operation_id")?;
32/// self.esi
33/// .query("GET", RequestType::Public, &path, None, None)
34/// .await
35/// }
36/// ```
37///
38/// Additionally, this macro supports path replacements to insert variables
39/// into the path from ESI.
40///
41/// # Example
42///
43/// ```rust,no_run
44/// # use esi_openapi::prelude::*;
45/// # use esi_openapi::api_get;
46/// pub struct SomeGroup<'a> {
47/// pub(crate) esi: &'a Esi,
48/// }
49///
50/// impl SomeGroup<'_> {
51///
52/// api_get!(
53/// /// Docs for the generated function
54/// function_name,
55/// "some_operation_id",
56/// RequestType::Public,
57/// Vec<u64>,
58/// (alliance_id: u64) => "{alliance_id}"
59/// );
60///
61/// }
62/// # fn main() {}
63/// ```
64/// ## Result:
65///
66/// ```rust,ignore
67/// /// Docs for the generated function
68/// pub async fn function_name(&self, alliance_id: u64) -> EsiResult<Vec<u64>> {
69/// let path = self.esi.get_endpoint_for_op_id("some_operation_id")?
70/// .replace("{alliance_id}", &alliance_id.to_string());
71/// self.esi
72/// .query("GET", RequestType::Public, &path, None, None)
73/// .await
74/// }
75/// ```
76///
77/// Finally, there is support for required and optional query params. These are different from path
78/// parameters: in 'markets/{region_id}/orders?page=1', region_id is a path parameter and page is a
79/// query parameter. Note that in the macro invocation, query parameters are separated from path
80/// parameters with a semicolon, and that optional query parameters always follow required ones.
81/// See [crate::groups::MarketGroup] for sample macro calls.
82///
83/// # Example
84///
85/// ```rust,no_run
86/// # use esi_openapi::prelude::*;
87/// # use esi_openapi::api_get;
88/// pub struct SomeGroup<'a> {
89/// pub(crate) esi: &'a Esi,
90/// }
91///
92/// impl SomeGroup<'_> {
93///
94/// api_get!(
95/// /// Docs for the generated function
96/// function_name,
97/// "some_operation_id",
98/// RequestType::Public,
99/// Vec<u64>,
100/// (region_id: u64) => "{region_id}";
101/// (page: i32) => "page";
102/// Optional(order_type: bool) => "order_type"
103/// );
104///
105/// }
106/// # fn main() {}
107/// ```
108/// ## Result:
109///
110/// ```rust,ignore
111/// /// Docs for the generated function
112/// pub async fn function_name(&self, region_id: u64, page: i32, order_type: Option<bool>) -> EsiResult<Vec<u64>> {
113/// let path = self.esi.get_endpoint_for_op_id("some_operation_id")?
114/// .replace("{region_id}", ®ion_id.to_string());
115/// let params = vec![
116/// ("page", page.to_string()),
117/// ]
118/// let mut params = params;
119/// if let Some(order_type) = order_type {
120/// params.push(("order_type", order_type.to_string()))
121/// }
122/// let params: Vec<(&str, &str)> = params.iter().map(|(a, b)| (*a, &**b)).collect();
123/// self.esi
124/// .query("GET", RequestType::Public, &path, Some(¶ms), None)
125/// .await
126/// }
127/// ```
128#[macro_export]
129macro_rules! api_get {
130 (
131 $(#[$m:meta])*
132 $fn_name:ident,
133 $op_id:literal,
134 $visibility:expr,
135 $ret_type:ty,
136 $( ($param:ident: $param_t:ty) => $replace:literal ),*
137 ) => {
138 $(#[$m])*
139 pub async fn $fn_name(&self, $( $param: $param_t, )*) -> EsiResult<$ret_type> {
140 let path = self
141 .esi
142 .get_endpoint_for_op_id($op_id)?
143 $(
144 .replace($replace, &$param.to_string())
145 )*;
146 self.esi.
147 query("GET", $visibility, &path, None, None)
148 .await
149 }
150 };
151 (
152 $(#[$m:meta])*
153 $fn_name:ident,
154 $op_id:literal,
155 $visibility:expr,
156 $ret_type:ty,
157 $( ($param:ident: $param_t:ty) => $replace:literal ),*
158 $( ; $( ($qparam:ident: $qparam_t:ty) => $qreplace:literal ),+ )?
159 $( ; $( Optional($opt_qparam:ident: $opt_qparam_t:ty) => $opt_qreplace:literal ),+ )?
160 ) => {
161 $(#[$m])*
162 pub async fn $fn_name(
163 &self,
164 $( $param: $param_t, )*
165 $($( $qparam: $qparam_t, )*)?
166 $($( $opt_qparam: Option<$opt_qparam_t>, )*)?
167 ) -> EsiResult<$ret_type> {
168 let path = self
169 .esi
170 .get_endpoint_for_op_id($op_id)?
171 $(
172 .replace($replace, &$param.to_string())
173 )*;
174 let params = vec![
175 $($(
176 ($qreplace, $qparam.to_string()),
177 )+)?
178 ];
179 $(
180 let mut params = params; // avoids unnecessary 'mut' warning
181 $(
182 if let Some($opt_qparam) = $opt_qparam {
183 params.push(($opt_qreplace, $opt_qparam.to_string()));
184 }
185 )+
186 )?
187 let params: Vec<(&str, &str)> = params.iter().map(|(a, b)| (*a, &**b)).collect();
188 self.esi.
189 query("GET", $visibility, &path, Some(¶ms), None)
190 .await
191 }
192 };
193}
194
195/// Create a function for calling a single endpoint
196/// with a POST request.
197///
198/// Follows the structure of the `api_get!` macro, with the
199/// addition of taking an additional pair of `ident` and `ty`
200/// to name and type the data that will be passed to
201/// `serde_json::to_string` for serializing for setting the
202/// request's body.
203///
204/// # Example
205///
206/// ```rust,no_run
207/// # use esi_openapi::prelude::*;
208/// # use esi_openapi::api_post;
209/// pub struct SomeGroup<'a> {
210/// pub(crate) esi: &'a Esi,
211/// }
212///
213/// impl SomeGroup<'_> {
214///
215/// api_post!(
216/// /// Docs for the generated function
217/// function_name,
218/// "some_operation_id",
219/// RequestType::Public,
220/// Vec<u64>,
221/// (alliance_id: u64) => "{alliance_id}",
222/// ids: &[u64],
223/// );
224///
225/// }
226/// # fn main() {}
227/// ```
228/// ## Result:
229///
230/// ```rust,ignore
231/// /// Docs for the generated function
232/// pub async fn function_name(&self, alliance_id: u64, ids: &[u64]) -> EsiResult<Vec<u64>> {
233/// let path = self.esi.get_endpoint_for_op_id("some_operation_id")?
234/// .replace("{alliance_id}", &alliance_id.to_string());
235/// let body = serde_json::to_string(ids);
236/// self.esi
237/// .query("GET", RequestType::Public, &path, None, Some(&body))
238/// .await
239/// }
240/// ```
241#[macro_export]
242macro_rules! api_post {
243 (
244 $(#[$m:meta])*
245 $fn_name:ident,
246 $op_id:literal,
247 $visibility:expr,
248 $ret_type:ty,
249 $( ($param:ident: $param_t:ty) => $replace:literal ),*,
250 $body_param:ident: $param_type:ty,
251 ) => {
252 $(#[$m])*
253 pub async fn $fn_name(&self, $( $param: $param_t, )* $body_param: $param_type) -> EsiResult<$ret_type> {
254 let path = self
255 .esi
256 .get_endpoint_for_op_id($op_id)?
257 $(
258 .replace($replace, &$param.to_string())
259 )*;
260 let body = serde_json::to_string($body_param)?;
261 self.esi.
262 query("POST", $visibility, &path, None, Some(&body))
263 .await
264 }
265 }
266}