Skip to content

Add .build() to allow manipulation of reqwest::RequestBuilder prior to typed send() #750

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
48 changes: 43 additions & 5 deletions progenitor-impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,16 +538,22 @@ impl Generator {
input_methods: &[method::OperationMethod],
has_inner: bool,
) -> Result<TokenStream> {
let builder_struct = input_methods
let (builder_struct, built_struct): (Vec<TokenStream>, Vec<TokenStream>) = input_methods
.iter()
.map(|method| self.builder_struct(method, TagStyle::Merged, has_inner))
.collect::<Result<Vec<_>>>()?;
.collect::<Result<Vec<_>>>()?
.into_iter()
.unzip();

let builder_methods = input_methods
.iter()
.map(|method| self.builder_impl(method))
.collect::<Vec<_>>();

// The allow(unused_imports) on the `pub use` is necessary with Rust
// 1.76+, in case the generated file is not at the top level of the
// crate.

let out = quote! {
impl Client {
#(#builder_methods)*
Expand All @@ -568,6 +574,21 @@ impl Generator {
ResponseValue,
};

pub mod built {
use super::super::types;
#[allow(unused_imports)]
use super::super::{
encode_path,
ByteStream,
Error,
HeaderMap,
HeaderValue,
RequestBuilderExt,
ResponseValue,
};
#(#built_struct)*
}

#(#builder_struct)*
}

Expand All @@ -586,10 +607,12 @@ impl Generator {
tag_info: BTreeMap<&String, &openapiv3::Tag>,
has_inner: bool,
) -> Result<TokenStream> {
let builder_struct = input_methods
let (builder_struct, built_struct): (Vec<TokenStream>, Vec<TokenStream>) = input_methods
.iter()
.map(|method| self.builder_struct(method, TagStyle::Separate, has_inner))
.collect::<Result<Vec<_>>>()?;
.map(|method| self.builder_struct(method, TagStyle::Merged, has_inner))
.collect::<Result<Vec<_>>>()?
.into_iter()
.unzip();

let (traits_and_impls, trait_preludes) = self.builder_tags(input_methods, &tag_info);

Expand All @@ -615,6 +638,21 @@ impl Generator {
ResponseValue,
};

pub mod built {
use super::super::types;
#[allow(unused_imports)]
use super::super::{
encode_path,
ByteStream,
Error,
HeaderMap,
HeaderValue,
RequestBuilderExt,
ResponseValue,
};
#(#built_struct)*
}

#(#builder_struct)*
}

Expand Down
98 changes: 77 additions & 21 deletions progenitor-impl/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ impl HttpMethod {
struct MethodSigBody {
success: TokenStream,
error: TokenStream,
body: TokenStream,
build: TokenStream,
send: TokenStream,
}

struct BuilderImpl {
Expand Down Expand Up @@ -608,7 +609,8 @@ impl Generator {
let MethodSigBody {
success: success_type,
error: error_type,
body,
build,
send,
} = self.method_sig_body(method, quote! { self }, has_inner)?;

let method_impl = quote! {
Expand All @@ -620,7 +622,11 @@ impl Generator {
ResponseValue<#success_type>,
Error<#error_type>,
> {
#body
#[allow(unused_mut)]
let mut request = {
#build
}.build()?;
#send
}
};

Expand Down Expand Up @@ -777,7 +783,6 @@ impl Generator {

// Generate a unique Ident for internal variables
let url_ident = unique_ident_from("url", &param_names);
let request_ident = unique_ident_from("request", &param_names);
let response_ident = unique_ident_from("response", &param_names);
let result_ident = unique_ident_from("result", &param_names);

Expand Down Expand Up @@ -1050,12 +1055,12 @@ impl Generator {
};
let pre_hook = self.settings.pre_hook.as_ref().map(|hook| {
quote! {
(#hook)(#inner &#request_ident);
(#hook)(#inner &request);
}
});
let pre_hook_async = self.settings.pre_hook_async.as_ref().map(|hook| {
quote! {
match (#hook)(#inner &mut #request_ident).await {
match (#hook)(#inner &mut request).await {
Ok(_) => (),
Err(e) => return Err(Error::PreHookError(e.to_string())),
}
Expand All @@ -1077,25 +1082,26 @@ impl Generator {

let method_func = format_ident!("{}", method.method.as_str());

let body_impl = quote! {
let build_impl = quote! {
#url_path

#headers_build

#[allow(unused_mut)]
let mut #request_ident = #client.client
#client.client
. #method_func (#url_ident)
#accept_header
#(#body_func)*
#( .query(#query_params) )*
#headers_use
#websock_hdrs
.build()?;
};

// Assumes `request: reqwest::Request`
let send_impl = quote! {
#pre_hook
#pre_hook_async
let #result_ident = #client.client
.execute(#request_ident)
.execute(request)
.await;
#post_hook
#post_hook_async
Expand Down Expand Up @@ -1141,7 +1147,8 @@ impl Generator {
Ok(MethodSigBody {
success: response_type.into_tokens(&self.type_space),
error: error_type.into_tokens(&self.type_space),
body: body_impl,
build: build_impl,
send: send_impl,
})
}

Expand Down Expand Up @@ -1333,14 +1340,14 @@ impl Generator {
/// also has a corresponding method:
/// ```ignore
/// impl<'a> OperationId<'a> {
/// pub fn param_1<V>(self, value: V)
/// pub fn param_1<V>(self, value: V) -> Self
/// where V: std::convert::TryInto<SomeType>
/// {
/// self.param_1 = value.try_into()
/// .map_err(|_| #err_msg.to_string());
/// self
/// }
/// pub fn param_2<V>(self, value: V)
/// pub fn param_2<V>(self, value: V) -> Self
/// where V: std::convert::TryInto<SomeType>
/// {
/// self.param_2 = value.try_into()
Expand Down Expand Up @@ -1396,7 +1403,7 @@ impl Generator {
method: &OperationMethod,
tag_style: TagStyle,
has_inner: bool,
) -> Result<TokenStream> {
) -> Result<(TokenStream, TokenStream)> {
let struct_name = sanitize(&method.operation_id, Case::Pascal);
let struct_ident = format_ident!("{}", struct_name);

Expand Down Expand Up @@ -1632,8 +1639,9 @@ impl Generator {
let MethodSigBody {
success,
error,
body,
} = self.method_sig_body(method, quote! { #client_ident }, has_inner)?;
build,
send,
} = self.method_sig_body(method, quote! { client }, has_inner)?;

let send_doc = format!(
"Sends a `{}` request to `{}`",
Expand All @@ -1646,6 +1654,14 @@ impl Generator {
ResponseValue<#success>,
Error<#error>,
> {
self.build()?.send().await
}
};

let build_impl = quote! {
pub fn build(self)
-> Result<built::#struct_ident<'a>, Error<#error>>
{
// Destructure the builder for convenience.
let Self {
#client_ident,
Expand All @@ -1665,8 +1681,11 @@ impl Generator {
.map_err(Error::InvalidRequest)?;
)*

// Do the work.
#body
let request = {#build};
Ok(built::#struct_ident {
client: #client_ident,
request,
})
}
};

Expand Down Expand Up @@ -1831,7 +1850,7 @@ impl Generator {
}
};

Ok(quote! {
let builder = quote! {
#[doc = #struct_doc]
#derive
pub struct #struct_ident<'a> {
Expand All @@ -1849,9 +1868,46 @@ impl Generator {

#( #param_impls )*
#send_impl
#build_impl
#stream_impl
}
})
};

let built = quote! {
pub struct #struct_ident<'a> {
pub (crate) client: &'a super::super::Client,
pub (crate) request: reqwest::RequestBuilder,
}

impl<'a> #struct_ident<'a> {
pub async fn send(self) -> Result<
ResponseValue<#success>,
Error<#error>,
> {
let Self {
client,
request
} = self;

#[allow(unused_mut)]
let mut request = request.build()?;

#send
}

pub fn map_request<F>(self, f: F) -> Self
where F: Fn(reqwest::RequestBuilder)
-> reqwest::RequestBuilder
{
Self {
client: self.client,
request: f(self.request)
}
}
}
};

Ok((builder, built))
}

fn builder_helper(&self, method: &OperationMethod) -> BuilderImpl {
Expand Down
Loading
Loading