1use quote::{ToTokens, quote};
22use syn::{Data, DeriveInput, Fields, ImplItem, Meta, Token, punctuated::Punctuated, Type};
23use proc_macro2::TokenStream as TokenStream2;
24
25#[proc_macro_derive(Deref, attributes(deref))]
26pub fn derive_deref(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
27 let input = syn::parse::<DeriveInput>(item).expect("`Deref` macro can only be used for struct");
28 if let Data::Struct(data_struct) = input.data {
29 let mut field_option: Option<(TokenStream2, &Type)> = None;
30 match &data_struct.fields {
31 Fields::Named(fields_named) => {
32 'a: for field in fields_named.named.iter() {
33 for attr in field.attrs.iter() {
34 if let Meta::Path(path) = attr.parse_meta().unwrap() {
35 let last = path.segments.last().unwrap();
36 let ident = &last.ident;
37 if ident.to_string() == "deref" {
38 field_option = Some((field.ident.as_ref().unwrap().to_token_stream(), &field.ty));
39 break 'a;
40 }
41 }
42 }
43 }
44 },
45 Fields::Unnamed(fields_unnamed) => {
46 let mut i = 0;
47 'b: for field in fields_unnamed.unnamed.iter() {
48 for attr in field.attrs.iter() {
49 if let Meta::Path(path) = attr.parse_meta().unwrap() {
50 let last = path.segments.last().unwrap();
51 let ident = &last.ident;
52 if ident.to_string() == "deref" {
53 field_option = Some((syn::Index::from(i).to_token_stream(), &field.ty));
54 break 'b;
55 }
56 }
57 }
58 i += 1;
59 }
60 },
61 Fields::Unit => {}
62 };
63 if let Some((field_token, field_type)) = field_option {
64 let mut type_args: Punctuated<_, syn::token::Comma> = Punctuated::new();
65 for param in input.generics.type_params() {
66 type_args.push(param.ident.clone());
67 }
68
69 let items = quote! {
70 type Target = #field_type;
71
72 fn deref(&self) -> &Self::Target {
73 &self.#field_token
74 }
75 };
76
77 let span = proc_macro2::Span::call_site();
78 let ident = &input.ident;
79 syn::ItemImpl {
80 attrs: vec![],
81 defaultness: None,
82 unsafety: None,
83 impl_token: Token,
84 generics: input.generics.clone(),
85 trait_: Some((None, syn::parse2(quote! { std::ops::Deref }).unwrap(), Token)),
86 self_ty: Box::new(syn::parse2(quote! { #ident<#type_args> }).unwrap()),
87 brace_token: syn::token::Brace { span },
88 items: vec![ImplItem::Verbatim(items)]
89 }.to_token_stream().into()
90 } else {
91 proc_macro::TokenStream::new()
92 }
93 } else {
94 proc_macro::TokenStream::new()
95 }
96}
97
98#[proc_macro_derive(DerefMut, attributes(deref))]
99pub fn derive_deref_mut(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
100 let input = syn::parse::<DeriveInput>(item).expect("`DerefMut` macro can only be used for struct");
101 if let Data::Struct(data_struct) = input.data {
102 let mut field_option: Option<(TokenStream2, &Type)> = None;
103 match &data_struct.fields {
104 Fields::Named(fields_named) => {
105 'a: for field in fields_named.named.iter() {
106 for attr in field.attrs.iter() {
107 if let Meta::Path(path) = attr.parse_meta().unwrap() {
108 let last = path.segments.last().unwrap();
109 let ident = &last.ident;
110 if ident.to_string() == "deref" {
111 field_option = Some((field.ident.as_ref().unwrap().to_token_stream(), &field.ty));
112 break 'a;
113 }
114 }
115 }
116 }
117 },
118 Fields::Unnamed(fields_unnamed) => {
119 let mut i = 0;
120 'b: for field in fields_unnamed.unnamed.iter() {
121 for attr in field.attrs.iter() {
122 if let Meta::Path(path) = attr.parse_meta().unwrap() {
123 let last = path.segments.last().unwrap();
124 let ident = &last.ident;
125 if ident.to_string() == "deref" {
126 field_option = Some((syn::Index::from(i).to_token_stream(), &field.ty));
127 break 'b;
128 }
129 }
130 }
131 i += 1;
132 }
133 },
134 Fields::Unit => {}
135 };
136 if let Some((field_token, _field_type)) = field_option {
137 let mut type_args: Punctuated<_, syn::token::Comma> = Punctuated::new();
138 for param in input.generics.type_params() {
139 type_args.push(param.ident.clone());
140 }
141
142 let items = quote! {
143 fn deref_mut(&mut self) -> &mut Self::Target {
144 &mut self.#field_token
145 }
146 };
147
148 let span = proc_macro2::Span::call_site();
149 let ident = &input.ident;
150 syn::ItemImpl {
151 attrs: vec![],
152 defaultness: None,
153 unsafety: None,
154 impl_token: Token,
155 generics: input.generics.clone(),
156 trait_: Some((None, syn::parse2(quote! { std::ops::DerefMut }).unwrap(), Token)),
157 self_ty: Box::new(syn::parse2(quote! { #ident<#type_args> }).unwrap()),
158 brace_token: syn::token::Brace { span },
159 items: vec![ImplItem::Verbatim(items)]
160 }.to_token_stream().into()
161 } else {
162 proc_macro::TokenStream::new()
163 }
164 } else {
165 proc_macro::TokenStream::new()
166 }
167}