1extern crate proc_macro;
29
30use proc_macro::TokenStream;
31use syn::{parse_macro_input, ItemStruct, Ident, Generics, Field, Fields, Index, Type, punctuated::Punctuated, token::Comma};
32use quote::quote;
33use proc_macro2::TokenStream as TokenStream2;
34
35#[proc_macro_derive(Deref, attributes(target))]
38pub fn derive_deref(input: TokenStream) -> TokenStream {
39 let item_struct = parse_macro_input!(input as ItemStruct);
41 let name = item_struct.ident;
42 let generics = item_struct.generics;
43
44 match extract_field_parameters(item_struct.fields, "Deref") {
47 Ok((field_name, field_type, is_mut_reference)) => impl_deref(name, generics, field_name, Some(field_type), is_mut_reference),
48 Err(error) => error,
49 }
50}
51
52#[proc_macro_derive(DerefMut, attributes(target))]
56pub fn derive_deref_mut(input: TokenStream) -> TokenStream {
59 let item_struct = parse_macro_input!(input as ItemStruct);
60 let name = item_struct.ident;
61 let generics = item_struct.generics;
62
63 match extract_field_parameters(item_struct.fields, "DerefMut") {
64 Ok((field_name, _, is_mut_reference)) => impl_deref(name, generics, field_name, None, is_mut_reference),
65 Err(error) => error,
66 }
67}
68
69fn get_field(fields: Punctuated<Field, Comma>) -> Result<(usize, Field), TokenStream> {
71 let attribute_name = "target";
72 let error = || quote! { compile_error!("`#[target]` is required for one field"); }.into();
73
74 let has_one_field = fields.len() == 1;
75 let mut fields_iter = fields.into_iter().fuse().enumerate();
76
77 if has_one_field {
78 fields_iter.next().ok_or_else(error)
80 } else {
81 let mut fields_iter = fields_iter.filter(|(_, field)| {
83 field.attrs.iter().any(|attribute| {
84 attribute.meta
85 .require_path_only()
86 .is_ok_and(|path| path.is_ident(attribute_name))
87 })
88 });
89
90 fields_iter.next().filter(|_| {
93 fields_iter
94 .next()
95 .is_none()
96 })
97 .ok_or_else(error)
98 }
99}
100
101fn extract_field_parameters(fields: Fields, trait_name: &str) -> Result<(TokenStream2, Type, Option<bool>), TokenStream> {
102 match fields {
103 Fields::Named(fields) => {
104 let (_, field) = get_field(fields.named)?;
105 let field_name = field.ident.unwrap();
106 let (field_type, is_mut_reference) = match field.ty {
107 Type::Reference(reference_type) => (*reference_type.elem, Some(reference_type.mutability.is_some())),
108 field_type => (field_type, None),
109 };
110
111 Ok((quote! { #field_name }, field_type, is_mut_reference))
112 },
113 Fields::Unnamed(fields) => {
114 let (field_index, field) = get_field(fields.unnamed)?;
115 let field_index = Index::from(field_index);
116 let (field_type, is_mut_reference) = match field.ty {
117 Type::Reference(reference_type) => (*reference_type.elem, Some(reference_type.mutability.is_some())),
118 field_type => (field_type, None),
119 };
120
121 Ok((quote! { #field_index }, field_type, is_mut_reference))
122 },
123 Fields::Unit => {
124 let error = &format!("unable to implement `{}` trait for struct of no fields", trait_name)[..];
125
126 Err(quote! { compile_error!(#error); }.into())
127 }
128 }
129}
130
131fn impl_deref(
132 struct_name: Ident,
133 struct_generics: Generics,
134 field_name: TokenStream2,
135 field_type: Option<Type>,
138 is_mut_reference: Option<bool>,
141) -> TokenStream
142{
143 let (impl_generics, type_generics, where_clause) = struct_generics.split_for_impl();
144
145 match field_type {
146 Some(field_type) => {
147 let reference = is_mut_reference.map_or_else(|| Some(quote!(&)), |_| None);
149
150 quote! {
151 impl #impl_generics core::ops::Deref for #struct_name #type_generics #where_clause {
152 type Target = #field_type;
153
154 fn deref(&self) -> &Self::Target {
155 #reference self.#field_name
156 }
157 }
158 }
159 },
160 None => {
161 let reference = match is_mut_reference {
162 Some(true) => None,
163 Some(false) => return quote! { compile_error!("`#[target]` is unable to be of an immutable reference"); }.into(),
164 None => Some(quote!(&mut)),
165 };
166
167 quote! {
168 impl #impl_generics core::ops::DerefMut for #struct_name #type_generics #where_clause {
169 fn deref_mut(&mut self) -> &mut Self::Target {
170 #reference self.#field_name
171 }
172 }
173 }
174 },
175 }
176 .into()
177}