From 3c1fc3a0bb70b228cede4cf0dd2c7a70a2d043cc Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Tue, 31 Dec 2024 11:01:15 +0100 Subject: [PATCH] Reduce work done by macros --- crates/musli-core/src/lib.rs | 18 +- crates/musli-macros/src/de.rs | 149 +++++++--------- crates/musli-macros/src/en.rs | 41 ++--- crates/musli-macros/src/expander.rs | 18 +- crates/musli-macros/src/internals/attr.rs | 37 +++- crates/musli-macros/src/internals/build.rs | 26 +-- .../musli-macros/src/internals/expansion.rs | 15 +- crates/musli-macros/src/internals/mode.rs | 126 ++++++-------- crates/musli-macros/src/internals/tokens.rs | 162 +++++++++--------- crates/musli-macros/src/packed.rs | 6 +- 10 files changed, 294 insertions(+), 304 deletions(-) diff --git a/crates/musli-core/src/lib.rs b/crates/musli-core/src/lib.rs index 477030fcb..92f390506 100644 --- a/crates/musli-core/src/lib.rs +++ b/crates/musli-core/src/lib.rs @@ -195,16 +195,23 @@ pub use musli_macros::visitor; /// Using these directly is not supported. #[doc(hidden)] pub mod __priv { - use crate::context::Context; - use crate::de::{Decoder, EntryDecoder}; + pub use crate::context::Context; + pub use crate::de::{ + AsDecoder, Decode, DecodeBytes, DecodePacked, DecodeTrace, Decoder, EntryDecoder, + MapDecoder, SequenceDecoder, VariantDecoder, + }; + pub use crate::en::{ + Encode, EncodeBytes, EncodePacked, EncodeTrace, Encoder, EntryEncoder, MapEncoder, + SequenceEncoder, VariantEncoder, + }; + pub use crate::hint::MapHint; + pub use crate::never::Never; pub use ::core::fmt; pub use ::core::mem::{offset_of, size_of}; pub use ::core::option::Option; pub use ::core::result::Result; - pub use crate::never::Never; - #[inline(always)] pub fn default() -> T where @@ -230,7 +237,4 @@ pub mod __priv { { skip(decoder.decode_value()?) } - - pub use Option::{None, Some}; - pub use Result::{Err, Ok}; } diff --git a/crates/musli-macros/src/de.rs b/crates/musli-macros/src/de.rs index 48ff81358..7de380387 100644 --- a/crates/musli-macros/src/de.rs +++ b/crates/musli-macros/src/de.rs @@ -8,7 +8,7 @@ use crate::expander::{NameMethod, StructKind}; use crate::internals::apply; use crate::internals::attr::{EnumTagging, Packing}; use crate::internals::build::{Body, Build, BuildData, Enum, Field, Variant}; -use crate::internals::tokens::Tokens; +use crate::internals::tokens::{Import, Tokens}; use crate::internals::Result; struct Ctxt<'a> { @@ -40,7 +40,7 @@ pub(crate) fn expand_decode_entry(e: Build<'_>) -> Result { let body = match &e.data { BuildData::Struct(st) => { - packed = crate::packed::packed(&e, st, &e.tokens.decode_t, "DECODE_PACKED"); + packed = crate::packed::packed(&e, st, e.tokens.decode_t, "DECODE_PACKED"); decode_struct(&cx, &e, st)? } BuildData::Enum(en) => { @@ -96,7 +96,7 @@ pub(crate) fn expand_decode_entry(e: Build<'_>) -> Result { attributes.push(syn::parse_quote!(#[allow(clippy::just_underscores_and_digits)])); } - let mode_ident = e.expansion.mode_path(e.tokens).as_path(); + let mode_ident = e.expansion.mode_path(&e.tokens); Ok(quote! { const _: () = { @@ -122,7 +122,7 @@ pub(crate) fn expand_decode_entry(e: Build<'_>) -> Result { } fn decode_struct(cx: &Ctxt<'_>, b: &Build<'_>, st: &Body<'_>) -> Result { - let Tokens { result_ok, .. } = b.tokens; + let Tokens { result, .. } = b.tokens; let body = match (st.kind, st.packing) { (_, Packing::Transparent) => decode_transparent(cx, b, st)?, @@ -131,7 +131,7 @@ fn decode_struct(cx: &Ctxt<'_>, b: &Build<'_>, st: &Body<'_>) -> Result decode_tagged(cx, b, st, None)?, }; - Ok(quote!(#result_ok({ #body }))) + Ok(quote!(#result::Ok({ #body }))) } fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { @@ -147,11 +147,8 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { context_t, decoder_t, fmt, - option_none, - option_some, option, - result_err, - result_ok, + result, skip_field, skip, map_decoder_t, @@ -170,7 +167,7 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { // Trying to decode an uninhabitable type. if en.variants.is_empty() { - return Ok(quote!(#result_err(#context_t::uninhabitable(#ctx_var, #type_name)))); + return Ok(quote!(#result::Err(#context_t::uninhabitable(#ctx_var, #type_name)))); } let binding_var = b.cx.ident("binding"); @@ -200,14 +197,14 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { Some(ident) => { quote! {{ if #skip(#variant_decoder_t::decode_value(#variant_decoder_var)?)? { - return #result_err(#context_t::invalid_variant_tag(#ctx_var, #type_name, &#variant_tag_var)); + return #result::Err(#context_t::invalid_variant_tag(#ctx_var, #type_name, &#variant_tag_var)); } Self::#ident {} }} } None => quote! { - return #result_err(#context_t::invalid_variant_tag(#ctx_var, #type_name, &#variant_tag_var)) + return #result::Err(#context_t::invalid_variant_tag(#ctx_var, #type_name, &#variant_tag_var)) }, }; @@ -241,16 +238,16 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { variants.push(variant); } - let arms = variants.iter().map(|o| o.as_arm(&binding_var, option_some)); + let arms = variants.iter().map(|o| o.as_arm(&binding_var, option)); let visit_type = &en.name_type.ty; let method = method.as_method_name(); decode_name = quote! { #decoder_t::#method(#variant_decoder_var, |#value_var: &#visit_type| { - #result_ok(match #value_var { + #result::Ok(match #value_var { #(#arms,)* - _ => #option_none, + _ => #option::None, }) }) }; @@ -287,7 +284,7 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { } }); - fallback = quote!(#option_none => { #fallback }); + fallback = quote!(#option::None => { #fallback }); name_type = syn::parse_quote!(#option<#output_type>); } } @@ -299,15 +296,15 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { for v in &en.variants { let path = &v.st.path; let pat = output_arm(v.pattern, &v.name, &binding_var); - arms.push(quote!(#pat => #result_ok(#path {}))); + arms.push(quote!(#pat => #result::Ok(#path {}))); } match en.fallback { Some(ident) => { - arms.push(quote!(_ => #result_ok(Self::#ident {}))); + arms.push(quote!(_ => #result::Ok(Self::#ident {}))); } None => { - arms.push(quote!(#value_var => #result_err(#context_t::invalid_variant_tag(#ctx_var, #type_name, &#value_var)))); + arms.push(quote!(#value_var => #result::Err(#context_t::invalid_variant_tag(#ctx_var, #type_name, &#value_var)))); } } @@ -394,7 +391,7 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { #fallback }; - #result_ok(#output_var) + #result::Ok(#output_var) })?; #leave @@ -461,7 +458,7 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { } #field_var => { if #skip_field(#entry_var)? { - return #result_err(#context_t::invalid_field_tag(#ctx_var, #type_name, &#field_var)); + return #result::Err(#context_t::invalid_field_tag(#ctx_var, #type_name, &#field_var)); } } } @@ -481,7 +478,7 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { let decode_outcome = quote! { #decoder_t::#method(#field_name_var, |#value_var: &#visit_type| { - #result_ok(match #value_var { + #result::Ok(match #value_var { #tag_arm => #outcome_type::Tag, #value_var => { #outcome_type::Skip(#context_t::collect_string(#ctx_var, #format_value_var)?) @@ -499,7 +496,7 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { } #outcome_type::Skip(#field_name) => { if #skip_field(#entry_var)? { - return #result_err(#context_t::invalid_field_string_tag(#ctx_var, #type_name, #field_name)); + return #result::Err(#context_t::invalid_field_string_tag(#ctx_var, #type_name, #field_name)); } } } @@ -534,8 +531,8 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { let #variant_tag_var: #name_type = #decoder_t::decode_map(#struct_var, |#struct_var| { let #variant_decoder_var = loop { - let #option_some(mut #entry_var) = #map_decoder_t::decode_entry(#struct_var)? else { - return #result_err(#context_t::missing_variant_field(#ctx_var, #type_name, #tag_static_value)); + let #option::Some(mut #entry_var) = #map_decoder_t::decode_entry(#struct_var)? else { + return #result::Err(#context_t::missing_variant_field(#ctx_var, #type_name, #tag_static_value)); }; let #field_name_var = #struct_field_decoder_t::decode_key(&mut #entry_var)?; @@ -552,7 +549,7 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { }; #leave - #result_ok(#output_var) + #result::Ok(#output_var) }}) } EnumTagging::Adjacent { @@ -612,23 +609,23 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { #tag_arm => { let #variant_decoder_var = #struct_field_decoder_t::decode_value(#entry_var)?; let #variant_tag_var: #name_type = #decode_name?; - #name_var = #option_some(#variant_tag_var); + #name_var = #option::Some(#variant_tag_var); } #content_arm => { - let #option_some(#variant_tag_var) = #name_var else { - return #result_err(#context_t::missing_adjacent_tag(#ctx_var, #type_name, &#content_value)); + let #option::Some(#variant_tag_var) = #name_var else { + return #result::Err(#context_t::missing_adjacent_tag(#ctx_var, #type_name, &#content_value)); }; let #body_decoder_var = #struct_field_decoder_t::decode_value(#entry_var)?; - break #result_ok(match #variant_tag_var { + break #result::Ok(match #variant_tag_var { #(#arms,)* #fallback }); } #field_var => { if #skip_field(#entry_var)? { - return #result_err(#context_t::invalid_field_tag(#ctx_var, #type_name, &#field_var)); + return #result::Err(#context_t::invalid_field_tag(#ctx_var, #type_name, &#field_var)); } } } @@ -647,7 +644,7 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { decode_match = quote! { let #outcome_var = #decoder_t::#method(#field_name_var, |#value_var: &#visit_type| { - #result_ok(match #value_var { + #result::Ok(match #value_var { #tag_arm => #outcome_type::Tag, #content_arm => #outcome_type::Content, #value_var => { @@ -659,23 +656,23 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { match #outcome_var { #outcome_type::Tag => { let #variant_decoder_var = #struct_field_decoder_t::decode_value(#entry_var)?; - #name_var = #option_some(#decode_name?); + #name_var = #option::Some(#decode_name?); } #outcome_type::Content => { - let #option_some(#variant_tag_var) = #name_var else { - return #result_err(#context_t::invalid_field_tag(#ctx_var, #type_name, &#tag_value)); + let #option::Some(#variant_tag_var) = #name_var else { + return #result::Err(#context_t::invalid_field_tag(#ctx_var, #type_name, &#tag_value)); }; let #body_decoder_var = #struct_field_decoder_t::decode_value(#entry_var)?; - break #result_ok(match #variant_tag_var { + break #result::Ok(match #variant_tag_var { #(#arms,)* #fallback }); } #outcome_type::Skip(#field_name) => { if #skip_field(#entry_var)? { - return #result_err(#context_t::invalid_field_string_tag(#ctx_var, #type_name, #field_name)); + return #result::Err(#context_t::invalid_field_string_tag(#ctx_var, #type_name, #field_name)); } } } @@ -710,11 +707,11 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { #enter #decoder_t::decode_map_hint(#decoder_var, &#struct_hint_static, move |#struct_decoder_var| { - let mut #name_var = #option_none; + let mut #name_var = #option::None; let #output_var = loop { - let #option_some(mut #entry_var) = #map_decoder_t::decode_entry(#struct_decoder_var)? else { - return #result_err(#context_t::expected_field_adjacent(#ctx_var, #type_name, &#tag_static, &#content_static)); + let #option::Some(mut #entry_var) = #map_decoder_t::decode_entry(#struct_decoder_var)? else { + return #result::Err(#context_t::expected_field_adjacent(#ctx_var, #type_name, &#tag_static, &#content_static)); }; let #field_name_var = #struct_field_decoder_t::decode_key(&mut #entry_var)?; @@ -723,7 +720,7 @@ fn decode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum) -> Result { }; #leave - #result_ok(#output_var) + #result::Ok(#output_var) })? }}) } @@ -762,7 +759,7 @@ fn decode_empty(cx: &Ctxt, b: &Build<'_>, st: &Body<'_>) -> Result let Tokens { context_t, decoder_t, - result_ok, + result, map_hint, .. } = b.tokens; @@ -787,7 +784,7 @@ fn decode_empty(cx: &Ctxt, b: &Build<'_>, st: &Body<'_>) -> Result Ok(quote! {{ #enter static #struct_hint_static: #map_hint = #map_hint::with_size(0); - let #output_var = #decoder_t::decode_map_hint(#decoder_var, &#struct_hint_static, |_| #result_ok(()))?; + let #output_var = #decoder_t::decode_map_hint(#decoder_var, &#struct_hint_static, |_| #result::Ok(()))?; #leave #path }}) @@ -815,11 +812,8 @@ fn decode_tagged( decoder_t, default_function, fmt, - option_none, - option_some, option, - result_err, - result_ok, + result, skip_field, map_decoder_t, struct_field_decoder_t, @@ -882,7 +876,7 @@ fn decode_tagged( }); let decode = quote! { - #var = #option_some(#decode_path(#struct_decoder_var)?); + #var = #option::Some(#decode_path(#struct_decoder_var)?); }; fields_with.push((tag, f, decode, (enter, leave))); @@ -892,7 +886,7 @@ fn decode_tagged( Some((_, Some(path))) => quote!(#path()), None => quote! {{ static #static_name_var: #static_name_type = #tag; - return #result_err(#context_t::expected_tag(#ctx_var, #type_name, #formatted_tag)) + return #result::Err(#context_t::expected_tag(#ctx_var, #type_name, #formatted_tag)) }}, }; @@ -900,8 +894,8 @@ fn decode_tagged( syn::Expr::Verbatim(quote! { match #var { - #option_some(#var) => #var, - #option_none => #fallback, + #option::Some(#var) => #var, + #option::None => #fallback, } }) } @@ -929,7 +923,7 @@ fn decode_tagged( let skip_field = quote! { if #skip_field(#struct_decoder_var)? { - return #result_err(#unsupported); + return #result::Err(#unsupported); } }; @@ -1001,17 +995,17 @@ fn decode_tagged( body = skip_field; } - let arms = outputs.iter().map(|o| o.as_arm(&binding_var, option_some)); + let arms = outputs.iter().map(|o| o.as_arm(&binding_var, option)); let visit_type = &st.name_type.ty; let method = method.as_method_name(); decode_tag = quote! { #decoder_t::#method(#struct_decoder_var, |#value_var: &#visit_type| { - #result_ok(match #value_var { + #result::Ok(match #value_var { #(#arms,)* #value_var => { - #option_none + #option::None } }) })? @@ -1049,7 +1043,7 @@ fn decode_tagged( .unskipped_fields .iter() .map(|f| &**f) - .map(|Field { var, ty, .. }| quote!(let mut #var: #option<#ty> = #option_none;)); + .map(|Field { var, ty, .. }| quote!(let mut #var: #option<#ty> = #option::None)); let enter = (cx.trace && cx.trace_body).then(|| { quote! { @@ -1065,14 +1059,14 @@ fn decode_tagged( Ok(quote! {{ #output_enum - #(#decls)* + #(#decls;)* #enter static #struct_hint_static: #map_hint = #map_hint::with_size(#fields_len); #decoder_t::decode_map_hint(#decoder_var, &#struct_hint_static, move |#type_decoder_var| { - while let #option_some(mut #struct_decoder_var) = #map_decoder_t::decode_entry(#type_decoder_var)? { + while let #option::Some(mut #struct_decoder_var) = #map_decoder_t::decode_entry(#type_decoder_var)? { let #name_var: #name_type = { let #struct_decoder_var = #struct_field_decoder_t::decode_key(&mut #struct_decoder_var)?; #decode_tag @@ -1082,7 +1076,7 @@ fn decode_tagged( } #leave - #result_ok(#path { #assigns }) + #result::Ok(#path { #assigns }) })? }}) } @@ -1213,13 +1207,8 @@ pub(crate) struct NameVariant<'a> { impl NameVariant<'_> { /// Generate the pattern for this output. - pub(crate) fn as_arm(&self, binding_var: &syn::Ident, option_some: &syn::Path) -> syn::Arm { - let body = syn::Expr::Path(syn::ExprPath { - attrs: Vec::new(), - qself: None, - path: self.path.clone(), - }); - + pub(crate) fn as_arm(&self, binding_var: &syn::Ident, option: Import<'_>) -> syn::Arm { + let path = &self.path; let arm = output_arm(self.pattern, self.name, binding_var); syn::Arm { @@ -1234,34 +1223,12 @@ impl NameVariant<'_> { ) }), fat_arrow_token: ]>::default(), - body: Box::new(build_call(option_some, [body])), + body: Box::new(syn::parse_quote!(#option::Some(#path))), comma: None, } } } -pub(crate) fn build_call(path: &syn::Path, it: A) -> syn::Expr -where - A: IntoIterator, -{ - let mut args = Punctuated::default(); - - for arg in it { - args.push(arg); - } - - syn::Expr::Call(syn::ExprCall { - attrs: Vec::new(), - func: Box::new(syn::Expr::Path(syn::ExprPath { - attrs: Vec::new(), - qself: None, - path: path.clone(), - })), - paren_token: syn::token::Paren::default(), - args, - }) -} - fn unsized_arm<'a>( b: &Build<'_>, span: Span, @@ -1282,8 +1249,8 @@ fn unsized_arm<'a>( pattern, }; - let option_some = &b.tokens.option_some; - (syn::parse_quote!(#option_some(#path)), output) + let option = &b.tokens.option; + (syn::parse_quote!(#option::Some(#path)), output) } struct Condition<'a> { diff --git a/crates/musli-macros/src/en.rs b/crates/musli-macros/src/en.rs index 0db0667cc..e4af23a1c 100644 --- a/crates/musli-macros/src/en.rs +++ b/crates/musli-macros/src/en.rs @@ -41,7 +41,7 @@ pub(crate) fn expand_insert_entry(e: Build<'_>) -> Result { let body = match &e.data { BuildData::Struct(st) => { - packed = crate::packed::packed(&e, st, &e.tokens.encode_t, "ENCODE_PACKED"); + packed = crate::packed::packed(&e, st, e.tokens.encode_t, "ENCODE_PACKED"); encode_map(&cx, &e, st)? } BuildData::Enum(en) => { @@ -73,7 +73,7 @@ pub(crate) fn expand_insert_entry(e: Build<'_>) -> Result { attributes.push(syn::parse_quote!(#[allow(clippy::just_underscores_and_digits)])); } - let mode_ident = e.expansion.mode_path(e.tokens).as_path(); + let mode_ident = e.expansion.mode_path(&e.tokens); Ok(quote! { const _: () = { @@ -116,7 +116,7 @@ fn encode_map(cx: &Ctxt<'_>, b: &Build<'_>, st: &Body<'_>) -> Result, b: &Build<'_>, st: &Body<'_>) -> Result, b: &Build<'_>, st: &Body<'_>) -> Result, b: &Build<'_>, st: &Body<'_>) -> Result { @@ -206,7 +206,7 @@ fn insert_fields<'st>( let Tokens { context_t, sequence_encoder_t, - result_ok, + result, map_encoder_t, map_entry_encoder_t, @@ -271,7 +271,7 @@ fn insert_fields<'st>( #encode_t_encode(#field_name_expr, #field_encoder_var)?; let #value_encoder_var = #map_entry_encoder_t::encode_value(#pair_encoder_var)?; #encode_path(#access, #value_encoder_var)?; - #result_ok(()) + #result::Ok(()) })?; #leave @@ -321,10 +321,7 @@ fn encode_enum(cx: &Ctxt<'_>, b: &Build<'_>, en: &Enum<'_>) -> Result, b: &Build<'_>, en: &Enum<'_>) -> Result { } } - pub(crate) fn ty(&self) -> syn::Type { + pub(crate) fn ty(&self) -> Cow<'_, syn::Type> { match self.method { NameMethod::Unsized(..) => { let ty = &self.ty; - syn::parse_quote!(&#ty) + Cow::Owned(syn::parse_quote!(&#ty)) } - NameMethod::Value => self.ty.clone(), + NameMethod::Value => Cow::Borrowed(&self.ty), } } pub(crate) fn name_format(&self, value: &syn::Ident) -> syn::Expr { match self.format_with { - Some((_, path)) => de::build_call(path, [syn::parse_quote!(&#value)]), + Some((_, path)) => syn::parse_quote!(#path(&#value)), None => syn::parse_quote!(&#value), } } @@ -143,7 +143,7 @@ pub(crate) struct Expander<'a> { pub(crate) cx: Ctxt, pub(crate) type_attr: TypeAttr, pub(crate) data: Data<'a>, - pub(crate) tokens: Tokens, + pub(crate) prefix: syn::Path, pub(crate) default: Vec, } @@ -223,11 +223,15 @@ impl<'a> Expander<'a> { cx, type_attr, data, - tokens: Tokens::new(input.ident.span(), prefix), + prefix, default, } } + pub(crate) fn tokens(&self) -> Tokens<'_> { + Tokens::new(&self.prefix) + } + /// Coerce into errors. pub(crate) fn into_errors(self) -> Vec { self.cx.into_errors() diff --git a/crates/musli-macros/src/internals/attr.rs b/crates/musli-macros/src/internals/attr.rs index c8e9590d1..14a8e7ead 100644 --- a/crates/musli-macros/src/internals/attr.rs +++ b/crates/musli-macros/src/internals/attr.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::mem; use proc_macro2::Span; +use quote::ToTokens; use syn::meta::ParseNestedMeta; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; @@ -13,6 +14,7 @@ use crate::internals::ATTR; use crate::internals::{Ctxt, Mode}; use super::build; +use super::mode::Method; #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) enum ModeKind { @@ -670,28 +672,51 @@ layer! { impl Field { /// Expand encode of the given field. - pub(crate) fn encode_path_expanded(&self, mode: Mode<'_>, span: Span) -> (Span, syn::Path) { + pub(crate) fn encode_path_expanded<'a>( + &self, + mode: Mode<'a>, + span: Span, + ) -> (Span, MethodOrPath<'a>) { let encode_path = self.encode_path(mode); if let Some((span, encode_path)) = encode_path { - (*span, encode_path.clone()) + (*span, MethodOrPath::Path(encode_path.clone())) } else { let field_encoding = self.encoding(mode).map(|&(_, e)| e).unwrap_or_default(); let encode_path = mode.encode_t_encode(field_encoding); - (span, encode_path) + (span, MethodOrPath::Method(encode_path)) } } /// Expand decode of the given field. - pub(crate) fn decode_path_expanded(&self, mode: Mode<'_>, span: Span) -> (Span, syn::Path) { + pub(crate) fn decode_path_expanded<'a>( + &self, + mode: Mode<'a>, + span: Span, + ) -> (Span, MethodOrPath<'a>) { let decode_path = self.decode_path(mode); if let Some((span, decode_path)) = decode_path { - (*span, decode_path.clone()) + (*span, MethodOrPath::Path(decode_path.clone())) } else { let field_encoding = self.encoding(mode).map(|&(_, e)| e).unwrap_or_default(); let decode_path = mode.decode_t_decode(field_encoding); - (span, decode_path) + (span, MethodOrPath::Method(decode_path)) + } + } +} + +pub(crate) enum MethodOrPath<'a> { + Method(Method<'a>), + Path(syn::Path), +} + +impl ToTokens for MethodOrPath<'_> { + #[inline] + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + match self { + MethodOrPath::Method(method) => method.to_tokens(tokens), + MethodOrPath::Path(path) => path.to_tokens(tokens), } } } diff --git a/crates/musli-macros/src/internals/build.rs b/crates/musli-macros/src/internals/build.rs index 025d242e5..1f91d95b5 100644 --- a/crates/musli-macros/src/internals/build.rs +++ b/crates/musli-macros/src/internals/build.rs @@ -10,7 +10,8 @@ use crate::expander::{ UnsizedMethod, VariantData, }; -use super::attr::{EnumTagging, FieldEncoding, ModeKind, Packing}; +use super::attr::{EnumTagging, FieldEncoding, MethodOrPath, ModeKind, Packing}; +use super::mode::Method; use super::name::NameAll; use super::tokens::Tokens; use super::ATTR; @@ -19,13 +20,13 @@ use super::{Ctxt, Expansion, Mode, Only, Result}; pub(crate) struct Build<'a> { pub(crate) input: &'a syn::DeriveInput, pub(crate) cx: &'a Ctxt, - pub(crate) tokens: &'a Tokens, + pub(crate) tokens: Tokens<'a>, pub(crate) bounds: &'a [(Span, syn::WherePredicate)], pub(crate) decode_bounds: &'a [(Span, syn::WherePredicate)], pub(crate) expansion: Expansion<'a>, pub(crate) data: BuildData<'a>, - pub(crate) decode_t_decode: syn::Path, - pub(crate) encode_t_encode: syn::Path, + pub(crate) decode_t_decode: Method<'a>, + pub(crate) encode_t_encode: Method<'a>, pub(crate) enum_tagging_span: Option, } @@ -136,8 +137,8 @@ pub(crate) struct Variant<'a> { pub(crate) struct Field<'a> { pub(crate) span: Span, pub(crate) index: usize, - pub(crate) encode_path: (Span, syn::Path), - pub(crate) decode_path: (Span, syn::Path), + pub(crate) encode_path: (Span, MethodOrPath<'a>), + pub(crate) decode_path: (Span, MethodOrPath<'a>), pub(crate) name: syn::Expr, pub(crate) pattern: Option<&'a syn::Pat>, /// Skip field entirely and always initialize with the specified expresion, @@ -161,7 +162,8 @@ pub(crate) fn setup<'a>( expansion: Expansion<'a>, only: Only, ) -> Result> { - let mode = expansion.as_mode(&e.tokens, only); + let tokens = e.tokens(); + let mode = expansion.as_mode(&tokens, only); let data = match &e.data { Data::Struct(data) => BuildData::Struct(setup_struct(e, mode, data)), @@ -179,7 +181,7 @@ pub(crate) fn setup<'a>( Ok(Build { input: e.input, cx: &e.cx, - tokens: &e.tokens, + tokens, bounds: e.type_attr.bounds(mode), decode_bounds: e.type_attr.decode_bounds(mode), expansion, @@ -190,7 +192,7 @@ pub(crate) fn setup<'a>( }) } -fn setup_struct<'a>(e: &'a Expander, mode: Mode<'_>, data: &'a StructData<'a>) -> Body<'a> { +fn setup_struct<'a>(e: &'a Expander, mode: Mode<'a>, data: &'a StructData<'a>) -> Body<'a> { let mut unskipped_fields = Vec::with_capacity(data.fields.len()); let mut all_fields = Vec::with_capacity(data.fields.len()); @@ -244,7 +246,7 @@ fn setup_struct<'a>(e: &'a Expander, mode: Mode<'_>, data: &'a StructData<'a>) - body } -fn setup_enum<'a>(e: &'a Expander, mode: Mode<'_>, data: &'a EnumData<'a>) -> Enum<'a> { +fn setup_enum<'a>(e: &'a Expander, mode: Mode<'a>, data: &'a EnumData<'a>) -> Enum<'a> { let mut variants = Vec::with_capacity(data.variants.len()); let mut fallback = None; @@ -313,7 +315,7 @@ fn setup_enum<'a>(e: &'a Expander, mode: Mode<'_>, data: &'a EnumData<'a>) -> En fn setup_variant<'a>( e: &'a Expander<'_>, - mode: Mode<'_>, + mode: Mode<'a>, data: &'a VariantData<'a>, fallback: &mut Option<&'a syn::Ident>, ) -> Variant<'a> { @@ -418,7 +420,7 @@ fn setup_variant<'a>( fn setup_field<'a>( e: &'a Expander, - mode: Mode<'_>, + mode: Mode<'a>, data: &'a FieldData<'a>, name_all: NameAll, packing: Packing, diff --git a/crates/musli-macros/src/internals/expansion.rs b/crates/musli-macros/src/internals/expansion.rs index 5466db883..1d372472e 100644 --- a/crates/musli-macros/src/internals/expansion.rs +++ b/crates/musli-macros/src/internals/expansion.rs @@ -10,19 +10,26 @@ pub(crate) struct Expansion<'a> { } impl<'a> Expansion<'a> { - pub(crate) fn as_mode(&'a self, tokens: &'a Tokens, only: Only) -> Mode<'a> { + pub(crate) fn as_mode(&self, tokens: &Tokens<'a>, only: Only) -> Mode<'a> { Mode { kind: Some(&self.mode_ident.kind), mode_path: self.mode_path(tokens), - tokens, + encode_packed_t: tokens.encode_packed_t, + encode_bytes_t: tokens.encode_bytes_t, + trace_encode_t: tokens.trace_encode_t, + encode_t: tokens.encode_t, + decode_packed_t: tokens.decode_packed_t, + decode_bytes_t: tokens.decode_bytes_t, + trace_decode_t: tokens.trace_decode_t, + decode_t: tokens.decode_t, only, } } - pub(crate) fn mode_path(&self, tokens: &'a Tokens) -> ModePath<'a> { + pub(crate) fn mode_path(&self, tokens: &Tokens<'a>) -> ModePath<'a> { match &self.mode_ident.kind { ModeKind::Custom(..) => ModePath::Ident(&self.mode_ident.ident), - _ => ModePath::Musli(&tokens.prefix, &self.mode_ident.ident), + _ => ModePath::Musli(tokens.prefix, &self.mode_ident.ident), } } } diff --git a/crates/musli-macros/src/internals/mode.rs b/crates/musli-macros/src/internals/mode.rs index 02744a6b7..f1394be21 100644 --- a/crates/musli-macros/src/internals/mode.rs +++ b/crates/musli-macros/src/internals/mode.rs @@ -1,12 +1,11 @@ //! Helper for determining the mode we're currently in. -use proc_macro2::Span; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; +use proc_macro2::{Ident, Span, TokenStream, TokenTree}; +use quote::ToTokens; use syn::Token; use super::attr::{FieldEncoding, ModeKind}; -use super::tokens::Tokens; +use super::tokens::Import; use super::Only; #[derive(Clone, Copy)] @@ -15,93 +14,78 @@ pub(crate) enum ModePath<'a> { Musli(&'a syn::Path, &'a syn::Ident), } -impl ModePath<'_> { - pub(crate) fn as_path(self) -> syn::Path { - match self { - ModePath::Ident(ident) => syn::Path::from(ident.clone()), +impl ToTokens for ModePath<'_> { + #[inline] + fn to_tokens(&self, tokens: &mut TokenStream) { + match *self { + ModePath::Ident(ident) => { + ident.to_tokens(tokens); + } ModePath::Musli(base, ident) => { - let mut path = base.clone(); - path.segments.push(syn::PathSegment::from(syn::Ident::new( - "mode", - Span::call_site(), - ))); - path.segments.push(syn::PathSegment::from(ident.clone())); - path + base.to_tokens(tokens); + ::default().to_tokens(tokens); + tokens.extend([TokenTree::Ident(Ident::new("mode", Span::call_site()))]); + ::default().to_tokens(tokens); + ident.to_tokens(tokens); } } } } +pub(crate) struct Method<'a>(Import<'a>, ModePath<'a>, &'static str); + +impl ToTokens for Method<'_> { + #[inline] + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Method(import, mode_path, name) = *self; + + import.to_tokens(tokens); + ::default().to_tokens(tokens); + ::default().to_tokens(tokens); + mode_path.to_tokens(tokens); + ]>::default().to_tokens(tokens); + ::default().to_tokens(tokens); + tokens.extend([TokenTree::Ident(Ident::new(name, Span::call_site()))]); + } +} + #[derive(Clone, Copy)] pub(crate) struct Mode<'a> { pub(crate) kind: Option<&'a ModeKind>, pub(crate) mode_path: ModePath<'a>, - pub(crate) tokens: &'a Tokens, + pub(crate) encode_packed_t: Import<'a>, + pub(crate) encode_bytes_t: Import<'a>, + pub(crate) trace_encode_t: Import<'a>, + pub(crate) encode_t: Import<'a>, + pub(crate) decode_packed_t: Import<'a>, + pub(crate) decode_bytes_t: Import<'a>, + pub(crate) trace_decode_t: Import<'a>, + pub(crate) decode_t: Import<'a>, pub(crate) only: Only, } -impl Mode<'_> { +impl<'a> Mode<'a> { /// Construct a typed encode call. - pub(crate) fn encode_t_encode(&self, encoding: FieldEncoding) -> syn::Path { - let (mut encode_t, name) = match encoding { - FieldEncoding::Packed => (self.tokens.encode_packed_t.clone(), "encode_packed"), - FieldEncoding::Bytes => (self.tokens.encode_bytes_t.clone(), "encode_bytes"), - FieldEncoding::Trace => (self.tokens.trace_encode_t.clone(), "trace_encode"), - FieldEncoding::Default => (self.tokens.encode_t.clone(), "encode"), + pub(crate) fn encode_t_encode(&self, encoding: FieldEncoding) -> Method<'a> { + let (encode_t, name) = match encoding { + FieldEncoding::Packed => (self.encode_packed_t, "encode_packed"), + FieldEncoding::Bytes => (self.encode_bytes_t, "encode_bytes"), + FieldEncoding::Trace => (self.trace_encode_t, "trace_encode"), + FieldEncoding::Default => (self.encode_t, "encode"), }; - if let Some(segment) = encode_t.segments.last_mut() { - add_mode_argument(&self.mode_path, segment); - } - - encode_t - .segments - .push(syn::PathSegment::from(syn::Ident::new( - name, - encode_t.span(), - ))); - - encode_t + Method(encode_t, self.mode_path, name) } /// Construct a typed decode call. - pub(crate) fn decode_t_decode(&self, encoding: FieldEncoding) -> syn::Path { - let (mut decode_t, name) = match encoding { - FieldEncoding::Packed => (self.tokens.decode_packed_t.clone(), "decode_packed"), - FieldEncoding::Bytes => (self.tokens.decode_bytes_t.clone(), "decode_bytes"), - FieldEncoding::Trace => (self.tokens.trace_decode_t.clone(), "trace_decode"), - FieldEncoding::Default => (self.tokens.decode_t.clone(), "decode"), + pub(crate) fn decode_t_decode(&self, encoding: FieldEncoding) -> Method<'a> { + let (decode_t, name) = match encoding { + FieldEncoding::Packed => (self.decode_packed_t, "decode_packed"), + FieldEncoding::Bytes => (self.decode_bytes_t, "decode_bytes"), + FieldEncoding::Trace => (self.trace_decode_t, "trace_decode"), + FieldEncoding::Default => (self.decode_t, "decode"), }; - if let Some(segment) = decode_t.segments.last_mut() { - add_mode_argument(&self.mode_path, segment); - } - - decode_t - .segments - .push(syn::PathSegment::from(syn::Ident::new( - name, - decode_t.span(), - ))); - - decode_t + Method(decode_t, self.mode_path, name) } } - -fn add_mode_argument(moded_ident: &ModePath<'_>, last: &mut syn::PathSegment) { - let mut arguments = syn::AngleBracketedGenericArguments { - colon2_token: Some(::default()), - lt_token: ::default(), - args: Punctuated::default(), - gt_token: ]>::default(), - }; - - arguments - .args - .push(syn::GenericArgument::Type(syn::Type::Path(syn::TypePath { - qself: None, - path: moded_ident.as_path(), - }))); - - last.arguments = syn::PathArguments::AngleBracketed(arguments); -} diff --git a/crates/musli-macros/src/internals/tokens.rs b/crates/musli-macros/src/internals/tokens.rs index 974b02ead..e2fed124f 100644 --- a/crates/musli-macros/src/internals/tokens.rs +++ b/crates/musli-macros/src/internals/tokens.rs @@ -1,89 +1,89 @@ -use proc_macro2::Span; +use proc_macro2::{Ident, Span, TokenTree}; +use quote::ToTokens; +use syn::Token; -pub(crate) struct Tokens { - pub(crate) as_decoder_t: syn::Path, - pub(crate) context_t: syn::Path, - pub(crate) decode_bytes_t: syn::Path, - pub(crate) decode_packed_t: syn::Path, - pub(crate) decode_t: syn::Path, - pub(crate) decoder_t: syn::Path, - pub(crate) default_function: syn::Path, - pub(crate) encode_bytes_t: syn::Path, - pub(crate) encode_packed_t: syn::Path, - pub(crate) encode_t: syn::Path, - pub(crate) encoder_t: syn::Path, - pub(crate) fmt: syn::Path, - pub(crate) map_decoder_t: syn::Path, - pub(crate) map_encoder_t: syn::Path, - pub(crate) map_entry_encoder_t: syn::Path, - pub(crate) map_hint: syn::Path, - pub(crate) offset_of: syn::Path, - pub(crate) option_none: syn::Path, - pub(crate) option_some: syn::Path, - pub(crate) option: syn::Path, - pub(crate) pack_decoder_t: syn::Path, - pub(crate) result_err: syn::Path, - pub(crate) result_ok: syn::Path, - pub(crate) result: syn::Path, - pub(crate) sequence_encoder_t: syn::Path, - pub(crate) size_of: syn::Path, - pub(crate) skip_field: syn::Path, - pub(crate) skip: syn::Path, - pub(crate) struct_field_decoder_t: syn::Path, - pub(crate) trace_decode_t: syn::Path, - pub(crate) trace_encode_t: syn::Path, - pub(crate) variant_decoder_t: syn::Path, - pub(crate) variant_encoder_t: syn::Path, - pub(crate) prefix: syn::Path, -} +#[derive(Clone, Copy)] +pub(crate) struct Import<'a>(&'a syn::Path, &'static str); -impl Tokens { - pub(crate) fn new(span: Span, prefix: syn::Path) -> Self { - Self { - as_decoder_t: path(span, &prefix, ["de", "AsDecoder"]), - context_t: path(span, &prefix, ["Context"]), - decode_bytes_t: path(span, &prefix, ["de", "DecodeBytes"]), - decode_packed_t: path(span, &prefix, ["de", "DecodePacked"]), - decode_t: path(span, &prefix, ["de", "Decode"]), - decoder_t: path(span, &prefix, ["de", "Decoder"]), - default_function: path(span, &prefix, ["__priv", "default"]), - encode_bytes_t: path(span, &prefix, ["en", "EncodeBytes"]), - encode_packed_t: path(span, &prefix, ["en", "EncodePacked"]), - encode_t: path(span, &prefix, ["en", "Encode"]), - encoder_t: path(span, &prefix, ["en", "Encoder"]), - fmt: path(span, &prefix, ["__priv", "fmt"]), - map_decoder_t: path(span, &prefix, ["de", "MapDecoder"]), - map_encoder_t: path(span, &prefix, ["en", "MapEncoder"]), - map_entry_encoder_t: path(span, &prefix, ["en", "EntryEncoder"]), - map_hint: path(span, &prefix, ["hint", "MapHint"]), - offset_of: path(span, &prefix, ["__priv", "offset_of"]), - option_none: path(span, &prefix, ["__priv", "None"]), - option_some: path(span, &prefix, ["__priv", "Some"]), - option: path(span, &prefix, ["__priv", "Option"]), - pack_decoder_t: path(span, &prefix, ["de", "SequenceDecoder"]), - result_err: path(span, &prefix, ["__priv", "Err"]), - result_ok: path(span, &prefix, ["__priv", "Ok"]), - result: path(span, &prefix, ["__priv", "Result"]), - sequence_encoder_t: path(span, &prefix, ["en", "SequenceEncoder"]), - size_of: path(span, &prefix, ["__priv", "size_of"]), - skip_field: path(span, &prefix, ["__priv", "skip_field"]), - skip: path(span, &prefix, ["__priv", "skip"]), - struct_field_decoder_t: path(span, &prefix, ["de", "EntryDecoder"]), - trace_decode_t: path(span, &prefix, ["de", "DecodeTrace"]), - trace_encode_t: path(span, &prefix, ["en", "EncodeTrace"]), - variant_decoder_t: path(span, &prefix, ["de", "VariantDecoder"]), - variant_encoder_t: path(span, &prefix, ["en", "VariantEncoder"]), - prefix, - } +impl ToTokens for Import<'_> { + #[inline] + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Import(path, name) = *self; + + path.to_tokens(tokens); + ::default().to_tokens(tokens); + tokens.extend([TokenTree::Ident(Ident::new("__priv", Span::call_site()))]); + ::default().to_tokens(tokens); + tokens.extend([TokenTree::Ident(Ident::new(name, Span::call_site()))]); } } -fn path(span: Span, prefix: &syn::Path, segments: [&'static str; N]) -> syn::Path { - let mut path = prefix.clone(); +pub(crate) struct Tokens<'a> { + pub(crate) as_decoder_t: Import<'a>, + pub(crate) context_t: Import<'a>, + pub(crate) decode_bytes_t: Import<'a>, + pub(crate) decode_packed_t: Import<'a>, + pub(crate) decode_t: Import<'a>, + pub(crate) decoder_t: Import<'a>, + pub(crate) default_function: Import<'a>, + pub(crate) encode_bytes_t: Import<'a>, + pub(crate) encode_packed_t: Import<'a>, + pub(crate) encode_t: Import<'a>, + pub(crate) encoder_t: Import<'a>, + pub(crate) fmt: Import<'a>, + pub(crate) map_decoder_t: Import<'a>, + pub(crate) map_encoder_t: Import<'a>, + pub(crate) map_entry_encoder_t: Import<'a>, + pub(crate) map_hint: Import<'a>, + pub(crate) offset_of: Import<'a>, + pub(crate) option: Import<'a>, + pub(crate) pack_decoder_t: Import<'a>, + pub(crate) result: Import<'a>, + pub(crate) sequence_encoder_t: Import<'a>, + pub(crate) size_of: Import<'a>, + pub(crate) skip_field: Import<'a>, + pub(crate) skip: Import<'a>, + pub(crate) struct_field_decoder_t: Import<'a>, + pub(crate) trace_decode_t: Import<'a>, + pub(crate) trace_encode_t: Import<'a>, + pub(crate) variant_decoder_t: Import<'a>, + pub(crate) variant_encoder_t: Import<'a>, + pub(crate) prefix: &'a syn::Path, +} - for segment in segments { - path.segments.push(syn::Ident::new(segment, span).into()); +impl<'a> Tokens<'a> { + pub(crate) fn new(prefix: &'a syn::Path) -> Self { + Self { + as_decoder_t: Import(prefix, "AsDecoder"), + context_t: Import(prefix, "Context"), + decode_bytes_t: Import(prefix, "DecodeBytes"), + decode_packed_t: Import(prefix, "DecodePacked"), + decode_t: Import(prefix, "Decode"), + decoder_t: Import(prefix, "Decoder"), + default_function: Import(prefix, "default"), + encode_bytes_t: Import(prefix, "EncodeBytes"), + encode_packed_t: Import(prefix, "EncodePacked"), + encode_t: Import(prefix, "Encode"), + encoder_t: Import(prefix, "Encoder"), + fmt: Import(prefix, "fmt"), + map_decoder_t: Import(prefix, "MapDecoder"), + map_encoder_t: Import(prefix, "MapEncoder"), + map_entry_encoder_t: Import(prefix, "EntryEncoder"), + map_hint: Import(prefix, "MapHint"), + offset_of: Import(prefix, "offset_of"), + option: Import(prefix, "Option"), + pack_decoder_t: Import(prefix, "SequenceDecoder"), + result: Import(prefix, "Result"), + sequence_encoder_t: Import(prefix, "SequenceEncoder"), + size_of: Import(prefix, "size_of"), + skip_field: Import(prefix, "skip_field"), + skip: Import(prefix, "skip"), + struct_field_decoder_t: Import(prefix, "EntryDecoder"), + trace_decode_t: Import(prefix, "DecodeTrace"), + trace_encode_t: Import(prefix, "EncodeTrace"), + variant_decoder_t: Import(prefix, "VariantDecoder"), + variant_encoder_t: Import(prefix, "VariantEncoder"), + prefix, + } } - - path } diff --git a/crates/musli-macros/src/packed.rs b/crates/musli-macros/src/packed.rs index ce27aeb89..ba24f9696 100644 --- a/crates/musli-macros/src/packed.rs +++ b/crates/musli-macros/src/packed.rs @@ -3,12 +3,12 @@ use quote::quote; use crate::internals::attr::{Packed, Packing}; use crate::internals::build::{Body, Build}; -use crate::internals::tokens::Tokens; +use crate::internals::tokens::{Import, Tokens}; pub(super) fn packed( e: &Build<'_>, st: &Body<'_>, - trait_t: &syn::Path, + trait_t: Import<'_>, packed_field: &str, ) -> syn::Expr { let Tokens { @@ -18,7 +18,7 @@ pub(super) fn packed( match st.packing { Packing::Packed(Packed::Bitwise) if st.all_fields.len() == st.unskipped_fields.len() => { let packed_field = syn::Ident::new(packed_field, Span::call_site()); - let mode_ident = e.expansion.mode_path(e.tokens).as_path(); + let mode_ident = e.expansion.mode_path(&e.tokens); let mut offsets = Vec::with_capacity(st.all_fields.len().saturating_sub(1)); let mut sizes = Vec::with_capacity(st.all_fields.len());