Skip to content

Commit 593db29

Browse files
committed
Implement From<T> for Url parts
This commit implements From<T> for Url parts enums. Closes #24
1 parent 2d1ba36 commit 593db29

25 files changed

+3651
-999
lines changed

api_generator/src/generator/code_gen/request/request_builder.rs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ impl<'a> RequestBuilder<'a> {
171171
enum_builder: &EnumBuilder,
172172
default_fields: &[&syn::Ident],
173173
) -> Tokens {
174-
let (enum_ty, _, _) = enum_builder.clone().build();
174+
let (enum_ty, _, _, _) = enum_builder.clone().build();
175175
let default_fields = Self::create_default_fields(default_fields);
176176

177177
// default cat APIs to using text/plain Content-Type and Accept headers. Not all
@@ -212,12 +212,14 @@ impl<'a> RequestBuilder<'a> {
212212
));
213213
quote!(
214214
#doc
215-
pub fn new(transport: &'a Transport, parts: #enum_ty) -> Self {
216-
#headers
217-
215+
pub fn new<P>(transport: &'a Transport, parts: P) -> Self
216+
where P: Into<#enum_ty>
217+
{
218+
#headers
219+
218220
#builder_ident {
219221
transport,
220-
parts,
222+
parts: parts.into(),
221223
headers,
222224
#(#default_fields),*,
223225
}
@@ -446,7 +448,7 @@ impl<'a> RequestBuilder<'a> {
446448

447449
let supports_body = endpoint.supports_body();
448450
let builder_ident = ident(builder_name);
449-
let (enum_ty, enum_struct, enum_impl) = enum_builder.clone().build();
451+
let (enum_ty, enum_struct, enum_impl, from_impls) = enum_builder.clone().build();
450452

451453
// collect all the fields for the builder struct. Start with url parameters
452454
let mut fields: Vec<Field> = endpoint
@@ -574,6 +576,8 @@ impl<'a> RequestBuilder<'a> {
574576

575577
#enum_impl
576578

579+
#(#from_impls)*
580+
577581
#[derive(Clone, Debug)]
578582
#[doc = #builder_doc]
579583
pub struct #builder_expr {
@@ -617,11 +621,15 @@ impl<'a> RequestBuilder<'a> {
617621
let i = ident(name);
618622
let b = builder_ident.clone();
619623

620-
match (endpoint.supports_body(), is_root_method) {
621-
(true, true) => (quote!(#i<'a, 'b>), quote!(#b<'a, 'b, ()>)),
622-
(false, true) => (quote!(#i<'a, 'b>), quote!(#b<'a, 'b>)),
623-
(true, false) => (quote!(#i<'b>), quote!(#b<'a, 'b, ()>)),
624-
(false, false) => (quote!(#i<'b>), quote!(#b<'a, 'b>)),
624+
match (endpoint.supports_body(), is_root_method, enum_builder.contains_single_parameterless_part()) {
625+
(true, true, true) => (quote!(#i<'a, 'b>), quote!(#b<'a, 'b, ()>)),
626+
(true, true, false) => (quote!(#i<'a, 'b, P>), quote!(#b<'a, 'b, ()>)),
627+
(false, true, true) => (quote!(#i<'a, 'b>), quote!(#b<'a, 'b>)),
628+
(false, true, false) => (quote!(#i<'a, 'b, P>), quote!(#b<'a, 'b>)),
629+
(true, false, true) => (quote!(#i<'b>), quote!(#b<'a, 'b, ()>)),
630+
(true, false, false) => (quote!(#i<'b, P>), quote!(#b<'a, 'b, ()>)),
631+
(false, false, true) => (quote!(#i<'b>), quote!(#b<'a, 'b>)),
632+
(false, false, false) => (quote!(#i<'b, P>), quote!(#b<'a, 'b>)),
625633
}
626634
};
627635

@@ -672,10 +680,12 @@ impl<'a> RequestBuilder<'a> {
672680
}
673681
)
674682
} else {
675-
let (enum_ty, _, _) = enum_builder.clone().build();
683+
let (enum_ty, _, _, _) = enum_builder.clone().build();
676684
quote!(
677685
#method_doc
678-
pub fn #fn_name(&'a self, parts: #enum_ty) -> #builder_ident_ret {
686+
pub fn #fn_name(&'a self, parts: P) -> #builder_ident_ret
687+
where P: Into<#enum_ty>
688+
{
679689
#builder_ident::new(#clone_expr, parts)
680690
}
681691
)

api_generator/src/generator/code_gen/url/enum_builder.rs

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use crate::generator::{
4040
ApiEndpoint, Path,
4141
};
4242
use inflector::Inflector;
43+
use std::collections::HashSet;
4344

4445
/// Builder for request url parts enum
4546
///
@@ -190,7 +191,7 @@ impl<'a> EnumBuilder<'a> {
190191
}
191192

192193
/// Build this enum and return ASTs for its type, struct declaration and impl
193-
pub fn build(self) -> (syn::Ty, syn::Item, syn::Item) {
194+
pub fn build(self) -> (syn::Ty, syn::Item, syn::Item, Vec<syn::Item>) {
194195
let variants = match self.variants.len() {
195196
0 => vec![Self::parts_none()],
196197
_ => self.variants,
@@ -223,6 +224,7 @@ impl<'a> EnumBuilder<'a> {
223224
body: Box::new(body),
224225
});
225226
}
227+
226228
let match_expr: syn::Expr =
227229
syn::ExprKind::Match(Box::new(path_none("self").into_expr()), arms).into();
228230

@@ -269,6 +271,114 @@ impl<'a> EnumBuilder<'a> {
269271
}
270272
};
271273

274+
let from_impls = {
275+
let mut from_impls = Vec::new();
276+
277+
// some APIs have more than one variant that accepts the same
278+
// tuple struct of argument values. Emit a From<T> impl only for the
279+
// first one seen.
280+
let mut seen_tys = HashSet::new();
281+
282+
for (variant, &path) in variants.iter().zip(self.paths.iter()) {
283+
let tys: Vec<syn::Ty> = path
284+
.path
285+
.params()
286+
.iter()
287+
.map(|&p| {
288+
let ty = &path.parts[p].ty;
289+
typekind_to_ty(p, ty, true, false)
290+
})
291+
.collect();
292+
293+
if tys.len() > 0 && seen_tys.insert(tys.clone()) {
294+
let enum_ident = &self.ident;
295+
let variant_ident = &variant.ident;
296+
297+
let (fn_decl, stmt, path) = {
298+
let (input_ty, stmt, from) = match tys.len() {
299+
1 => {
300+
let ty = &tys[0];
301+
(
302+
ty.clone(),
303+
quote!(#enum_ident::#variant_ident(t)),
304+
quote!(From<#ty>),
305+
)
306+
}
307+
n => {
308+
let input_ty = syn::Ty::Tup(tys.clone());
309+
let tuple_destr = {
310+
let mut idents = Vec::with_capacity(n);
311+
for i in 0..n {
312+
idents.push(ident(format!("t.{}", i)))
313+
}
314+
idents
315+
};
316+
317+
(
318+
input_ty,
319+
quote!(#enum_ident::#variant_ident(#(#tuple_destr),*)),
320+
quote!(From<(#(#tys),*)>),
321+
)
322+
}
323+
};
324+
325+
(
326+
syn::FnDecl {
327+
inputs: vec![syn::FnArg::Captured(
328+
syn::Pat::Path(None, path_none("t")),
329+
input_ty,
330+
)],
331+
output: syn::FunctionRetTy::Ty(enum_ty.clone()),
332+
variadic: false,
333+
},
334+
syn::parse_expr(stmt.to_string().as_str())
335+
.unwrap()
336+
.into_stmt(),
337+
Some(syn::parse_path(from.to_string().as_str()).unwrap()),
338+
)
339+
};
340+
341+
let item = syn::ImplItem {
342+
ident: ident("from"),
343+
vis: syn::Visibility::Inherited,
344+
defaultness: syn::Defaultness::Final,
345+
attrs: vec![doc(format!(
346+
"Builds a [{}::{}] for the {} API",
347+
enum_ident, variant_ident, self.api_name
348+
))],
349+
node: syn::ImplItemKind::Method(
350+
syn::MethodSig {
351+
unsafety: syn::Unsafety::Normal,
352+
constness: syn::Constness::NotConst,
353+
abi: None,
354+
decl: fn_decl,
355+
generics: generics_none(),
356+
},
357+
syn::Block { stmts: vec![stmt] },
358+
),
359+
};
360+
361+
let item = syn::Item {
362+
ident: ident(""),
363+
vis: syn::Visibility::Inherited,
364+
attrs: vec![],
365+
node: syn::ItemKind::Impl(
366+
syn::Unsafety::Normal,
367+
syn::ImplPolarity::Positive,
368+
generics.clone(),
369+
path,
370+
Box::new(enum_ty.clone()),
371+
vec![item],
372+
),
373+
};
374+
375+
from_impls.push(item);
376+
}
377+
}
378+
379+
from_impls
380+
};
381+
272382
let enum_decl = syn::Item {
273383
ident: self.ident,
274384
vis: syn::Visibility::Public,
@@ -290,7 +400,7 @@ impl<'a> EnumBuilder<'a> {
290400
node: syn::ItemKind::Enum(variants, generics),
291401
};
292402

293-
(enum_ty, enum_decl, enum_impl)
403+
(enum_ty, enum_decl, enum_impl, from_impls)
294404
}
295405
}
296406

@@ -384,7 +494,7 @@ mod tests {
384494
},
385495
);
386496

387-
let (enum_ty, enum_decl, enum_impl) = EnumBuilder::from(&endpoint).build();
497+
let (enum_ty, enum_decl, enum_impl, _) = EnumBuilder::from(&endpoint).build();
388498

389499
assert_eq!(ty_b("SearchParts"), enum_ty);
390500

elasticsearch/src/generated/namespace_clients/async_search.rs

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ impl<'b> AsyncSearchDeleteParts<'b> {
5858
}
5959
}
6060
}
61+
impl<'b> From<&'b str> for AsyncSearchDeleteParts<'b> {
62+
#[doc = "Builds a [AsyncSearchDeleteParts::Id] for the Async Search Delete API"]
63+
fn from(t: &'b str) -> AsyncSearchDeleteParts<'b> {
64+
AsyncSearchDeleteParts::Id(t)
65+
}
66+
}
6167
#[derive(Clone, Debug)]
6268
#[doc = "Builder for the [Async Search Delete API](https://www.elastic.co/guide/en/elasticsearch/reference/8.0/async-search.html)\n\nDeletes an async search by ID. If the search is still running, the search request will be cancelled. Otherwise, the saved search results are deleted."]
6369
pub struct AsyncSearchDelete<'a, 'b> {
@@ -72,11 +78,14 @@ pub struct AsyncSearchDelete<'a, 'b> {
7278
}
7379
impl<'a, 'b> AsyncSearchDelete<'a, 'b> {
7480
#[doc = "Creates a new instance of [AsyncSearchDelete] with the specified API parts"]
75-
pub fn new(transport: &'a Transport, parts: AsyncSearchDeleteParts<'b>) -> Self {
81+
pub fn new<P>(transport: &'a Transport, parts: P) -> Self
82+
where
83+
P: Into<AsyncSearchDeleteParts<'b>>,
84+
{
7685
let headers = HeaderMap::new();
7786
AsyncSearchDelete {
7887
transport,
79-
parts,
88+
parts: parts.into(),
8089
headers,
8190
error_trace: None,
8291
filter_path: None,
@@ -175,6 +184,12 @@ impl<'b> AsyncSearchGetParts<'b> {
175184
}
176185
}
177186
}
187+
impl<'b> From<&'b str> for AsyncSearchGetParts<'b> {
188+
#[doc = "Builds a [AsyncSearchGetParts::Id] for the Async Search Get API"]
189+
fn from(t: &'b str) -> AsyncSearchGetParts<'b> {
190+
AsyncSearchGetParts::Id(t)
191+
}
192+
}
178193
#[derive(Clone, Debug)]
179194
#[doc = "Builder for the [Async Search Get API](https://www.elastic.co/guide/en/elasticsearch/reference/8.0/async-search.html)\n\nRetrieves the results of a previously submitted async search request given its ID."]
180195
pub struct AsyncSearchGet<'a, 'b> {
@@ -192,11 +207,14 @@ pub struct AsyncSearchGet<'a, 'b> {
192207
}
193208
impl<'a, 'b> AsyncSearchGet<'a, 'b> {
194209
#[doc = "Creates a new instance of [AsyncSearchGet] with the specified API parts"]
195-
pub fn new(transport: &'a Transport, parts: AsyncSearchGetParts<'b>) -> Self {
210+
pub fn new<P>(transport: &'a Transport, parts: P) -> Self
211+
where
212+
P: Into<AsyncSearchGetParts<'b>>,
213+
{
196214
let headers = HeaderMap::new();
197215
AsyncSearchGet {
198216
transport,
199-
parts,
217+
parts: parts.into(),
200218
headers,
201219
error_trace: None,
202220
filter_path: None,
@@ -328,6 +346,12 @@ impl<'b> AsyncSearchSubmitParts<'b> {
328346
}
329347
}
330348
}
349+
impl<'b> From<&'b [&'b str]> for AsyncSearchSubmitParts<'b> {
350+
#[doc = "Builds a [AsyncSearchSubmitParts::Index] for the Async Search Submit API"]
351+
fn from(t: &'b [&'b str]) -> AsyncSearchSubmitParts<'b> {
352+
AsyncSearchSubmitParts::Index(t)
353+
}
354+
}
331355
#[derive(Clone, Debug)]
332356
#[doc = "Builder for the [Async Search Submit API](https://www.elastic.co/guide/en/elasticsearch/reference/8.0/async-search.html)\n\nExecutes a search request asynchronously."]
333357
pub struct AsyncSearchSubmit<'a, 'b, B> {
@@ -387,11 +411,14 @@ where
387411
B: Body,
388412
{
389413
#[doc = "Creates a new instance of [AsyncSearchSubmit] with the specified API parts"]
390-
pub fn new(transport: &'a Transport, parts: AsyncSearchSubmitParts<'b>) -> Self {
414+
pub fn new<P>(transport: &'a Transport, parts: P) -> Self
415+
where
416+
P: Into<AsyncSearchSubmitParts<'b>>,
417+
{
391418
let headers = HeaderMap::new();
392419
AsyncSearchSubmit {
393420
transport,
394-
parts,
421+
parts: parts.into(),
395422
headers,
396423
_source: None,
397424
_source_excludes: None,
@@ -932,18 +959,24 @@ impl<'a> AsyncSearch<'a> {
932959
self.transport
933960
}
934961
#[doc = "[Async Search Delete API](https://www.elastic.co/guide/en/elasticsearch/reference/8.0/async-search.html)\n\nDeletes an async search by ID. If the search is still running, the search request will be cancelled. Otherwise, the saved search results are deleted."]
935-
pub fn delete<'b>(&'a self, parts: AsyncSearchDeleteParts<'b>) -> AsyncSearchDelete<'a, 'b> {
962+
pub fn delete<'b, P>(&'a self, parts: P) -> AsyncSearchDelete<'a, 'b>
963+
where
964+
P: Into<AsyncSearchDeleteParts<'b>>,
965+
{
936966
AsyncSearchDelete::new(self.transport(), parts)
937967
}
938968
#[doc = "[Async Search Get API](https://www.elastic.co/guide/en/elasticsearch/reference/8.0/async-search.html)\n\nRetrieves the results of a previously submitted async search request given its ID."]
939-
pub fn get<'b>(&'a self, parts: AsyncSearchGetParts<'b>) -> AsyncSearchGet<'a, 'b> {
969+
pub fn get<'b, P>(&'a self, parts: P) -> AsyncSearchGet<'a, 'b>
970+
where
971+
P: Into<AsyncSearchGetParts<'b>>,
972+
{
940973
AsyncSearchGet::new(self.transport(), parts)
941974
}
942975
#[doc = "[Async Search Submit API](https://www.elastic.co/guide/en/elasticsearch/reference/8.0/async-search.html)\n\nExecutes a search request asynchronously."]
943-
pub fn submit<'b>(
944-
&'a self,
945-
parts: AsyncSearchSubmitParts<'b>,
946-
) -> AsyncSearchSubmit<'a, 'b, ()> {
976+
pub fn submit<'b, P>(&'a self, parts: P) -> AsyncSearchSubmit<'a, 'b, ()>
977+
where
978+
P: Into<AsyncSearchSubmitParts<'b>>,
979+
{
947980
AsyncSearchSubmit::new(self.transport(), parts)
948981
}
949982
}

0 commit comments

Comments
 (0)