Skip to content

Commit

Permalink
Implement From<T> for Url parts
Browse files Browse the repository at this point in the history
This commit implements From<T> for Url parts enums.

Closes #24
  • Loading branch information
russcam committed Jul 20, 2020
1 parent 2d1ba36 commit 593db29
Show file tree
Hide file tree
Showing 25 changed files with 3,651 additions and 999 deletions.
36 changes: 23 additions & 13 deletions api_generator/src/generator/code_gen/request/request_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ impl<'a> RequestBuilder<'a> {
enum_builder: &EnumBuilder,
default_fields: &[&syn::Ident],
) -> Tokens {
let (enum_ty, _, _) = enum_builder.clone().build();
let (enum_ty, _, _, _) = enum_builder.clone().build();
let default_fields = Self::create_default_fields(default_fields);

// default cat APIs to using text/plain Content-Type and Accept headers. Not all
Expand Down Expand Up @@ -212,12 +212,14 @@ impl<'a> RequestBuilder<'a> {
));
quote!(
#doc
pub fn new(transport: &'a Transport, parts: #enum_ty) -> Self {
#headers

pub fn new<P>(transport: &'a Transport, parts: P) -> Self
where P: Into<#enum_ty>
{
#headers

#builder_ident {
transport,
parts,
parts: parts.into(),
headers,
#(#default_fields),*,
}
Expand Down Expand Up @@ -446,7 +448,7 @@ impl<'a> RequestBuilder<'a> {

let supports_body = endpoint.supports_body();
let builder_ident = ident(builder_name);
let (enum_ty, enum_struct, enum_impl) = enum_builder.clone().build();
let (enum_ty, enum_struct, enum_impl, from_impls) = enum_builder.clone().build();

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

#enum_impl

#(#from_impls)*

#[derive(Clone, Debug)]
#[doc = #builder_doc]
pub struct #builder_expr {
Expand Down Expand Up @@ -617,11 +621,15 @@ impl<'a> RequestBuilder<'a> {
let i = ident(name);
let b = builder_ident.clone();

match (endpoint.supports_body(), is_root_method) {
(true, true) => (quote!(#i<'a, 'b>), quote!(#b<'a, 'b, ()>)),
(false, true) => (quote!(#i<'a, 'b>), quote!(#b<'a, 'b>)),
(true, false) => (quote!(#i<'b>), quote!(#b<'a, 'b, ()>)),
(false, false) => (quote!(#i<'b>), quote!(#b<'a, 'b>)),
match (endpoint.supports_body(), is_root_method, enum_builder.contains_single_parameterless_part()) {
(true, true, true) => (quote!(#i<'a, 'b>), quote!(#b<'a, 'b, ()>)),
(true, true, false) => (quote!(#i<'a, 'b, P>), quote!(#b<'a, 'b, ()>)),
(false, true, true) => (quote!(#i<'a, 'b>), quote!(#b<'a, 'b>)),
(false, true, false) => (quote!(#i<'a, 'b, P>), quote!(#b<'a, 'b>)),
(true, false, true) => (quote!(#i<'b>), quote!(#b<'a, 'b, ()>)),
(true, false, false) => (quote!(#i<'b, P>), quote!(#b<'a, 'b, ()>)),
(false, false, true) => (quote!(#i<'b>), quote!(#b<'a, 'b>)),
(false, false, false) => (quote!(#i<'b, P>), quote!(#b<'a, 'b>)),
}
};

Expand Down Expand Up @@ -672,10 +680,12 @@ impl<'a> RequestBuilder<'a> {
}
)
} else {
let (enum_ty, _, _) = enum_builder.clone().build();
let (enum_ty, _, _, _) = enum_builder.clone().build();
quote!(
#method_doc
pub fn #fn_name(&'a self, parts: #enum_ty) -> #builder_ident_ret {
pub fn #fn_name(&'a self, parts: P) -> #builder_ident_ret
where P: Into<#enum_ty>
{
#builder_ident::new(#clone_expr, parts)
}
)
Expand Down
116 changes: 113 additions & 3 deletions api_generator/src/generator/code_gen/url/enum_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use crate::generator::{
ApiEndpoint, Path,
};
use inflector::Inflector;
use std::collections::HashSet;

/// Builder for request url parts enum
///
Expand Down Expand Up @@ -190,7 +191,7 @@ impl<'a> EnumBuilder<'a> {
}

/// Build this enum and return ASTs for its type, struct declaration and impl
pub fn build(self) -> (syn::Ty, syn::Item, syn::Item) {
pub fn build(self) -> (syn::Ty, syn::Item, syn::Item, Vec<syn::Item>) {
let variants = match self.variants.len() {
0 => vec![Self::parts_none()],
_ => self.variants,
Expand Down Expand Up @@ -223,6 +224,7 @@ impl<'a> EnumBuilder<'a> {
body: Box::new(body),
});
}

let match_expr: syn::Expr =
syn::ExprKind::Match(Box::new(path_none("self").into_expr()), arms).into();

Expand Down Expand Up @@ -269,6 +271,114 @@ impl<'a> EnumBuilder<'a> {
}
};

let from_impls = {
let mut from_impls = Vec::new();

// some APIs have more than one variant that accepts the same
// tuple struct of argument values. Emit a From<T> impl only for the
// first one seen.
let mut seen_tys = HashSet::new();

for (variant, &path) in variants.iter().zip(self.paths.iter()) {
let tys: Vec<syn::Ty> = path
.path
.params()
.iter()
.map(|&p| {
let ty = &path.parts[p].ty;
typekind_to_ty(p, ty, true, false)
})
.collect();

if tys.len() > 0 && seen_tys.insert(tys.clone()) {
let enum_ident = &self.ident;
let variant_ident = &variant.ident;

let (fn_decl, stmt, path) = {
let (input_ty, stmt, from) = match tys.len() {
1 => {
let ty = &tys[0];
(
ty.clone(),
quote!(#enum_ident::#variant_ident(t)),
quote!(From<#ty>),
)
}
n => {
let input_ty = syn::Ty::Tup(tys.clone());
let tuple_destr = {
let mut idents = Vec::with_capacity(n);
for i in 0..n {
idents.push(ident(format!("t.{}", i)))
}
idents
};

(
input_ty,
quote!(#enum_ident::#variant_ident(#(#tuple_destr),*)),
quote!(From<(#(#tys),*)>),
)
}
};

(
syn::FnDecl {
inputs: vec![syn::FnArg::Captured(
syn::Pat::Path(None, path_none("t")),
input_ty,
)],
output: syn::FunctionRetTy::Ty(enum_ty.clone()),
variadic: false,
},
syn::parse_expr(stmt.to_string().as_str())
.unwrap()
.into_stmt(),
Some(syn::parse_path(from.to_string().as_str()).unwrap()),
)
};

let item = syn::ImplItem {
ident: ident("from"),
vis: syn::Visibility::Inherited,
defaultness: syn::Defaultness::Final,
attrs: vec![doc(format!(
"Builds a [{}::{}] for the {} API",
enum_ident, variant_ident, self.api_name
))],
node: syn::ImplItemKind::Method(
syn::MethodSig {
unsafety: syn::Unsafety::Normal,
constness: syn::Constness::NotConst,
abi: None,
decl: fn_decl,
generics: generics_none(),
},
syn::Block { stmts: vec![stmt] },
),
};

let item = syn::Item {
ident: ident(""),
vis: syn::Visibility::Inherited,
attrs: vec![],
node: syn::ItemKind::Impl(
syn::Unsafety::Normal,
syn::ImplPolarity::Positive,
generics.clone(),
path,
Box::new(enum_ty.clone()),
vec![item],
),
};

from_impls.push(item);
}
}

from_impls
};

let enum_decl = syn::Item {
ident: self.ident,
vis: syn::Visibility::Public,
Expand All @@ -290,7 +400,7 @@ impl<'a> EnumBuilder<'a> {
node: syn::ItemKind::Enum(variants, generics),
};

(enum_ty, enum_decl, enum_impl)
(enum_ty, enum_decl, enum_impl, from_impls)
}
}

Expand Down Expand Up @@ -384,7 +494,7 @@ mod tests {
},
);

let (enum_ty, enum_decl, enum_impl) = EnumBuilder::from(&endpoint).build();
let (enum_ty, enum_decl, enum_impl, _) = EnumBuilder::from(&endpoint).build();

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

Expand Down
57 changes: 45 additions & 12 deletions elasticsearch/src/generated/namespace_clients/async_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ impl<'b> AsyncSearchDeleteParts<'b> {
}
}
}
impl<'b> From<&'b str> for AsyncSearchDeleteParts<'b> {
#[doc = "Builds a [AsyncSearchDeleteParts::Id] for the Async Search Delete API"]
fn from(t: &'b str) -> AsyncSearchDeleteParts<'b> {
AsyncSearchDeleteParts::Id(t)
}
}
#[derive(Clone, Debug)]
#[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."]
pub struct AsyncSearchDelete<'a, 'b> {
Expand All @@ -72,11 +78,14 @@ pub struct AsyncSearchDelete<'a, 'b> {
}
impl<'a, 'b> AsyncSearchDelete<'a, 'b> {
#[doc = "Creates a new instance of [AsyncSearchDelete] with the specified API parts"]
pub fn new(transport: &'a Transport, parts: AsyncSearchDeleteParts<'b>) -> Self {
pub fn new<P>(transport: &'a Transport, parts: P) -> Self
where
P: Into<AsyncSearchDeleteParts<'b>>,
{
let headers = HeaderMap::new();
AsyncSearchDelete {
transport,
parts,
parts: parts.into(),
headers,
error_trace: None,
filter_path: None,
Expand Down Expand Up @@ -175,6 +184,12 @@ impl<'b> AsyncSearchGetParts<'b> {
}
}
}
impl<'b> From<&'b str> for AsyncSearchGetParts<'b> {
#[doc = "Builds a [AsyncSearchGetParts::Id] for the Async Search Get API"]
fn from(t: &'b str) -> AsyncSearchGetParts<'b> {
AsyncSearchGetParts::Id(t)
}
}
#[derive(Clone, Debug)]
#[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."]
pub struct AsyncSearchGet<'a, 'b> {
Expand All @@ -192,11 +207,14 @@ pub struct AsyncSearchGet<'a, 'b> {
}
impl<'a, 'b> AsyncSearchGet<'a, 'b> {
#[doc = "Creates a new instance of [AsyncSearchGet] with the specified API parts"]
pub fn new(transport: &'a Transport, parts: AsyncSearchGetParts<'b>) -> Self {
pub fn new<P>(transport: &'a Transport, parts: P) -> Self
where
P: Into<AsyncSearchGetParts<'b>>,
{
let headers = HeaderMap::new();
AsyncSearchGet {
transport,
parts,
parts: parts.into(),
headers,
error_trace: None,
filter_path: None,
Expand Down Expand Up @@ -328,6 +346,12 @@ impl<'b> AsyncSearchSubmitParts<'b> {
}
}
}
impl<'b> From<&'b [&'b str]> for AsyncSearchSubmitParts<'b> {
#[doc = "Builds a [AsyncSearchSubmitParts::Index] for the Async Search Submit API"]
fn from(t: &'b [&'b str]) -> AsyncSearchSubmitParts<'b> {
AsyncSearchSubmitParts::Index(t)
}
}
#[derive(Clone, Debug)]
#[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."]
pub struct AsyncSearchSubmit<'a, 'b, B> {
Expand Down Expand Up @@ -387,11 +411,14 @@ where
B: Body,
{
#[doc = "Creates a new instance of [AsyncSearchSubmit] with the specified API parts"]
pub fn new(transport: &'a Transport, parts: AsyncSearchSubmitParts<'b>) -> Self {
pub fn new<P>(transport: &'a Transport, parts: P) -> Self
where
P: Into<AsyncSearchSubmitParts<'b>>,
{
let headers = HeaderMap::new();
AsyncSearchSubmit {
transport,
parts,
parts: parts.into(),
headers,
_source: None,
_source_excludes: None,
Expand Down Expand Up @@ -932,18 +959,24 @@ impl<'a> AsyncSearch<'a> {
self.transport
}
#[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."]
pub fn delete<'b>(&'a self, parts: AsyncSearchDeleteParts<'b>) -> AsyncSearchDelete<'a, 'b> {
pub fn delete<'b, P>(&'a self, parts: P) -> AsyncSearchDelete<'a, 'b>
where
P: Into<AsyncSearchDeleteParts<'b>>,
{
AsyncSearchDelete::new(self.transport(), parts)
}
#[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."]
pub fn get<'b>(&'a self, parts: AsyncSearchGetParts<'b>) -> AsyncSearchGet<'a, 'b> {
pub fn get<'b, P>(&'a self, parts: P) -> AsyncSearchGet<'a, 'b>
where
P: Into<AsyncSearchGetParts<'b>>,
{
AsyncSearchGet::new(self.transport(), parts)
}
#[doc = "[Async Search Submit API](https://www.elastic.co/guide/en/elasticsearch/reference/8.0/async-search.html)\n\nExecutes a search request asynchronously."]
pub fn submit<'b>(
&'a self,
parts: AsyncSearchSubmitParts<'b>,
) -> AsyncSearchSubmit<'a, 'b, ()> {
pub fn submit<'b, P>(&'a self, parts: P) -> AsyncSearchSubmit<'a, 'b, ()>
where
P: Into<AsyncSearchSubmitParts<'b>>,
{
AsyncSearchSubmit::new(self.transport(), parts)
}
}
Expand Down
Loading

0 comments on commit 593db29

Please sign in to comment.