diff --git a/services/autorust/codegen/src/codegen.rs b/services/autorust/codegen/src/codegen.rs index fd844d63b7..bfb17abf68 100644 --- a/services/autorust/codegen/src/codegen.rs +++ b/services/autorust/codegen/src/codegen.rs @@ -143,6 +143,9 @@ impl TypeNameCode { pub fn is_string(&self) -> bool { self.type_name == Some(TypeName::String) } + pub fn is_bytes(&self) -> bool { + self.type_name == Some(TypeName::Bytes) + } pub fn is_date_time(&self) -> bool { self.type_name == Some(TypeName::DateTime) } diff --git a/services/autorust/codegen/src/codegen_operations.rs b/services/autorust/codegen/src/codegen_operations.rs index 87b797627d..a36cd933ee 100644 --- a/services/autorust/codegen/src/codegen_operations.rs +++ b/services/autorust/codegen/src/codegen_operations.rs @@ -14,7 +14,7 @@ use heck::ToSnakeCase; use indexmap::IndexMap; use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; -use std::collections::{BTreeSet, HashSet}; +use std::collections::BTreeSet; pub mod query_param { pub const API_VERSION: &str = "api-version"; @@ -219,10 +219,10 @@ pub fn create_operations(cg: &CodeGen) -> Result { Some(operation_code) => { let OperationCode { mut builder_instances, - module_code, + mut module_code, } = code; operation_code.builder_instances.append(&mut builder_instances); - operation_code.module_code.extend(module_code); + operation_code.module_code.append(&mut module_code); } None => { operations_code.insert(module_name, code); @@ -249,7 +249,7 @@ pub fn create_operations(cg: &CodeGen) -> Result { impl Client { #builders } - #module_code + #(#module_code)* } }); } @@ -258,7 +258,7 @@ pub fn create_operations(cg: &CodeGen) -> Result { impl Client { #builders } - #module_code + #(#module_code)* }); } } @@ -266,9 +266,17 @@ pub fn create_operations(cg: &CodeGen) -> Result { Ok(file) } +struct OperationModuleCode { + module_name: Ident, + response_code: ResponseCode, + builder_struct_code: BuilderStructCode, + builder_setters_code: BuilderSettersCode, + builder_future_code: BuilderFutureCode, +} + struct OperationCode { builder_instances: Vec, - module_code: TokenStream, + module_code: Vec, } struct WebOperationGen(WebOperation); @@ -321,6 +329,12 @@ impl WebOperationGen { fn produces(&self) -> Vec<&str> { self.0.produces.iter().map(String::as_str).collect() } + + fn pageable(&self) -> Option { + self.0.pageable.as_ref().map(|p| Pageable { + next_link_name: p.next_link_name.clone(), + }) + } } /// Creating a function name from the path and verb when an operationId is not specified. @@ -331,12 +345,14 @@ fn create_function_name(verb: &WebVerb, path: &str) -> String { path.join("_") } -struct RequestCode { +/// Calls `azure_core::Request::new` and set the authentication. +struct NewRequestCode { auth: AuthCode, verb: WebVerb, + path: String, } -impl ToTokens for RequestCode { +impl ToTokens for NewRequestCode { fn to_tokens(&self, tokens: &mut TokenStream) { let auth = &self.auth; let verb = verb_to_tokens(&self.verb); @@ -347,8 +363,10 @@ impl ToTokens for RequestCode { } } -// Only bearer token authentication is supported right now. +/// Sets the authentication. +/// Only bearer token authentication is supported right now. struct AuthCode {} + impl ToTokens for AuthCode { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.extend(quote! { @@ -373,12 +391,13 @@ fn verb_to_tokens(verb: &WebVerb) -> TokenStream { } } -struct BuildRequestParamsCode { +/// Sets all of the request parameters. +struct SetRequestParamsCode { content_type: String, params: FunctionParams, } -impl ToTokens for BuildRequestParamsCode { +impl ToTokens for SetRequestParamsCode { fn to_tokens(&self, tokens: &mut TokenStream) { for param in self.params.params() { let FunctionParam { @@ -518,326 +537,410 @@ impl ToTokens for BuildRequestParamsCode { // Create code for the web operation fn create_operation_code(cg: &CodeGen, operation: &WebOperationGen) -> Result { - let params = parse_path_params(&operation.0.path); - let params: Result> = params - .iter() - .map(|s| { - let param = s.to_snake_case_ident()?; - Ok(quote! { &this.#param }) - }) - .collect(); - let params = params?; - let url_str_args = quote! { #(#params),* }; - - let fpath = format!("{{}}{}", &format_path(&operation.0.path)); - - let parameters = operation.0.parameters(); - let param_names: HashSet<_> = parameters.iter().map(|p| p.name()).collect(); - let has_param_api_version = param_names.contains(query_param::API_VERSION); - let mut skip = parse_query_params(&operation.0.path)?; - skip.insert(query_param::API_VERSION.to_string()); - let parameters: Vec<&WebParameter> = parameters.clone().into_iter().filter(|p| !skip.contains(p.name())).collect(); - let parameters = create_function_params_code(¶meters)?; + let parameters = FunctionParams::new(operation)?; let verb = operation.0.verb.clone(); let is_post = verb == WebVerb::Post; let auth = AuthCode {}; - let new_request_code = RequestCode { verb, auth }; - - let mut ts_request_builder = TokenStream::new(); // TODO change to type - - // api-version param - if has_param_api_version { - let api_version = operation.api_version(); - ts_request_builder.extend(quote! { - req.url_mut().query_pairs_mut().append_pair(azure_core::query_param::API_VERSION, #api_version); - }); - } + let new_request_code = NewRequestCode { + verb, + auth, + path: operation.0.path.clone(), + }; // get the consumes content-type from the operation, else the spec, else default to json let consumes = operation .pick_consumes() - .unwrap_or_else(|| cg.spec.pick_consumes().unwrap_or(content_type::APPLICATION_JSON)); - - // params - let build_request_params = BuildRequestParamsCode { - content_type: consumes.to_string(), - params: parameters.clone(), + .unwrap_or_else(|| cg.spec.pick_consumes().unwrap_or(content_type::APPLICATION_JSON)) + .to_string(); + let request_builder = SetRequestCode { + has_param_api_version: parameters.has_api_version, + api_version: operation.api_version().to_string(), + consumes, + parameters: parameters.clone(), + has_body_parameter: operation.0.has_body_parameter(), + is_post, + }; + let in_operation_group = operation.0.in_group(); + let builder_instance_code = BuilderInstanceCode::new(operation, ¶meters, in_operation_group)?; + let builder_struct_code = BuilderStructCode::new(¶meters, in_operation_group); + let builder_setters_code = BuilderSettersCode::new(¶meters); + let response_code = ResponseCode::new(operation)?; + let long_running_operation = operation.0.long_running_operation; + let builder_future_code = BuilderFutureCode::new(new_request_code, request_builder, response_code.clone(), long_running_operation)?; + + let module_code = OperationModuleCode { + module_name: operation.function_name()?, + response_code, + builder_struct_code, + builder_setters_code, + builder_future_code, }; - ts_request_builder.extend(build_request_params.into_token_stream()); - let has_body_parameter = operation.0.has_body_parameter(); - if !has_body_parameter { - ts_request_builder.extend(quote! { - let req_body = azure_core::EMPTY_BODY; - }); - } + Ok(OperationCode { + builder_instances: vec![builder_instance_code], + module_code: vec![module_code], + }) +} - // if it is a post and there is no body, set the Content-Length to 0 - if is_post && !has_body_parameter { - ts_request_builder.extend(quote! { - req.insert_header(azure_core::headers::CONTENT_LENGTH, "0"); - }); - } +/// Set all body and parameters for the request. +struct SetRequestCode { + has_param_api_version: bool, + api_version: String, + consumes: String, + parameters: FunctionParams, + has_body_parameter: bool, + is_post: bool, +} - let responses = &operation.0.responses; - let success_responses = get_success_responses(responses); - let is_single_response = success_responses.len() == 1; +impl ToTokens for SetRequestCode { + fn to_tokens(&self, tokens: &mut TokenStream) { + // api-version param + if self.has_param_api_version { + let api_version = &self.api_version; + tokens.extend(quote! { + req.url_mut().query_pairs_mut().append_pair(azure_core::query_param::API_VERSION, #api_version); + }); + } - /* - let fresponse = if is_single_response { - let tp = create_response_type(&success_responses[0])?.unwrap_or(quote! { () }); - quote! { std::result::Result<#tp> } - } else { - quote! { std::result::Result } - }; - */ - - let mut response_enum = TokenStream::new(); - if is_single_response { - let tp = create_response_type(&success_responses[0])? - .map(TypeNameCode::into_token_stream) - .unwrap_or(quote! { () }); - response_enum.extend(quote! { - type Response = #tp; - }); - } else { - let mut success_responses_ts = TokenStream::new(); - let mut continuation_response = TokenStream::new(); - for (status_code, rsp) in &success_responses { - let tp = create_response_type(rsp)?; - let has_tp = tp.is_some(); - - let tp = match tp { - Some(tp) => quote! { (#tp) }, - None => quote! {}, - }; - let enum_type_name = get_response_type_ident(status_code)?; - success_responses_ts.extend(quote! { #enum_type_name#tp, }); + // params + let build_request_params = SetRequestParamsCode { + content_type: self.consumes.clone(), + params: self.parameters.clone(), + }; + tokens.extend(build_request_params.into_token_stream()); - if has_tp { - continuation_response.extend(quote! { - Self::#enum_type_name(x) => x.continuation(), - }); - } else { - continuation_response.extend(quote! { Self::#enum_type_name => None, }); - } + if !self.has_body_parameter { + tokens.extend(quote! { + let req_body = azure_core::EMPTY_BODY; + }); } - response_enum.extend(quote! { - #[derive(Debug)] - pub enum Response { - #success_responses_ts - } - }); - if let Some(_pageable) = &operation.0.pageable { - response_enum.extend(quote! { - impl azure_core::Continuable for Response { - type Continuation = String; - fn continuation(&self) -> Option { - match self { - #continuation_response - } - } - } + // if it is a post and there is no body, set the Content-Length to 0 + if self.is_post && !self.has_body_parameter { + tokens.extend(quote! { + req.insert_header(azure_core::headers::CONTENT_LENGTH, "0"); }); } } +} - let mut match_status = TokenStream::new(); - for (status_code, rsp) in &success_responses { - match status_code { - autorust_openapi::StatusCode::Code(_) => { - let tp = create_response_type(rsp)?.map(TypeNameCode::into_token_stream); - let rsp_value = create_rsp_value(tp.as_ref()); - let status_code_name = get_status_code_ident(status_code)?; - let response_type_name = get_response_type_ident(status_code)?; - - if is_single_response { - match tp { - Some(_) => { - match_status.extend(quote! { - azure_core::StatusCode::#status_code_name => { - let rsp_body = rsp_stream.collect().await?; - #rsp_value - Ok(rsp_value) - } - }); - } - None => { - match_status.extend(quote! { - azure_core::StatusCode::#status_code_name => { - Ok(()) - } - }); - } +/// The response for an operation. +/// An operation may have multiple valid status codes. +#[derive(Clone)] +struct ResponseCode { + status_responses: Vec, + pageable: Option, +} + +#[derive(Clone)] +struct Pageable { + next_link_name: Option, +} + +/// A single status code response of an operation. +#[derive(Clone)] +struct StatusResponseCode { + name: Ident, + status_code_name: Ident, + response_type: Option, +} + +impl ResponseCode { + fn new(operation: &WebOperationGen) -> Result { + let mut status_responses = Vec::new(); + let responses = &operation.0.responses; + for (status_code, rsp) in &get_success_responses(responses) { + status_responses.push(StatusResponseCode { + name: get_response_type_ident(status_code)?, + status_code_name: get_status_code_ident(status_code)?, + response_type: create_response_type(rsp)?, + }); + } + Ok(Self { + status_responses, + pageable: operation.pageable(), + }) + } + + fn is_single_response(&self) -> bool { + self.status_responses.len() == 1 + } +} + +impl ToTokens for ResponseCode { + fn to_tokens(&self, tokens: &mut TokenStream) { + if self.is_single_response() { + let tp = self.status_responses[0] + .response_type + .as_ref() + .map(TypeNameCode::to_token_stream) + .unwrap_or(quote! { () }); + tokens.extend(quote! { + type Response = #tp; + }); + } else { + let mut success_responses_ts = TokenStream::new(); + let mut continuation_response = TokenStream::new(); + for status_response in &self.status_responses { + let response_type = &status_response.response_type; + let code = match response_type { + Some(response_type) => quote! { (#response_type) }, + None => quote! {}, + }; + let enum_type_name = &status_response.name; + success_responses_ts.extend(quote! { #enum_type_name#code, }); + + let continuation = if response_type.is_some() { + quote! { + Self::#enum_type_name(x) => x.continuation(), } } else { - match tp { - Some(_) => { - match_status.extend(quote! { - azure_core::StatusCode::#status_code_name => { - let rsp_body = rsp_stream.collect().await?; - #rsp_value - Ok(Response::#response_type_name(rsp_value)) - } - }); - } - None => { - match_status.extend(quote! { - azure_core::StatusCode::#status_code_name => { - Ok(Response::#response_type_name) - } - }); + quote! { Self::#enum_type_name => None, } + }; + continuation_response.extend(continuation); + } + tokens.extend(quote! { + #[derive(Debug)] + pub enum Response { + #success_responses_ts + } + }); + + if self.pageable.is_some() { + tokens.extend(quote! { + impl azure_core::Continuable for Response { + type Continuation = String; + fn continuation(&self) -> Option { + match self { + #continuation_response + } } } - } + }); } - autorust_openapi::StatusCode::Default => {} } } - match_status.extend(quote! { - status_code => { - Err(azure_core::error::Error::from(azure_core::error::ErrorKind::HttpResponse { status: status_code, error_code: None })) - } - }); +} - let in_group = operation.0.in_group(); - let builder_instance_code = BuilderInstanceCode::new(operation, ¶meters, in_group)?; - let builder_struct_code = BuilderStructCode::new(¶meters, in_group); - let builder_setters_code = BuilderSettersCode::new(¶meters); +/// The `into_future` function of the request builder. +struct BuilderFutureCode { + new_request_code: NewRequestCode, + request_builder: SetRequestCode, + response_code: ResponseCode, + url_args: Vec, + long_running_operation: bool, +} - let basic_future = quote! { - pub fn into_future(self) -> futures::future::BoxFuture<'static, azure_core::Result> { - Box::pin({ - let this = self.clone(); - async move { - let url = azure_core::Url::parse(&format!(#fpath, this.client.endpoint(), #url_str_args))?; - #new_request_code - #ts_request_builder - req.set_body(req_body); - let rsp = this.client.send(&mut req).await?; - let (rsp_status, rsp_headers, rsp_stream) = rsp.deconstruct(); - match rsp_status { - #match_status - } +impl BuilderFutureCode { + fn new( + new_request_code: NewRequestCode, + request_builder: SetRequestCode, + response_code: ResponseCode, + long_running_operation: bool, + ) -> Result { + let params = parse_path_params(&new_request_code.path); + let url_args: Result> = params.iter().map(|s| s.to_snake_case_ident()).collect(); + let url_args = url_args?; + Ok(Self { + new_request_code, + request_builder, + response_code, + url_args, + long_running_operation, + }) + } + + fn is_single_response(&self) -> bool { + self.response_code.status_responses.len() == 1 + } +} + +impl ToTokens for BuilderFutureCode { + fn to_tokens(&self, tokens: &mut TokenStream) { + let new_request_code = &self.new_request_code; + let request_builder = &self.request_builder; + + let url_args = self.url_args.iter().map(|url_arg| { + quote! { &this.#url_arg } + }); + let url_str_args = quote! { #(#url_args),* }; + + let fpath = format!("{{}}{}", &format_path(&new_request_code.path)); + + let mut match_status = TokenStream::new(); + for status_response in &self.response_code.status_responses { + let response_type = &status_response.response_type; + let rsp_value = create_rsp_value(response_type.as_ref()); + let status_code_name = &status_response.status_code_name; + let response_type_name = &status_response.name; + + let status_code_code = if self.is_single_response() { + match response_type { + Some(_) => quote! { + let rsp_body = rsp_stream.collect().await?; + #rsp_value + Ok(rsp_value) + }, + None => quote! { Ok(()) }, } - }) + } else { + match response_type { + Some(_) => quote! { + let rsp_body = rsp_stream.collect().await?; + #rsp_value + Ok(Response::#response_type_name(rsp_value)) + }, + None => quote! { Ok(Response::#response_type_name) }, + } + }; + match_status.extend(quote! { + azure_core::StatusCode::#status_code_name => { + #status_code_code + } + }); } - }; + match_status.extend(quote! { + status_code => { + Err(azure_core::error::Error::from(azure_core::error::ErrorKind::HttpResponse { status: status_code, error_code: None })) + } + }); - let fut = if let Some(pageable) = &operation.0.pageable { - // TODO: Pageable requires the values to be part of the response schema, - // however, some schemas do this via the header x-ms-continuation rather than - // provide a next_link_name. For now, those cases get documented that we don't - // poll and move on. - if pageable.next_link_name.is_none() { - // most often when this happens, the continuation token is provided - // by an HTTP Header x-ms-continuation, which should be extracted - // from the response. - // - // Note, this is only *sometimes* this is specified in the spec. - // - // Ref: https://github.com/Azure/azure-sdk-for-rust/issues/446 - let mut fut = - quote! { #[doc = "only the first response will be fetched as the continuation token is not part of the response schema"]}; - fut.extend(basic_future); - fut - } else { - let mut stream_api_version = quote! {}; - - // per discussion in SDK meeting, we should always set the - // api-version on the request if we have a version. - if has_param_api_version { - let api_version = operation.api_version(); - stream_api_version = quote! { - let has_api_version_already = req.url_mut().query_pairs().any(|(k, _)| k == azure_core::query_param::API_VERSION); - if !has_api_version_already { - req.url_mut().query_pairs_mut().append_pair(azure_core::query_param::API_VERSION, #api_version); + let basic_future = quote! { + pub fn into_future(self) -> futures::future::BoxFuture<'static, azure_core::Result> { + Box::pin({ + let this = self.clone(); + async move { + let url = azure_core::Url::parse(&format!(#fpath, this.client.endpoint(), #url_str_args))?; + #new_request_code + #request_builder + req.set_body(req_body); + let rsp = this.client.send(&mut req).await?; + let (rsp_status, rsp_headers, rsp_stream) = rsp.deconstruct(); + match rsp_status { + #match_status + } } - }; + }) } + }; - quote! { - pub fn into_stream(self) -> azure_core::Pageable { - let make_request = move |continuation: Option| { - let this = self.clone(); - async move { - let mut url = azure_core::Url::parse(&format!(#fpath, this.client.endpoint(), #url_str_args))?; - - let rsp = match continuation { - Some(value) => { - url.set_path(""); - url = url.join(&value)?; - #new_request_code - #stream_api_version - let req_body = azure_core::EMPTY_BODY; - req.set_body(req_body); - this.client.send(&mut req).await? - } - None => { - #new_request_code - #ts_request_builder - req.set_body(req_body); - this.client.send(&mut req).await? - } - }; - let (rsp_status, rsp_headers, rsp_stream) = rsp.deconstruct(); - match rsp_status { - #match_status - } + let fut = if let Some(pageable) = &self.response_code.pageable { + // TODO: Pageable requires the values to be part of the response schema, + // however, some schemas do this via the header x-ms-continuation rather than + // provide a next_link_name. For now, those cases get documented that we don't + // poll and move on. + if pageable.next_link_name.is_none() { + // most often when this happens, the continuation token is provided + // by an HTTP Header x-ms-continuation, which should be extracted + // from the response. + // + // Note, this is only *sometimes* this is specified in the spec. + // + // Ref: https://github.com/Azure/azure-sdk-for-rust/issues/446 + let mut fut = quote! { #[doc = "only the first response will be fetched as the continuation token is not part of the response schema"]}; + fut.extend(basic_future); + fut + } else { + let mut stream_api_version = quote! {}; + + // per discussion in SDK meeting, we should always set the + // api-version on the request if we have a version. + if request_builder.has_param_api_version { + let api_version = &request_builder.api_version; + stream_api_version = quote! { + let has_api_version_already = req.url_mut().query_pairs().any(|(k, _)| k == azure_core::query_param::API_VERSION); + if !has_api_version_already { + req.url_mut().query_pairs_mut().append_pair(azure_core::query_param::API_VERSION, #api_version); } }; + } - azure_core::Pageable::new(make_request) + quote! { + pub fn into_stream(self) -> azure_core::Pageable { + let make_request = move |continuation: Option| { + let this = self.clone(); + async move { + let mut url = azure_core::Url::parse(&format!(#fpath, this.client.endpoint(), #url_str_args))?; + + let rsp = match continuation { + Some(value) => { + url.set_path(""); + url = url.join(&value)?; + #new_request_code + #stream_api_version + let req_body = azure_core::EMPTY_BODY; + req.set_body(req_body); + this.client.send(&mut req).await? + } + None => { + #new_request_code + #request_builder + req.set_body(req_body); + this.client.send(&mut req).await? + } + }; + let (rsp_status, rsp_headers, rsp_stream) = rsp.deconstruct(); + match rsp_status { + #match_status + } + } + }; + + azure_core::Pageable::new(make_request) + } } } - } - } else if operation.0.long_running_operation { - // TODO: Long running options should also move to the Pageable stream - // model, however this is not possible at the moment because the - // continuation token is often not returned in the response body, but - // instead a header which we don't include as part of the response - // model. - // - // As is, Pageable requires implementing the Continuable trait on the - // response object. - // - // ref: https://github.com/Azure/azure-sdk-for-rust/issues/741 - let mut fut = quote! {#[doc = "only the first response will be fetched as long running operations are not supported yet"]}; - fut.extend(basic_future); - fut - } else { - basic_future - }; + } else if self.long_running_operation { + // TODO: Long running options should also move to the Pageable stream + // model, however this is not possible at the moment because the + // continuation token is often not returned in the response body, but + // instead a header which we don't include as part of the response + // model. + // + // As is, Pageable requires implementing the Continuable trait on the + // response object. + // + // ref: https://github.com/Azure/azure-sdk-for-rust/issues/741 + let mut fut = quote! {#[doc = "only the first response will be fetched as long running operations are not supported yet"]}; + fut.extend(basic_future); + fut + } else { + basic_future + }; + tokens.extend(fut); + } +} - let fname = operation.function_name()?; - let module_code = quote! { +impl ToTokens for OperationModuleCode { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + module_name, + response_code, + builder_struct_code, + builder_setters_code, + builder_future_code, + } = &self; + tokens.extend(quote! { + pub mod #module_name { + use super::models; - pub mod #fname { - use super::models; + #response_code - #response_enum + #builder_struct_code - #builder_struct_code + impl Builder { + #builder_setters_code + #builder_future_code + } - impl Builder { - #builder_setters_code - #fut } - - } - - }; - - Ok(OperationCode { - builder_instances: vec![builder_instance_code], - module_code, - }) + }) + } } -fn create_rsp_value(tp: Option<&TokenStream>) -> TokenStream { - if tp.map(|tp| tp.to_string()) == Some("bytes :: Bytes".to_owned()) { +fn create_rsp_value(tp: Option<&TypeNameCode>) -> TokenStream { + if tp.map(TypeNameCode::is_bytes) == Some(true) { quote! { let rsp_value = rsp_body; } @@ -897,8 +1000,36 @@ impl FunctionParam { #[derive(Clone)] struct FunctionParams { params: Vec, + has_api_version: bool, } impl FunctionParams { + fn new(operation: &WebOperationGen) -> Result { + let parameters = operation.0.parameters(); + let has_api_version = parameters.iter().any(|p| p.name() == query_param::API_VERSION); + let mut skip = parse_query_params(&operation.0.path)?; + skip.insert(query_param::API_VERSION.to_string()); + let parameters: Vec<&WebParameter> = parameters.clone().into_iter().filter(|p| !skip.contains(p.name())).collect(); + + let mut params = Vec::new(); + for param in parameters.iter().filter(|p| !skip.contains(p.name())) { + let name = param.name().to_owned(); + let description = param.description().clone(); + let variable_name = name.to_snake_case_ident()?; + let type_name = type_name_gen(¶m.type_name()?)?.qualify_models(true).optional(!param.required()); + let kind = ParamKind::from(param.type_()); + let collection_format = param.collection_format().clone(); + params.push(FunctionParam { + name, + description, + variable_name, + type_name, + kind, + collection_format, + }); + } + Ok(Self { params, has_api_version }) + } + fn params(&self) -> Vec<&FunctionParam> { self.params.iter().collect() } @@ -941,38 +1072,18 @@ impl ToTokens for FunctionCallParamsCode { } } -fn create_function_params_code(parameters: &[&WebParameter]) -> Result { - let mut params = Vec::new(); - for param in parameters.iter() { - let name = param.name().to_owned(); - let description = param.description().clone(); - let variable_name = name.to_snake_case_ident()?; - let type_name = type_name_gen(¶m.type_name()?)?.qualify_models(true).optional(!param.required()); - let kind = ParamKind::from(param.type_()); - let collection_format = param.collection_format().clone(); - params.push(FunctionParam { - name, - description, - variable_name, - type_name, - kind, - collection_format, - }); - } - Ok(FunctionParams { params }) -} - +/// Create the client function that produces the request builder instance. #[derive(Clone)] struct BuilderInstanceCode { summary: Option, description: Option, fname: Ident, parameters: FunctionParams, - in_group: bool, + in_operation_group: bool, } impl BuilderInstanceCode { - fn new(operation: &WebOperationGen, parameters: &FunctionParams, in_group: bool) -> Result { + fn new(operation: &WebOperationGen, parameters: &FunctionParams, in_operation_group: bool) -> Result { let fname = operation.function_name()?; let summary = operation.0.summary.clone(); let description = operation.0.description.clone(); @@ -981,7 +1092,7 @@ impl BuilderInstanceCode { description, fname, parameters: parameters.clone(), - in_group, + in_operation_group, }) } } @@ -989,7 +1100,7 @@ impl BuilderInstanceCode { impl ToTokens for BuilderInstanceCode { fn to_tokens(&self, tokens: &mut TokenStream) { let mut params: Vec = Vec::new(); - if self.in_group { + if self.in_operation_group { params.push(quote! { client: self.0.clone() }); } else { params.push(quote! { client: self.clone() }); @@ -1064,17 +1175,18 @@ impl ToTokens for BuilderInstanceCode { } } +/// The request builder struct type, not the impl. #[derive(Clone)] struct BuilderStructCode { parameters: FunctionParams, - in_group: bool, + in_operation_group: bool, } impl BuilderStructCode { - fn new(parameters: &FunctionParams, in_group: bool) -> Self { + fn new(parameters: &FunctionParams, in_operation_group: bool) -> Self { Self { parameters: parameters.clone(), - in_group, + in_operation_group, } } } @@ -1082,7 +1194,7 @@ impl BuilderStructCode { impl ToTokens for BuilderStructCode { fn to_tokens(&self, tokens: &mut TokenStream) { let mut params: Vec = Vec::new(); - if self.in_group { + if self.in_operation_group { params.push(quote! { pub(crate) client: super::super::Client }); } else { params.push(quote! { pub(crate) client: super::Client }); @@ -1112,6 +1224,7 @@ impl ToTokens for BuilderStructCode { } } +/// The setter functions for the request builder. #[derive(Clone)] struct BuilderSettersCode { parameters: FunctionParams,