Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement From<T> for Url parts #126

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 25 additions & 11 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 {
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,19 @@ 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 +684,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