diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/derive_more-impl/src/add_like.rs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/derive_more-impl/src/add_like.rs')
-rw-r--r-- | third_party/rust/derive_more-impl/src/add_like.rs | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/third_party/rust/derive_more-impl/src/add_like.rs b/third_party/rust/derive_more-impl/src/add_like.rs new file mode 100644 index 0000000000..58076461ca --- /dev/null +++ b/third_party/rust/derive_more-impl/src/add_like.rs @@ -0,0 +1,153 @@ +use crate::add_helpers::{struct_exprs, tuple_exprs}; +use crate::utils::{ + add_extra_type_param_bound_op_output, field_idents, named_to_vec, numbered_vars, + unnamed_to_vec, +}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use std::iter; +use syn::{Data, DataEnum, DeriveInput, Field, Fields, Ident}; + +pub fn expand(input: &DeriveInput, trait_name: &str) -> TokenStream { + let trait_name = trait_name.trim_end_matches("Self"); + let trait_ident = format_ident!("{trait_name}"); + let method_name = trait_name.to_lowercase(); + let method_ident = format_ident!("{method_name}"); + let input_type = &input.ident; + + let generics = add_extra_type_param_bound_op_output(&input.generics, &trait_ident); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let (output_type, block) = match input.data { + Data::Struct(ref data_struct) => match data_struct.fields { + Fields::Unnamed(ref fields) => ( + quote! { #input_type #ty_generics }, + tuple_content(input_type, &unnamed_to_vec(fields), &method_ident), + ), + Fields::Named(ref fields) => ( + quote! { #input_type #ty_generics }, + struct_content(input_type, &named_to_vec(fields), &method_ident), + ), + _ => panic!("Unit structs cannot use derive({trait_name})"), + }, + Data::Enum(ref data_enum) => ( + quote! { + ::core::result::Result<#input_type #ty_generics, ::derive_more::ops::BinaryError> + }, + enum_content(input_type, data_enum, &method_ident), + ), + + _ => panic!("Only structs and enums can use derive({trait_name})"), + }; + + quote! { + #[automatically_derived] + impl #impl_generics ::derive_more::#trait_ident for #input_type #ty_generics #where_clause { + type Output = #output_type; + + #[inline] + fn #method_ident(self, rhs: #input_type #ty_generics) -> #output_type { + #block + } + } + } +} + +fn tuple_content<T: ToTokens>( + input_type: &T, + fields: &[&Field], + method_ident: &Ident, +) -> TokenStream { + let exprs = tuple_exprs(fields, method_ident); + quote! { #input_type(#(#exprs),*) } +} + +fn struct_content( + input_type: &Ident, + fields: &[&Field], + method_ident: &Ident, +) -> TokenStream { + // It's safe to unwrap because struct fields always have an identifier + let exprs = struct_exprs(fields, method_ident); + let field_names = field_idents(fields); + + quote! { #input_type{#(#field_names: #exprs),*} } +} + +#[allow(clippy::cognitive_complexity)] +fn enum_content( + input_type: &Ident, + data_enum: &DataEnum, + method_ident: &Ident, +) -> TokenStream { + let mut matches = vec![]; + let mut method_iter = iter::repeat(method_ident); + + for variant in &data_enum.variants { + let subtype = &variant.ident; + let subtype = quote! { #input_type::#subtype }; + + match variant.fields { + Fields::Unnamed(ref fields) => { + // The pattern that is outputted should look like this: + // (Subtype(left_vars), TypePath(right_vars)) => Ok(TypePath(exprs)) + let size = unnamed_to_vec(fields).len(); + let l_vars = &numbered_vars(size, "l_"); + let r_vars = &numbered_vars(size, "r_"); + let method_iter = method_iter.by_ref(); + let matcher = quote! { + (#subtype(#(#l_vars),*), + #subtype(#(#r_vars),*)) => { + ::core::result::Result::Ok(#subtype(#(#l_vars.#method_iter(#r_vars)),*)) + } + }; + matches.push(matcher); + } + Fields::Named(ref fields) => { + // The pattern that is outputted should look like this: + // (Subtype{a: __l_a, ...}, Subtype{a: __r_a, ...} => { + // Ok(Subtype{a: __l_a.add(__r_a), ...}) + // } + let field_vec = named_to_vec(fields); + let size = field_vec.len(); + let field_names = &field_idents(&field_vec); + let l_vars = &numbered_vars(size, "l_"); + let r_vars = &numbered_vars(size, "r_"); + let method_iter = method_iter.by_ref(); + let matcher = quote! { + (#subtype{#(#field_names: #l_vars),*}, + #subtype{#(#field_names: #r_vars),*}) => { + ::core::result::Result::Ok(#subtype{#(#field_names: #l_vars.#method_iter(#r_vars)),*}) + } + }; + matches.push(matcher); + } + Fields::Unit => { + let operation_name = method_ident.to_string(); + matches.push(quote! { + (#subtype, #subtype) => ::core::result::Result::Err( + ::derive_more::ops::BinaryError::Unit( + ::derive_more::ops::UnitError::new(#operation_name) + ) + ) + }); + } + } + } + + if data_enum.variants.len() > 1 { + // In the strange case where there's only one enum variant this is would be an unreachable + // match. + let operation_name = method_ident.to_string(); + matches.push(quote! { + _ => ::core::result::Result::Err(::derive_more::ops::BinaryError::Mismatch( + ::derive_more::ops::WrongVariantError::new(#operation_name) + )) + }); + } + quote! { + match (self, rhs) { + #(#matches),* + } + } +} |