derive_more_impl/ops/
add_assign.rs1#[cfg(doc)]
4use std::ops;
5
6use proc_macro2::TokenStream;
7use quote::{format_ident, ToTokens as _};
8use syn::spanned::Spanned as _;
9
10use super::{AssignStructuralExpansion, SkippedFields};
11use crate::utils::attr::{self, ParseMultiple as _};
12
13pub fn expand(input: &syn::DeriveInput, trait_name: &str) -> syn::Result<TokenStream> {
22 let trait_name = normalize_trait_name(trait_name);
23 let attr_name = format_ident!("{}", trait_name_to_attribute_name(trait_name));
24
25 let mut variants = vec![];
26 match &input.data {
27 syn::Data::Struct(data) => {
28 if let Some(skip) = attr::Skip::parse_attrs(&input.attrs, &attr_name)? {
29 return Err(syn::Error::new(
30 skip.span,
31 format!(
32 "`#[{attr_name}({})]` attribute can be placed only on struct fields",
33 skip.item.name(),
34 ),
35 ));
36 } else if matches!(data.fields, syn::Fields::Unit) {
37 return Err(syn::Error::new(
38 data.struct_token.span(),
39 format!("`{trait_name}` cannot be derived for unit structs"),
40 ));
41 }
42 let mut skipped_fields = SkippedFields::default();
43 for (n, field) in data.fields.iter().enumerate() {
44 if attr::Skip::parse_attrs(&field.attrs, &attr_name)?.is_some() {
45 _ = skipped_fields.insert(n);
46 }
47 }
48 if data.fields.len() == skipped_fields.len() {
49 return Err(syn::Error::new(
50 data.struct_token.span(),
51 format!(
52 "`{trait_name}` cannot be derived for structs with all the fields being \
53 skipped",
54 ),
55 ));
56 }
57 variants.push((None, &data.fields, skipped_fields));
58 }
59 syn::Data::Enum(data) => {
60 return Err(syn::Error::new(
61 data.enum_token.span(),
62 format!("`{trait_name}` cannot be derived for enums"),
63 ));
64 }
65 syn::Data::Union(data) => {
66 return Err(syn::Error::new(
67 data.union_token.span(),
68 format!("`{trait_name}` cannot be derived for unions"),
69 ));
70 }
71 }
72
73 Ok(AssignStructuralExpansion {
74 trait_ty: format_ident!("{trait_name}"),
75 method_ident: format_ident!("{}", trait_name_to_method_name(trait_name)),
76 self_ty: (&input.ident, &input.generics),
77 variants,
78 is_enum: false,
79 }
80 .into_token_stream())
81}
82
83fn normalize_trait_name(name: &str) -> &'static str {
85 match name {
86 "AddAssign" => "AddAssign",
87 "BitAndAssign" => "BitAndAssign",
88 "BitOrAssign" => "BitOrAssign",
89 "BitXorAssign" => "BitXorAssign",
90 "SubAssign" => "SubAssign",
91 _ => unimplemented!(),
92 }
93}
94
95fn trait_name_to_attribute_name(name: &str) -> &'static str {
97 trait_name_to_method_name(name)
98}
99
100fn trait_name_to_method_name(name: &str) -> &'static str {
102 match name {
103 "AddAssign" => "add_assign",
104 "BitAndAssign" => "bitand_assign",
105 "BitOrAssign" => "bitor_assign",
106 "BitXorAssign" => "bitxor_assign",
107 "SubAssign" => "sub_assign",
108 _ => unimplemented!(),
109 }
110}