1use quote::{format_ident, quote};
50
51use proc_macro::TokenStream;
52use syn::{parse_macro_input, DeriveInput};
53
54mod field_kind;
55mod helpers;
56
57use field_kind::FieldKind;
58
59#[proc_macro_derive(IntoOwned)]
60pub fn into_owned(input: TokenStream) -> TokenStream {
61 let ast = parse_macro_input!(input as DeriveInput);
62
63 let expanded = impl_with_generator(&ast, IntoOwnedGen);
64
65 TokenStream::from(expanded)
66}
67
68#[proc_macro_derive(Borrowed)]
69pub fn borrowed(input: TokenStream) -> TokenStream {
70 let ast = parse_macro_input!(input as DeriveInput);
71
72 let expanded = impl_with_generator(&ast, BorrowedGen);
73
74 TokenStream::from(expanded)
75}
76
77fn impl_with_generator<G: BodyGenerator>(
78 ast: &syn::DeriveInput,
79 gen: G,
80) -> proc_macro2::TokenStream {
81 let name = &ast.ident;
83
84 let borrowed_params = gen.quote_borrowed_params(ast);
85 let borrowed = if borrowed_params.is_empty() {
86 quote! {}
87 } else {
88 quote! { < #(#borrowed_params),* > }
89 };
90
91 let params = gen.quote_type_params(ast);
92 let params = if params.is_empty() {
93 quote! {}
94 } else {
95 quote! { < #(#params),* > }
96 };
97
98 let owned_params = gen.quote_rhs_params(ast);
99 let owned = if owned_params.is_empty() {
100 quote! {}
101 } else {
102 quote! { < #(#owned_params),* > }
103 };
104
105 let body = match ast.data {
106 syn::Data::Struct(ref body) => {
107 let inner = gen.visit_struct(body);
108 quote! { #name #inner }
109 }
110 syn::Data::Enum(ref body) => {
111 let cases = body.variants.iter().map(|variant| {
112 let unqualified_ident = &variant.ident;
113 let ident = quote! { #name::#unqualified_ident };
114
115 gen.visit_enum_data(ident, variant)
116 });
117 quote! { match self { #(#cases),* } }
118 }
119 syn::Data::Union(_) => todo!("unions are not supported (5)"),
120 };
121
122 gen.combine_impl(borrowed, name, params, owned, body)
123}
124
125trait BodyGenerator {
127 fn quote_borrowed_params(&self, ast: &syn::DeriveInput) -> Vec<proc_macro2::TokenStream> {
128 let borrowed_lifetime_params = ast.generics.lifetimes().map(|alpha| quote! { #alpha });
129 let borrowed_type_params = ast.generics.type_params().map(|ty| quote! { #ty });
130 borrowed_lifetime_params
131 .chain(borrowed_type_params)
132 .collect::<Vec<_>>()
133 }
134
135 fn quote_type_params(&self, ast: &syn::DeriveInput) -> Vec<proc_macro2::TokenStream> {
136 ast.generics
137 .lifetimes()
138 .map(|alpha| quote! { #alpha })
139 .chain(ast.generics.type_params().map(|ty| {
140 let ident = &ty.ident;
141 quote! { #ident }
142 }))
143 .collect::<Vec<_>>()
144 }
145
146 fn quote_rhs_params(&self, ast: &syn::DeriveInput) -> Vec<proc_macro2::TokenStream> {
147 let owned_lifetime_params = ast.generics.lifetimes().map(|_| quote! { 'static });
148 let owned_type_params = ast.generics.type_params().map(|ty| {
149 let ident = &ty.ident;
150 quote! { #ident }
151 });
152 owned_lifetime_params
153 .chain(owned_type_params)
154 .collect::<Vec<_>>()
155 }
156
157 fn visit_struct(&self, data: &syn::DataStruct) -> proc_macro2::TokenStream;
158 fn visit_enum_data(
159 &self,
160 variant: proc_macro2::TokenStream,
161 data: &syn::Variant,
162 ) -> proc_macro2::TokenStream;
163 fn combine_impl(
164 &self,
165 borrows: proc_macro2::TokenStream,
166 name: &syn::Ident,
167 rhs_params: proc_macro2::TokenStream,
168 owned: proc_macro2::TokenStream,
169 body: proc_macro2::TokenStream,
170 ) -> proc_macro2::TokenStream;
171}
172
173struct IntoOwnedGen;
174
175impl BodyGenerator for IntoOwnedGen {
176 fn visit_struct(&self, data: &syn::DataStruct) -> proc_macro2::TokenStream {
177 enum Fields {
179 Named,
180 Tuple,
181 Unit,
182 }
183
184 use Fields::*;
185
186 let fields_kind = data
187 .fields
188 .iter()
189 .next()
190 .map(|field| if field.ident.is_some() { Named } else { Tuple })
191 .unwrap_or(Unit);
192
193 match fields_kind {
194 Named => {
195 let fields = data.fields.iter().map(|field| {
196 let ident = field.ident.as_ref().expect("unexpected unnamed field");
197 let field_ref = quote! { self.#ident };
198 let code = FieldKind::resolve(&field.ty).move_or_clone_field(&field_ref);
199 quote! { #ident: #code }
200 });
201 quote! { { #(#fields),* } }
202 }
203 Tuple => {
204 let fields = data.fields.iter().enumerate().map(|(index, field)| {
205 let index = syn::Index::from(index);
206 let index = quote! { self.#index };
207 FieldKind::resolve(&field.ty).move_or_clone_field(&index)
208 });
209 quote! { ( #(#fields),* ) }
210 }
211 Unit => {
212 quote! {}
213 }
214 }
215 }
216
217 fn visit_enum_data(
218 &self,
219 ident: proc_macro2::TokenStream,
220 variant: &syn::Variant,
221 ) -> proc_macro2::TokenStream {
222 if variant.fields.is_empty() {
223 return quote!(#ident => #ident);
224 }
225
226 let fields_are_named = variant.fields.iter().any(|field| field.ident.is_some());
227
228 if fields_are_named {
229 let named_fields = variant
230 .fields
231 .iter()
232 .filter_map(|field| field.ident.as_ref());
233
234 let cloned = variant.fields.iter().map(|field| {
235 let ident = field.ident.as_ref().unwrap();
236 let ident = quote!(#ident);
237 let code = FieldKind::resolve(&field.ty).move_or_clone_field(&ident);
238 quote! { #ident: #code }
239 });
240 quote! { #ident { #(#named_fields),* } => #ident { #(#cloned),* } }
241 } else {
242 let unnamed_fields = &variant
243 .fields
244 .iter()
245 .enumerate()
246 .filter(|(_, field)| field.ident.is_none())
247 .map(|(index, _)| format_ident!("arg_{}", index))
248 .collect::<Vec<_>>();
249
250 let cloned = unnamed_fields
251 .iter()
252 .zip(variant.fields.iter())
253 .map(|(ident, field)| {
254 let ident = quote! { #ident };
255 FieldKind::resolve(&field.ty).move_or_clone_field(&ident)
256 })
257 .collect::<Vec<_>>();
258
259 quote! { #ident ( #(#unnamed_fields),* ) => #ident ( #(#cloned),* ) }
260 }
261 }
262
263 fn combine_impl(
264 &self,
265 borrowed: proc_macro2::TokenStream,
266 name: &syn::Ident,
267 params: proc_macro2::TokenStream,
268 owned: proc_macro2::TokenStream,
269 body: proc_macro2::TokenStream,
270 ) -> proc_macro2::TokenStream {
271 quote! {
272 impl #borrowed #name #params {
273 pub fn into_owned(self) -> #name #owned { #body }
275 }
276 }
277 }
278}
279
280struct BorrowedGen;
281
282impl BodyGenerator for BorrowedGen {
283 fn quote_rhs_params(&self, ast: &syn::DeriveInput) -> Vec<proc_macro2::TokenStream> {
284 let owned_lifetime_params = ast.generics.lifetimes().map(|_| quote! { '__borrowedgen });
285 let owned_type_params = ast.generics.type_params().map(|ty| {
286 let ident = &ty.ident;
287 quote! { #ident }
288 });
289 owned_lifetime_params
290 .chain(owned_type_params)
291 .collect::<Vec<_>>()
292 }
293
294 fn visit_struct(&self, data: &syn::DataStruct) -> proc_macro2::TokenStream {
295 let fields = data.fields.iter().map(|field| {
296 let ident = field.ident.as_ref().expect("this fields has no ident (4)");
297 let field_ref = quote! { self.#ident };
298 let code = FieldKind::resolve(&field.ty).borrow_or_clone(&field_ref);
299 quote! { #ident: #code }
300 });
301 quote! { { #(#fields),* } }
302 }
303
304 fn visit_enum_data(
305 &self,
306 ident: proc_macro2::TokenStream,
307 variant: &syn::Variant,
308 ) -> proc_macro2::TokenStream {
309 if variant.fields.is_empty() {
310 return quote!(#ident => #ident);
311 }
312
313 let fields_are_named = variant.fields.iter().any(|field| field.ident.is_some());
314 if fields_are_named {
315 let idents = variant
316 .fields
317 .iter()
318 .map(|field| field.ident.as_ref().expect("this fields has no ident (5)"));
319 let cloned = variant.fields.iter().map(|field| {
320 let ident = field.ident.as_ref().expect("this fields has no ident (6)");
321 let ident = quote! { #ident };
322 let code = FieldKind::resolve(&field.ty).borrow_or_clone(&ident);
323 quote! { #ident: #code }
324 });
325 quote! { #ident { #(ref #idents),* } => #ident { #(#cloned),* } }
326 } else {
327 let idents = (0..variant.fields.len())
328 .map(|index| quote::format_ident!("x{}", index))
329 .collect::<Vec<_>>();
330 let cloned = idents
331 .iter()
332 .zip(variant.fields.iter())
333 .map(|(ident, field)| {
334 let ident = quote! { #ident };
335 FieldKind::resolve(&field.ty).borrow_or_clone(&ident)
336 })
337 .collect::<Vec<_>>();
338 quote! { #ident ( #(ref #idents),* ) => #ident ( #(#cloned),* ) }
339 }
340 }
341
342 fn combine_impl(
343 &self,
344 borrowed: proc_macro2::TokenStream,
345 name: &syn::Ident,
346 params: proc_macro2::TokenStream,
347 owned: proc_macro2::TokenStream,
348 body: proc_macro2::TokenStream,
349 ) -> proc_macro2::TokenStream {
350 quote! {
351 impl #borrowed #name #params {
352 pub fn borrowed<'__borrowedgen>(&'__borrowedgen self) -> #name #owned { #body }
354 }
355 }
356 }
357}