derive_tools_meta/derive/
deref_mut.rs

1use macro_tools ::
2{
3  diag, generic_params, struct_like ::StructLike, Result, qt, attr, syn, proc_macro2, return_syn_err, syn_err, Spanned,
4};
5
6///
7/// Derive macro to implement `DerefMut` when-ever it's possible to do automatically.
8///
9pub fn deref_mut(input: proc_macro ::TokenStream) -> Result< proc_macro2 ::TokenStream > 
10{
11  let original_input = input.clone();
12  let parsed = syn ::parse :: < StructLike >(input)?;
13  let has_debug = attr ::has_debug(parsed.attrs().iter())?;
14  let item_name = &parsed.ident();
15
16  let (_generics_with_defaults, generics_impl, generics_ty, generics_where) = generic_params ::decompose(parsed.generics());
17
18  let result =  match parsed 
19  {
20  StructLike ::Unit(ref _item) =>
21  {
22   return_syn_err!(parsed.span(), "Expects a structure with one field");
23 }
24  StructLike ::Struct(ref item) =>
25  {
26   let fields_count = item.fields.len();
27   let mut target_field_type = None;
28   let mut target_field_name = None;
29   let mut deref_mut_attr_count = 0;
30
31   if fields_count == 0 
32   {
33  return_syn_err!(item.span(), "DerefMut cannot be derived for structs with no fields.");
34 } else  if fields_count == 1 
35  {
36  // Single field struct: automatically deref_mut to that field
37  let field = item.fields.iter().next().expect("Expects a single field to derive DerefMut");
38  target_field_type = Some(field.ty.clone());
39  target_field_name.clone_from(&field.ident);
40 } else {
41  // Multi-field struct: require #[ deref_mut ] attribute on one field
42  for field in &item.fields 
43  {
44   if attr ::has_deref_mut(field.attrs.iter())? 
45   {
46  deref_mut_attr_count += 1;
47  target_field_type = Some(field.ty.clone());
48  target_field_name.clone_from(&field.ident);
49 }
50 }
51
52  if deref_mut_attr_count == 0 
53  {
54   return_syn_err!(
55  item.span(),
56  "DerefMut cannot be derived for multi-field structs without a `#[ deref_mut ]` attribute on one field."
57 );
58 } else  if deref_mut_attr_count > 1 
59  {
60   return_syn_err!(item.span(), "Only one field can have the `#[ deref_mut ]` attribute.");
61 }
62 }
63
64   let field_type =
65  target_field_type.ok_or_else(|| syn_err!(item.span(), "Could not determine target field type for DerefMut."))?;
66   let field_name = target_field_name;
67
68   generate(
69  item_name,
70  &generics_impl,
71  &generics_ty,
72  &generics_where,
73  &field_type,
74  field_name.as_ref(),
75 )
76 }
77  StructLike ::Enum(ref item) =>
78  {
79   return_syn_err!(
80  item.span(),
81  "DerefMut cannot be derived for enums. It is only applicable to structs with a single field."
82 );
83 }
84 };
85
86  if has_debug 
87  {
88  let about = format!("derive: DerefMut\nstructure: {item_name}");
89  diag ::report_print(about, &original_input, &result);
90 }
91
92  Ok(result)
93}
94
95/// Generates `DerefMut` implementation for structs.
96///
97/// Example of generated code :
98/// ```text
99/// impl DerefMut for IsTransparent
100/// {
101///   fn deref_mut( &mut self ) -> &mut bool
102/// ///   {
103/// ///     &mut self.0
104/// /// }
105/// /// }
106/// ```
107fn generate(
108  item_name: &syn ::Ident,
109  generics_impl: &syn ::punctuated ::Punctuated< syn ::GenericParam, syn ::token ::Comma >,
110  generics_ty: &syn ::punctuated ::Punctuated< syn ::GenericParam, syn ::token ::Comma >,
111  generics_where: &syn ::punctuated ::Punctuated< syn ::WherePredicate, syn ::token ::Comma >,
112  field_type: &syn ::Type,
113  field_name: Option< &syn ::Ident >,
114) -> proc_macro2 ::TokenStream {
115  let body =  if let Some(field_name) = field_name 
116  {
117  qt! { &mut self.#field_name }
118 } else {
119  qt! { &mut self.0 }
120 };
121
122  qt! {
123  #[ automatically_derived ]
124  impl #generics_impl ::core ::ops ::DerefMut for #item_name #generics_ty
125  where
126   #generics_where
127  {
128   fn deref_mut( &mut self ) -> &mut #field_type
129   {
130  #body
131 }
132 }
133 }
134}