From 0a4239b678de08792b59f8997de4ee8080d3d62f Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Sat, 15 Dec 2018 21:22:07 +0100 Subject: [PATCH] Get rid of almost all calls to append_all --- src/api/mod.rs | 66 ++++++++++++---------- src/api/request.rs | 135 ++++++++++++++++++++++---------------------- src/api/response.rs | 85 ++++++++++++++-------------- 3 files changed, 144 insertions(+), 142 deletions(-) diff --git a/src/api/mod.rs b/src/api/mod.rs index 7287d397..1b807406 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -82,7 +82,22 @@ impl ToTokens for Api { let request_path_init_fields = self.request.request_path_init_fields(); - let mut set_tokens = quote! { + let path_segments = path_str[1..].split('/').into_iter(); + let path_segment_push = path_segments.clone().map(|segment| { + let arg = if segment.starts_with(':') { + let path_var = &segment[1..]; + let path_var_ident = Ident::new(path_var, Span::call_site()); + quote!(&request_path.#path_var_ident.to_string()) + } else { + quote!(#segment) + }; + + quote! { + path_segments.push(#arg); + } + }); + + let set_tokens = quote! { let request_path = RequestPath { #request_path_init_fields }; @@ -91,28 +106,22 @@ impl ToTokens for Api { // cannot-be-base url like `mailto:` or `data:`, which is not // the case for our placeholder url. let mut path_segments = url.path_segments_mut().unwrap(); + #(#path_segment_push)* }; - let mut parse_tokens = TokenStream::new(); - - for (i, segment) in path_str[1..].split('/').into_iter().enumerate() { - set_tokens.append_all(quote! { - path_segments.push - }); - - if segment.starts_with(':') { + let path_fields = path_segments + .enumerate() + .filter(|(_, s)| s.starts_with(':')) + .map(|(i, segment)| { let path_var = &segment[1..]; let path_var_ident = Ident::new(path_var, Span::call_site()); - - set_tokens.append_all(quote! { - (&request_path.#path_var_ident.to_string()); - }); - - let path_field = self.request.path_field(path_var) + let path_field = self + .request + .path_field(path_var) .expect("expected request to have path field"); let ty = &path_field.ty; - parse_tokens.append_all(quote! { + quote! { #path_var_ident: { let segment = path_segments.get(#i).unwrap().as_bytes(); let decoded = @@ -120,14 +129,13 @@ impl ToTokens for Api { .decode_utf8_lossy(); #ty::deserialize(decoded.into_deserializer()) .map_err(|e: ::serde_json::error::Error| e)? - }, - }); - } else { - set_tokens.append_all(quote! { - (#segment); - }); - } - } + } + } + }); + + let parse_tokens = quote! { + #(#path_fields,)* + }; (set_tokens, parse_tokens) } else { @@ -168,13 +176,11 @@ impl ToTokens for Api { }; let add_headers_to_request = if self.request.has_header_fields() { - let mut header_tokens = quote! { + let add_headers = self.request.add_headers_to_request(); + quote! { let headers = http_request.headers_mut(); - }; - - header_tokens.append_all(self.request.add_headers_to_request()); - - header_tokens + #add_headers + } } else { TokenStream::new() }; diff --git a/src/api/request.rs b/src/api/request.rs index 01fea4d7..54c4453c 100644 --- a/src/api/request.rs +++ b/src/api/request.rs @@ -11,7 +11,7 @@ pub struct Request { impl Request { pub fn add_headers_to_request(&self) -> TokenStream { - self.header_fields().fold(TokenStream::new(), |mut header_tokens, request_field| { + let append_stmts = self.header_fields().map(|request_field| { let (field, header_name_string) = match request_field { RequestField::Header(field, header_name_string) => (field, header_name_string), _ => panic!("expected request field to be header variant"), @@ -20,20 +20,22 @@ impl Request { let field_name = &field.ident; let header_name = Ident::new(header_name_string.as_ref(), Span::call_site()); - header_tokens.append_all(quote! { + quote! { headers.append( ::http::header::#header_name, ::http::header::HeaderValue::from_str(request.#field_name.as_ref()) .expect("failed to convert value into HeaderValue"), ); - }); + } + }); - header_tokens - }) + quote! { + #(#append_stmts)* + } } pub fn parse_headers_from_request(&self) -> TokenStream { - self.header_fields().fold(TokenStream::new(), |mut header_tokens, request_field| { + let fields = self.header_fields().map(|request_field| { let (field, header_name_string) = match request_field { RequestField::Header(field, header_name_string) => (field, header_name_string), _ => panic!("expected request field to be header variant"), @@ -42,15 +44,17 @@ impl Request { let field_name = &field.ident; let header_name = Ident::new(header_name_string.as_ref(), Span::call_site()); - header_tokens.append_all(quote! { + quote! { #field_name: headers.get(::http::header::#header_name) .and_then(|v| v.to_str().ok()) .ok_or(::serde_json::Error::missing_field(#header_name_string))? - .to_owned(), - }); + .to_owned() + } + }); - header_tokens - }) + quote! { + #(#fields,)* + } } pub fn has_body_fields(&self) -> bool { @@ -120,19 +124,28 @@ impl Request { self.struct_init_fields(RequestFieldKind::Query, quote!(request_query)) } - fn struct_init_fields(&self, request_field_kind: RequestFieldKind, src: TokenStream) -> TokenStream { - let mut tokens = TokenStream::new(); + fn struct_init_fields( + &self, + request_field_kind: RequestFieldKind, + src: TokenStream, + ) -> TokenStream { + let fields = self.fields.iter().filter_map(|f| { + f.field_(request_field_kind).map(|field| { + let field_name = field + .ident + .as_ref() + .expect("expected field to have an identifier"); + let span = field.span(); - for field in self.fields.iter().flat_map(|f| f.field_(request_field_kind)) { - let field_name = field.ident.as_ref().expect("expected field to have an identifier"); - let span = field.span(); + quote_spanned! {span=> + #field_name: #src.#field_name + } + }) + }); - tokens.append_all(quote_spanned! {span=> - #field_name: #src.#field_name, - }); + quote! { + #(#fields,)* } - - tokens } } @@ -224,114 +237,98 @@ impl ToTokens for Request { let request_struct_body = if self.fields.is_empty() { quote!(;) } else { - let fields = self.fields.iter().fold(TokenStream::new(), |mut field_tokens, request_field| { + let fields = self.fields.iter().map(|request_field| { let field = request_field.field(); let span = field.span(); let stripped_field = strip_serde_attrs(field); - field_tokens.append_all(quote_spanned!(span=> #stripped_field,)); - - field_tokens + quote_spanned!(span=> #stripped_field) }); quote! { { - #fields + #(#fields),* } } }; - let request_body_struct; - - if let Some(newtype_body_field) = self.newtype_body_field() { + let request_body_struct = if let Some(newtype_body_field) = self.newtype_body_field() { let mut field = newtype_body_field.clone(); let ty = &field.ty; let span = field.span(); - request_body_struct = quote_spanned! {span=> + quote_spanned! {span=> /// Data in the request body. #[derive(Debug, Deserialize, Serialize)] struct RequestBody(#ty); - }; + } } else if self.has_body_fields() { - let fields = self.fields.iter().fold(TokenStream::new(), |mut field_tokens, request_field| { + let fields = self.fields.iter().filter_map(|request_field| { match *request_field { RequestField::Body(ref field) => { let span = field.span(); - - field_tokens.append_all(quote_spanned!(span=> #field,)); - - field_tokens + Some(quote_spanned!(span=> #field)) } - _ => field_tokens, + _ => None, } }); - request_body_struct = quote! { + quote! { /// Data in the request body. #[derive(Debug, Deserialize, Serialize)] struct RequestBody { - #fields + #(#fields),* } - }; + } } else { - request_body_struct = TokenStream::new(); - } + TokenStream::new() + }; - let request_path_struct; - - if self.has_path_fields() { - let fields = self.fields.iter().fold(TokenStream::new(), |mut field_tokens, request_field| { + let request_path_struct = if self.has_path_fields() { + let fields = self.fields.iter().filter_map(|request_field| { match *request_field { RequestField::Path(ref field) => { let span = field.span(); - field_tokens.append_all(quote_spanned!(span=> #field,)); - - field_tokens + Some(quote_spanned!(span=> #field)) } - _ => field_tokens, + _ => None, } }); - request_path_struct = quote! { + quote! { /// Data in the request path. #[derive(Debug, Deserialize, Serialize)] struct RequestPath { - #fields + #(#fields),* } - }; + } } else { - request_path_struct = TokenStream::new(); - } + TokenStream::new() + }; - let request_query_struct; - - if self.has_query_fields() { - let fields = self.fields.iter().fold(TokenStream::new(), |mut field_tokens, request_field| { + let request_query_struct = if self.has_query_fields() { + let fields = self.fields.iter().filter_map(|request_field| { match *request_field { RequestField::Query(ref field) => { let span = field.span(); - - field_tokens.append_all(quote_spanned!(span=> #field,)); - - field_tokens + Some(quote_spanned!(span=> #field)) } - _ => field_tokens, + _ => None, } }); - request_query_struct = quote! { + quote! { /// Data in the request's query string. #[derive(Debug, Deserialize, Serialize)] struct RequestQuery { - #fields + #(#fields),* } - }; + } } else { - request_query_struct = TokenStream::new(); - } + TokenStream::new() + }; tokens.append_all(quote! { #request_struct_header diff --git a/src/api/response.rs b/src/api/response.rs index a926913d..98f0e8ac 100644 --- a/src/api/response.rs +++ b/src/api/response.rs @@ -27,9 +27,7 @@ impl Response { } pub fn init_fields(&self) -> TokenStream { - let mut tokens = TokenStream::new(); - - for response_field in self.fields.iter() { + let fields = self.fields.iter().map(|response_field| { match *response_field { ResponseField::Body(ref field) => { let field_name = field @@ -38,9 +36,9 @@ impl Response { .expect("expected field to have an identifier"); let span = field.span(); - tokens.append_all(quote_spanned! {span=> - #field_name: response_body.#field_name, - }); + quote_spanned! {span=> + #field_name: response_body.#field_name + } } ResponseField::Header(ref field, ref header) => { let field_name = field @@ -50,13 +48,13 @@ impl Response { let header_name = Ident::new(header.as_ref(), Span::call_site()); let span = field.span(); - tokens.append_all(quote_spanned! {span=> + quote_spanned! {span=> #field_name: headers.remove(::http::header::#header_name) .expect("response missing expected header") .to_str() .expect("failed to convert HeaderValue to str") - .to_owned(), - }); + .to_owned() + } } ResponseField::NewtypeBody(ref field) => { let field_name = field @@ -65,32 +63,36 @@ impl Response { .expect("expected field to have an identifier"); let span = field.span(); - tokens.append_all(quote_spanned! {span=> - #field_name: response_body, - }); + quote_spanned! {span=> + #field_name: response_body + } } } - } + }); - tokens + quote! { + #(#fields,)* + } } pub fn apply_header_fields(&self) -> TokenStream { - let mut tokens = TokenStream::new(); - - for response_field in self.fields.iter() { + let header_calls = self.fields.iter().filter_map(|response_field| { if let ResponseField::Header(ref field, ref header) = *response_field { let field_name = field.ident.as_ref().expect("expected field to have an identifier"); let header_name = Ident::new(header.as_ref(), Span::call_site()); let span = field.span(); - tokens.append_all(quote_spanned! {span=> + Some(quote_spanned! {span=> .header(::http::header::#header_name, response.#field_name) - }); + }) + } else { + None } - } + }); - tokens + quote! { + #(#header_calls)* + } } pub fn to_body(&self) -> TokenStream { @@ -112,7 +114,11 @@ impl Response { } }); - quote!(ResponseBody{#(#fields),*}) + quote! { + ResponseBody { + #(#fields),* + } + } } } @@ -221,60 +227,53 @@ impl ToTokens for Response { let response_struct_body = if self.fields.is_empty() { quote!(;) } else { - let fields = self.fields.iter().fold(TokenStream::new(), |mut fields_tokens, response_field| { + let fields = self.fields.iter().map(|response_field| { let field = response_field.field(); let span = field.span(); let stripped_field = strip_serde_attrs(field); - fields_tokens.append_all(quote_spanned!(span=> #stripped_field,)); - - fields_tokens + quote_spanned!(span=> #stripped_field) }); quote! { { - #fields + #(#fields),* } } }; - let response_body_struct; - - if let Some(newtype_body_field) = self.newtype_body_field() { + let response_body_struct = if let Some(newtype_body_field) = self.newtype_body_field() { let mut field = newtype_body_field.clone(); let ty = &field.ty; let span = field.span(); - response_body_struct = quote_spanned! {span=> + quote_spanned! {span=> /// Data in the response body. #[derive(Debug, Deserialize, Serialize)] struct ResponseBody(#ty); - }; + } } else if self.has_body_fields() { - let fields = self.fields.iter().fold(TokenStream::new(), |mut field_tokens, response_field| { + let fields = self.fields.iter().filter_map(|response_field| { match *response_field { ResponseField::Body(ref field) => { let span = field.span(); - - field_tokens.append_all(quote_spanned!(span=> #field,)); - - field_tokens + Some(quote_spanned!(span=> #field)) } - _ => field_tokens, + _ => None, } }); - response_body_struct = quote! { + quote! { /// Data in the response body. #[derive(Debug, Deserialize, Serialize)] struct ResponseBody { - #fields + #(#fields),* } - }; + } } else { - response_body_struct = TokenStream::new(); - } + TokenStream::new() + }; tokens.append_all(quote! { #response_struct_header