borrow_macro/
lib.rs

1#![allow(clippy::panic)]
2#![allow(clippy::unwrap_used)]
3#![allow(clippy::expect_used)]
4
5use std::fmt::Debug;
6use quote::quote;
7use syn::{parse_macro_input, DeriveInput, Ident, Data, Fields, Type};
8use itertools::Itertools;
9use proc_macro2::TokenStream;
10use proc_macro2::Span;
11use syn::Token;
12use syn::parse::Parse;
13use syn::parse::ParseStream;
14
15// =============
16// === Utils ===
17// =============
18
19fn snake_to_camel(s: &str) -> String {
20    s.split('_').map(|s| {
21        let mut chars = s.chars();
22        match chars.next() {
23            None => String::new(),
24            Some(f) => f.to_uppercase().chain(chars).collect()
25        }
26    }).collect()
27}
28
29fn internal(s: &str) -> String {
30    format!("__{s}")
31}
32
33fn get_fields(input: &DeriveInput) -> Vec<&syn::Field> {
34    if let Data::Struct(data) = &input.data {
35        if let Fields::Named(fields) = &data.fields {
36            fields.named.iter().collect::<Vec<_>>()
37        } else {
38            Vec::new()
39        }
40    } else {
41        Vec::new()
42    }
43}
44
45fn get_params(input: &DeriveInput) -> TokenStream {
46    let lifetimes = input.generics.params.iter().filter_map(|t| {
47        if let syn::GenericParam::Lifetime(lt) = t {
48            Some(lt)
49        } else {
50            None
51        }
52    }).collect_vec();
53
54    let ty_params = input.generics.params.iter().filter_map(|t| {
55        if let syn::GenericParam::Type(ty) = t {
56            Some(ty.ident.clone())
57        } else {
58            None
59        }
60    }).collect_vec();
61    quote! {#(#lifetimes,)* #(#ty_params,)*}
62}
63
64fn get_bounds(input: &DeriveInput) -> TokenStream {
65    let inline_bounds = input.generics.params.iter().filter_map(|t| {
66        if let syn::GenericParam::Type(ty) = t {
67            (!ty.bounds.is_empty()).then_some(quote!{#ty})
68        } else {
69            None
70        }
71    }).collect_vec();
72
73    let where_bounds = input.generics.where_clause.as_ref().map(|t|
74        t.predicates.iter().map(|t| quote!{#t}).collect_vec()
75    ).unwrap_or_default();
76
77    quote! {#(#inline_bounds,)* #(#where_bounds,)*}
78}
79
80
81fn get_module_tokens(attr: &syn::Attribute) -> Option<TokenStream> {
82    if !attr.path().is_ident("module") {
83        return None;
84    }
85
86    // Parse as Meta::List to get access to the tokens inside
87    match &attr.meta {
88        syn::Meta::List(syn::MetaList { tokens, .. }) => Some(tokens.clone()),
89        _ => None,
90    }
91}
92
93// ===================
94// === Meta Derive ===
95// ===================
96
97fn meta_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
98    let input = parse_macro_input!(input as DeriveInput);
99    let ident = &input.ident;
100    let fields = get_fields(&input);
101    let params = get_params(&input);
102    let bounds = get_bounds(&input);
103    let field_types = fields.iter().map(|f| &f.ty).collect_vec();
104
105    let has_fields_for_struct = quote! {
106        impl<#params> borrow::HasFields for #ident<#params>
107        where #bounds {
108            type Fields = borrow::HList![#(#field_types,)*];
109        }
110    };
111
112    let has_fields_ext_for_struct = {
113        let fields_hidden = field_types.iter().map(|_| quote! {borrow::Hidden});
114        let fields_ref    = field_types.iter().map(|t| quote! {&'__a #t});
115        let fields_mut    = field_types.iter().map(|t| quote! {&'__a mut #t});
116        quote! {
117            impl<#params> borrow::HasFieldsExt for #ident<#params>
118            where #bounds {
119                type FieldsAsHidden = borrow::HList![ #(#fields_hidden,)* ];
120                type FieldsAsRef<'__a> = borrow::HList![ #(#fields_ref,)* ] where Self: '__a;
121                type FieldsAsMut<'__a> = borrow::HList![ #(#fields_mut,)* ] where Self: '__a;
122            }
123        }
124    };
125
126    let out = quote! {
127        #has_fields_for_struct
128        #has_fields_ext_for_struct
129    };
130
131    out.into()
132}
133
134// ======================
135// === Partial Derive ===
136// ======================
137
138// The internal macro documentation shows expansion parts for the following input:
139// ```
140// pub struct GeometryCtx {}
141// pub struct MaterialCtx {}
142// pub struct MeshCtx {}
143// pub struct SceneCtx {}
144//
145// #[derive(borrow::Partial)]
146// pub struct Ctx<'t, T: Debug> {
147//     pub version: &'t T,
148//     pub geometry: GeometryCtx,
149//     pub material: MaterialCtx,
150//     pub mesh: MeshCtx,
151//     pub scene: SceneCtx,
152// }
153//```
154#[allow(clippy::cognitive_complexity)]
155#[proc_macro_derive(Partial, attributes(module))]
156pub fn partial_borrow_derive(input_raw: proc_macro::TokenStream) -> proc_macro::TokenStream {
157
158    let input_raw2 = input_raw.clone();
159    let input = parse_macro_input!(input_raw2 as DeriveInput);
160
161    let path = input.attrs.iter()
162        .find_map(get_module_tokens)
163        .expect("Expected #[module(...)] attribute");
164
165    let ident = &input.ident;
166    let fields = get_fields(&input);
167    let params = get_params(&input);
168    let bounds = get_bounds(&input);
169
170    let fields_vis = fields.iter().map(|f| f.vis.clone()).collect_vec();
171    let fields_ident = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect_vec();
172    let fields_ty = fields.iter().map(|f| &f.ty).collect_vec();
173
174    // Fields in the form __$upper_case_field__
175    let fields_param = fields.iter().map(|f| {
176        let ident = f.ident.as_ref().unwrap();
177        Ident::new(&format!("__{}", snake_to_camel(&ident.to_string())), ident.span())
178    }).collect_vec();
179
180
181
182    let mut out: Vec<TokenStream> = vec![];
183
184    // === Ctx 1 ===
185
186    out.push(meta_derive(input_raw.clone()).into());
187
188    // === CtxRef 1 ===
189
190    let ref_ident = Ident::new(&format!("{ident}Ref"), ident.span());
191
192    // Generates:
193    //
194    // ```
195    // pub struct CtxRef<__Self__, __Track__, __Version, __Geometry, __Material, __Mesh, __Scene> {
196    //     pub version: borrow::Field<__Track__, __Version>,
197    //     pub geometry: borrow::Field<__Track__, __Geometry>,
198    //     pub material: borrow::Field<__Track__, __Material>,
199    //     pub mesh: borrow::Field<__Track__, __Mesh>,
200    //     pub scene: borrow::Field<__Track__, __Scene>,
201    //     pub marker: std::marker::PhantomData<__Self__>,
202    //     pub usage_tracker: borrow::UsageTracker,
203    // }
204    // ```
205    let ref_struct_def = {
206        quote! {
207            pub struct #ref_ident<__S__, __Track__, #(#fields_param,)*>
208            where __Track__: borrow::Bool {
209                #(#fields_vis #fields_ident: borrow::Field<__Track__, #fields_param>,)*
210                marker: std::marker::PhantomData<__S__>,
211                usage_tracker: borrow::UsageTracker,
212            }
213        }
214    };
215
216    out.push(ref_struct_def.clone());
217    out.push(meta_derive(ref_struct_def.into()).into());
218
219    // Generates:
220    //
221    // ```
222    // #[macro_export]
223    // macro_rules! CtxMacro {
224    //     (@0 $pfx:tt $track:tt $s:tt $($ts:tt)*) => { $crate::Ctx! { @1 $pfx $track $s [] [] [] [] [] $($ts)* } };
225    //     (@1 $pfx:tt $track:tt $s:tt $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt *        $n:tt $($ts:tt)*) => { $crate::Ctx! { @1 $pfx $track $s $n  $n  $n  $n  $n  $($ts)* } };
226    //     (@1 $pfx:tt $track:tt $s:tt $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt version  $n:tt $($ts:tt)*) => { $crate::Ctx! { @1 $pfx $track $s $n  $t1 $t2 $t3 $t4 $($ts)* } };
227    //     (@1 $pfx:tt $track:tt $s:tt $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt geometry $n:tt $($ts:tt)*) => { $crate::Ctx! { @1 $pfx $track $s $t0 $n  $t2 $t3 $t4 $($ts)* } };
228    //     (@1 $pfx:tt $track:tt $s:tt $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt material $n:tt $($ts:tt)*) => { $crate::Ctx! { @1 $pfx $track $s $t0 $t1 $n  $t3 $t4 $($ts)* } };
229    //     (@1 $pfx:tt $track:tt $s:tt $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt mesh     $n:tt $($ts:tt)*) => { $crate::Ctx! { @1 $pfx $track $s $t0 $t1 $t2 $n  $t4 $($ts)* } };
230    //     (@1 $pfx:tt $track:tt $s:tt $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt scene    $n:tt $($ts:tt)*) => { $crate::Ctx! { @1 $pfx $track $s $t0 $t1 $t2 $t3 $n  $($ts)* } };
231    //     (@1 [$($pfx:tt)*] [$($track:tt)*] [$s:ty] [$($t0:tt)*] [$($t1:tt)*] [$($t2:tt)*] [$($t3:tt)*] [$($t4:tt)*] ) => {
232    //         $($pfx)* CtxRef<
233    //             $s,
234    //             $($track)*,
235    //             borrow::field!{$s, N0, $($t0)*},
236    //             borrow::field!{$s, N1, $($t1)*},
237    //             borrow::field!{$s, N2, $($t2)*},
238    //             borrow::field!{$s, N3, $($t3)*},
239    //             borrow::field!{$s, N4, $($t4)*}
240    //         >
241    //     };
242    // }
243    // pub use CtxMacro as Ctx;
244    // ```
245    out.push({
246        fn matcher(i: usize) -> Ident {
247            Ident::new(&format!("t{i}"), Span::call_site())
248        }
249        let macro_ident = Ident::new(&format!("{ident}Macro"), ident.span());
250        let matchers = (0..fields_ident.len()).map(matcher).map(|t| quote!{$#t:tt}).collect_vec();
251        let def_results  = (0..fields_ident.len()).map(matcher).map(|t| quote!{$#t}).collect_vec();
252        let init_rule = {
253            let all_empty = (0..fields_ident.len()).map(|_| quote!{[]}).collect_vec();
254            quote! {
255                (@0 $pfx:tt $track:tt $s:tt $($ts:tt)*) => {
256                    #path::#ident! { @1 $pfx $track $s #(#all_empty)* $($ts)* }
257                };
258            }
259        };
260        let field_rules = fields_ident.iter().enumerate().map(|(i, field)| {
261            let mut results = def_results.clone();
262            results[i] = quote! {$n};
263            quote! {
264                (@1 $pfx:tt $track:tt $s:tt #(#matchers)* #field $n:tt $($ts:tt)*) => {
265                    #path::#ident! { @1 $pfx $track $s #(#results)* $($ts)* }
266                };
267            }
268        });
269        let star_rule = {
270            let all_n_results = (0..fields_ident.len()).map(|_| quote!{$n}).collect_vec();
271            quote! {
272                (@1 $pfx:tt $track:tt $s:tt #(#matchers)* * $n:tt $($ts:tt)*) => {
273                    #path::#ident! { @1 $pfx $track $s #(#all_n_results)*  $($ts)* }
274                };
275            }
276        };
277        let production = {
278            let matchers_exp = (0..fields_ident.len()).map(matcher).map(|t|
279                quote!{[$($#t:tt)*]}
280            ).collect_vec();
281            let fields = def_results.iter().enumerate().map(|(i, t)| {
282                let n = Ident::new(&format!("N{i}"), Span::call_site());
283                quote! {
284                    borrow::field!{$s, #n, $(#t)*}
285                }
286            }).collect_vec();
287            quote! {
288                (@1 [$($pfx:tt)*] [$($track:tt)*] [$s:ty] #(#matchers_exp)* ) => {
289                    $($pfx)* #path::#ref_ident<$s, $($track)*, #(#fields,)*>
290                };
291            }
292        };
293        quote! {
294            #[macro_export]
295            macro_rules! #macro_ident {
296                #init_rule
297                #star_rule
298                #(#field_rules)*
299                #production
300            }
301            pub use #macro_ident as #ident;
302        }
303    });
304
305    // Generates:
306    //
307    // ```
308    // impl<'t, T, __Version, __Geometry, __Material, __Mesh, __Scene>
309    // borrow::AsRefWithFields<borrow::HList![__Version, __Geometry, __Material, __Mesh, __Scene]>
310    // for Ctx<'t, T>
311    // where T: Debug {
312    //     type Output = CtxRef<Ctx<'t, T>, borrow::True, __Version, __Geometry, __Material, __Mesh, __Scene>;
313    // }
314    // ```
315    out.push(
316        quote! {
317            impl<#params #(#fields_param,)*>
318            borrow::AsRefWithFields<borrow::HList![#(#fields_param,)*]>
319            for #ident<#params>
320            where #bounds {
321                type Output = #ref_ident<#ident<#params>, borrow::True, #(#fields_param,)*>;
322            }
323        }
324    );
325
326    // Generates:
327    //
328    // ```
329    // impl<'__s__, __S__, __Track__, __Version, __Geometry, __Material, __Mesh, __Scene> borrow::CloneRef<'__s__>
330    // for CtxRef<__S__, __Track__, __Version, __Geometry, __Material, __Mesh, __Scene>
331    // where
332    //     __Track__: borrow::Bool,
333    //     borrow::Field<__Track__, __Version>: borrow::CloneField<'__s__, __Track__>,
334    //     borrow::Field<__Track__, __Geometry>: borrow::CloneField<'__s__, __Track__>,
335    //     borrow::Field<__Track__, __Material>: borrow::CloneField<'__s__, __Track__>,
336    //     borrow::Field<__Track__, __Mesh>: borrow::CloneField<'__s__, __Track__>,
337    //     borrow::Field<__Track__, __Scene>: borrow::CloneField<'__s__, __Track__>,
338    // {
339    //     type Cloned = CtxRef<
340    //         __S__,
341    //         __Track__,
342    //         borrow::ClonedField<'__s__, borrow::Field<__Track__, __Version>, __Track__>,
343    //         borrow::ClonedField<'__s__, borrow::Field<__Track__, __Geometry>, __Track__>,
344    //         borrow::ClonedField<'__s__, borrow::Field<__Track__, __Material>, __Track__>,
345    //         borrow::ClonedField<'__s__, borrow::Field<__Track__, __Mesh>, __Track__>,
346    //         borrow::ClonedField<'__s__, borrow::Field<__Track__, __Scene>, __Track__>
347    //     >;
348    //     fn clone_ref_disabled_usage_tracking(&'__s__ mut self) -> Self::Cloned {
349    //         use borrow::CloneField;
350    //         CtxRef {
351    //             version: self.version.clone_field_disabled_usage_tracking(),
352    //             geometry: self.geometry.clone_field_disabled_usage_tracking(),
353    //             material: self.material.clone_field_disabled_usage_tracking(),
354    //             mesh: self.mesh.clone_field_disabled_usage_tracking(),
355    //             scene: self.scene.clone_field_disabled_usage_tracking(),
356    //             marker: std::marker::PhantomData,
357    //             usage_tracker: borrow::UsageTracker::new(),
358    //         }
359    //     }
360    // }
361    // ```
362    out.push(
363        quote! {
364            impl<'__s__, __S__, __Track__, #(#fields_param,)*> borrow::CloneRef<'__s__>
365            for #ref_ident<__S__, __Track__, #(#fields_param,)*>
366            where
367                __Track__: borrow::Bool,
368                #(borrow::Field<__Track__, #fields_param>: borrow::CloneField<'__s__, __Track__>,)*
369            {
370                type Cloned = #ref_ident<
371                    __S__,
372                    __Track__,
373                    #(borrow::ClonedField<'__s__, borrow::Field<__Track__, #fields_param>, __Track__>,)*
374                >;
375                fn clone_ref_disabled_usage_tracking(&'__s__ mut self) -> Self::Cloned {
376                    use borrow::CloneField;
377                    #ref_ident {
378                        #(#fields_ident: self.#fields_ident.clone_field_disabled_usage_tracking(),)*
379                        marker: std::marker::PhantomData,
380                        usage_tracker: borrow::UsageTracker::new(),
381                    }
382                }
383            }
384        }
385    );
386
387    // Generates:
388    //
389    // ```
390    // #[allow(non_camel_case_types)]
391    // #[allow(non_snake_case)]
392    // impl<__S__, __Track__, __Track__Target__,
393    //     __Version, __Geometry, __Material, __Mesh, __Scene,
394    //     __Version__Target, __Geometry__Target, __Material__Target, __Mesh__Target, __Scene__Target,
395    //     __Version__Rest, __Geometry__Rest, __Material__Rest, __Mesh__Rest, __Scene__Rest>
396    // borrow::IntoPartial<CtxRef<__S__, __Track__Target__, __Version__Target, __Geometry__Target, __Material__Target, __Mesh__Target, __Scene__Target>>
397    // for CtxRef<__S__, __Track__, __Version, __Geometry, __Material, __Mesh, __Scene>
398    // where
399    //     __Track__: borrow::Bool,
400    //     __Track__Target__: borrow::Bool,
401    //     borrow::AcquireMarker: borrow::Acquire<__Version, __Version__Target, Rest=__Version__Rest>,
402    //     borrow::AcquireMarker: borrow::Acquire<__Geometry, __Geometry__Target, Rest=__Geometry__Rest>,
403    //     borrow::AcquireMarker: borrow::Acquire<__Material, __Material__Target, Rest=__Material__Rest>,
404    //     borrow::AcquireMarker: borrow::Acquire<__Mesh, __Mesh__Target, Rest=__Mesh__Rest>,
405    //     borrow::AcquireMarker: borrow::Acquire<__Scene, __Scene__Target, Rest=__Scene__Rest>,
406    // {
407    //     type Rest = CtxRef<__S__, __Track__, __Version__Rest, __Geometry__Rest, __Material__Rest, __Mesh__Rest, __Scene__Rest>;
408    //     #[track_caller]
409    //     #[inline(always)]
410    //     fn into_split_impl(
411    //         mut self
412    //     ) -> (CtxRef<
413    //         __S__,
414    //         __Track__Target__,
415    //         __Version__Target,
416    //         __Geometry__Target,
417    //         __Material__Target,
418    //         __Mesh__Target,
419    //         __Scene__Target
420    //     >,
421    //         Self::Rest
422    //     ) {
423    //         use borrow::Acquire;
424    //         let usage_tracker = borrow::UsageTracker::new();
425    //         let (version, __version__rest) = borrow::AcquireMarker::acquire(self.version, usage_tracker.clone());
426    //         let (geometry, __geometry__rest) = borrow::AcquireMarker::acquire(self.geometry, usage_tracker.clone());
427    //         let (material, __material__rest) = borrow::AcquireMarker::acquire(self.material, usage_tracker.clone());
428    //         let (mesh, __mesh__rest) = borrow::AcquireMarker::acquire(self.mesh, usage_tracker.clone());
429    //         let (scene, __scene__rest) = borrow::AcquireMarker::acquire(self.scene, usage_tracker.clone());
430    //         (
431    //             CtxRef {
432    //                 version,
433    //                 geometry,
434    //                 material,
435    //                 mesh,
436    //                 scene,
437    //                 marker: std::marker::PhantomData,
438    //                 usage_tracker
439    //             },
440    //             CtxRef {
441    //                 version: __version__rest,
442    //                 geometry: __geometry__rest,
443    //                 material: __material__rest,
444    //                 mesh: __mesh__rest,
445    //                 scene: __scene__rest,
446    //                 marker: std::marker::PhantomData,
447    //                 usage_tracker: borrow::UsageTracker::new(),
448    //             }
449    //         )
450    //     }
451    // }
452    // ```
453
454    out.push({
455        let field_params_target = fields_param.iter().map(|i| {
456            Ident::new(&format!("{i}{}", internal("Target")), i.span())
457        }).collect_vec();
458
459        let field_params_rest = fields_param.iter().map(|i| {
460            Ident::new(&format!("{i}{}", internal("Rest")), i.span())
461        }).collect_vec();
462
463        let fields_rest_ident = fields_ident.iter().map(|i|
464            Ident::new(&format!("{}{}", internal(&i.to_string()), internal("rest")), i.span())
465        ).collect_vec();
466
467        quote! {
468            #[allow(non_camel_case_types)]
469            #[allow(non_snake_case)]
470            impl<__S__, __Track__, __Track__Target__,
471                #(#fields_param,)*
472                #(#field_params_target,)*
473                #(#field_params_rest,)*
474            >
475            borrow::IntoPartial<#ref_ident<__S__, __Track__Target__, #(#field_params_target,)*>>
476            for #ref_ident<__S__, __Track__, #(#fields_param,)*>
477            where
478                __Track__: borrow::Bool,
479                __Track__Target__: borrow::Bool,
480                #(
481                    borrow::AcquireMarker: borrow::Acquire<
482                        #fields_param,
483                        #field_params_target,
484                        Rest=#field_params_rest
485                    >,
486                )*
487            {
488                type Rest = #ref_ident<__S__, __Track__, #(#field_params_rest,)*>;
489
490                #[track_caller]
491                #[inline(always)]
492                fn into_split_impl(
493                    mut self
494                ) -> (
495                    #ref_ident<__S__, __Track__Target__, #(#field_params_target,)*>,
496                    Self::Rest
497                ) {
498                    use borrow::Acquire;
499                    let usage_tracker = borrow::UsageTracker::new();
500                    #(let (#fields_ident, #fields_rest_ident) =
501                        borrow::AcquireMarker::acquire(self.#fields_ident, usage_tracker.clone());)*
502                    (
503                        #ref_ident {
504                            #(#fields_ident,)*
505                            marker: std::marker::PhantomData,
506                            usage_tracker
507                        },
508                        #ref_ident {
509                            #(#fields_ident: #fields_rest_ident,)*
510                            marker: std::marker::PhantomData,
511                            usage_tracker: borrow::UsageTracker::new()
512                        }
513                    )
514                }
515            }
516        }
517    });
518
519
520    // Generates:
521
522    // ```
523    // #[allow(non_camel_case_types)]
524    // impl<'__a__, __S__, __Track__, __Target__,
525    //     __Version, __Geometry, __Material, __Mesh, __Scene>
526    // borrow::Partial<'__a__, __Target__>
527    // for CtxRef<__S__, __Track__, __Version, __Geometry, __Material, __Mesh, __Scene> where
528    //     __Track__: borrow::Bool,
529    //     Self: borrow::CloneRef<'__a__>,
530    //     borrow::ClonedRef<'__a__, Self>: borrow::IntoPartial<__Target__>
531    // {
532    //     type Rest = <borrow::ClonedRef<'__a__, Self> as borrow::IntoPartial<__Target__>>::Rest;
533    //     #[track_caller]
534    //     #[inline(always)]
535    //     fn split_impl(&'__a__ mut self) -> (__Target__, Self::Rest) {
536    //         use borrow::CloneRef;
537    //         use borrow::IntoPartial;
538    //         // As the usage trackers are cloned and immediately destroyed by `into_split_impl`,
539    //         // we need to disable them.
540    //         let this = self.clone_ref_disabled_usage_tracking();
541    //         this.into_split_impl()
542    //     }
543    // }
544    // ```
545    out.push({
546        quote! {
547            #[allow(non_camel_case_types)]
548            impl<'__a__, __S__, __Track__, __Target__, #(#fields_param,)*>
549            borrow::Partial<'__a__, __Target__>
550            for #ref_ident<__S__, __Track__, #(#fields_param,)*> where
551                __Track__: borrow::Bool,
552                Self: borrow::CloneRef<'__a__>,
553                borrow::ClonedRef<'__a__, Self>: borrow::IntoPartial<__Target__>
554            {
555                type Rest = <borrow::ClonedRef<'__a__, Self> as borrow::IntoPartial<__Target__>>::Rest;
556                #[track_caller]
557                #[inline(always)]
558                fn split_impl(&'__a__ mut self) -> (__Target__, Self::Rest) {
559                    use borrow::CloneRef;
560                    use borrow::IntoPartial;
561                    // As the usage trackers are cloned and immediately destroyed by `into_split_impl`,
562                    // we need to disable them.
563                    let this = self.clone_ref_disabled_usage_tracking();
564                    this.into_split_impl()
565                }
566            }
567        }
568    });
569
570    // For each field. For the 'version' field:
571    //
572    // ```
573    // impl<'__s__, '__tgt__, 't, T, __Track__, __Version, __Geometry, __Material, __Mesh, __Scene>
574    // CtxRef<Ctx<'t, T>, __Track__, __Version, __Geometry, __Material, __Mesh, __Scene>
575    // where
576    //     __Track__: borrow::Bool,
577    //     T: Debug,
578    //     &'t T: '__tgt__,
579    //     Self: borrow::CloneRef<'__s__>,
580    //     borrow::ClonedRef<'__s__, Self>: borrow::IntoPartial<
581    //         CtxRef<
582    //             Ctx<'t, T>,
583    //             __Track__,
584    //             borrow::Hidden,
585    //             &'__tgt__ mut GeometryCtx,
586    //             borrow::Hidden,
587    //             borrow::Hidden,
588    //             borrow::Hidden
589    //         >
590    //     >
591    // {
592    //     #[track_caller]
593    //     #[inline(always)]
594    //     pub fn extract_geometry2(&'__s__ mut self) -> (
595    //         borrow::Field<__Track__, &'__tgt__ mut GeometryCtx>,
596    //         <borrow::ClonedRef<'__s__, Self> as borrow::IntoPartial<
597    //             CtxRef<
598    //                 Ctx<'t, T>,
599    //                 __Track__,
600    //                 borrow::Hidden,
601    //                 &'__tgt__ mut GeometryCtx,
602    //                 borrow::Hidden,
603    //                 borrow::Hidden,
604    //                 borrow::Hidden
605    //             >
606    //         >>::Rest
607    //     ) {
608    //         let split = borrow::IntoPartial::into_split_impl(
609    //             borrow::CloneRef::clone_ref_disabled_usage_tracking(self)
610    //         );
611    //         (split.0.geometry, split.1)
612    //     }
613    // }
614    // ```
615    out.extend((0..fields_param.len()).map(|i| {
616        let field_ident = &fields_ident[i];
617        let field_ty = &fields_ty[i];
618        let field_ref_mut = quote! {&'__tgt__ mut #field_ty};
619        let field_ref = quote! {&'__tgt__ #field_ty};
620
621        let mut params2 = fields_param.clone();
622        params2.remove(i);
623
624        let mut target_params_mut = fields_param.iter().map(|_| quote! {borrow::Hidden}).collect_vec();
625        target_params_mut[i] = field_ref_mut.clone();
626
627        let mut target_params = fields_param.iter().map(|_| quote! {borrow::Hidden}).collect_vec();
628        target_params[i] = field_ref.clone();
629
630        let fn_ident = Ident::new(&format!("borrow_{field_ident}"), field_ident.span());
631        let fn_ident_mut = Ident::new(&format!("borrow_{field_ident}_mut"), field_ident.span());
632
633        quote! {
634            #[allow(non_camel_case_types)]
635            impl<'__s__, '__tgt__, #params __Track__, #(#fields_param,)*>
636            #ref_ident<#ident<#params>, __Track__, #(#fields_param,)*>
637            where
638                #bounds
639                __Track__: borrow::Bool,
640                #field_ty: '__tgt__,
641                Self: borrow::CloneRef<'__s__>,
642                borrow::ClonedRef<'__s__, Self>: borrow::IntoPartial<
643                    #ref_ident<
644                        #ident<#params>,
645                        __Track__,
646                        #(#target_params_mut,)*
647                    >
648                >
649            {
650                #[track_caller]
651                #[inline(always)]
652                pub fn #fn_ident_mut(&'__s__ mut self) -> (
653                    borrow::Field<__Track__, #field_ref_mut>,
654                        <borrow::ClonedRef<'__s__, Self> as borrow::IntoPartial<
655                            #ref_ident<
656                                #ident<#params>,
657                                __Track__,
658                                #(#target_params_mut,)*
659                            >
660                        >>::Rest
661                ) {
662                    let split = borrow::IntoPartial::into_split_impl(
663                        borrow::CloneRef::clone_ref_disabled_usage_tracking(self)
664                    );
665                    (split.0.#field_ident, split.1)
666                }
667            }
668
669            #[allow(non_camel_case_types)]
670            impl<'__s__, '__tgt__, #params __Track__, #(#fields_param,)*>
671            #ref_ident<#ident<#params>, __Track__, #(#fields_param,)*>
672            where
673                #bounds
674                __Track__: borrow::Bool,
675                #field_ty: '__tgt__,
676                Self: borrow::CloneRef<'__s__>,
677                borrow::ClonedRef<'__s__, Self>: borrow::IntoPartial<
678                    #ref_ident<
679                        #ident<#params>,
680                        __Track__,
681                        #(#target_params,)*
682                    >
683                >
684            {
685                #[track_caller]
686                #[inline(always)]
687                pub fn #fn_ident(&'__s__ mut self) -> (
688                    borrow::Field<__Track__, #field_ref>,
689                        <borrow::ClonedRef<'__s__, Self> as borrow::IntoPartial<
690                            #ref_ident<
691                                #ident<#params>,
692                                __Track__,
693                                #(#target_params,)*
694                            >
695                        >>::Rest
696                ) {
697                    let split = borrow::IntoPartial::into_split_impl(
698                        borrow::CloneRef::clone_ref_disabled_usage_tracking(self)
699                    );
700                    (split.0.#field_ident, split.1)
701                }
702            }
703        }
704    }));
705
706
707    // Generates:
708    //
709    // ```
710    // impl<__S__, __Track__, __Version, __Geometry, __Material, __Mesh, __Scene> borrow::HasUsageTrackedFields
711    // for CtxRef<__S__, __Track__, __Version, __Geometry, __Material, __Mesh, __Scene>
712    // where __Track__: borrow::Bool {
713    //     #[inline(always)]
714    //     fn disable_field_usage_tracking(&self) {
715    //         self.version.disable_usage_tracking();
716    //         self.geometry.disable_usage_tracking();
717    //         self.material.disable_usage_tracking();
718    //         self.mesh.disable_usage_tracking();
719    //         self.scene.disable_usage_tracking();
720    //     }
721    //
722    //     #[inline(always)]
723    //     fn mark_all_fields_as_used(&self) {
724    //         self.version.mark_as_used();
725    //         self.geometry.mark_as_used();
726    //         self.material.mark_as_used();
727    //         self.mesh.mark_as_used();
728    //         self.scene.mark_as_used();
729    //     }
730    // }
731    // ```
732    out.push(quote! {
733        impl<__S__, __Track__, #(#fields_param,)*> borrow::HasUsageTrackedFields
734        for #ref_ident<__S__, __Track__, #(#fields_param,)*>
735        where __Track__: borrow::Bool {
736            #[inline(always)]
737            fn disable_field_usage_tracking(&self) {
738                #(self.#fields_ident.disable_usage_tracking();)*
739            }
740            #[inline(always)]
741            fn mark_all_fields_as_used(&self) {
742                #(self.#fields_ident.mark_as_used();)*
743            }
744        }
745    });
746
747    // Generates:
748    //
749    // ```
750    // impl<'t, T> borrow::AsRefsMut for Ctx<'t, T>
751    // where T: Debug {
752    //     type Target<'__s> =
753    //     borrow::RefWithFields<Ctx<'t, T>, borrow::FieldsAsMut<'__s, Ctx<'t, T>>>
754    //     where Self: '__s;
755    //     #[track_caller]
756    //     #[inline(always)]
757    //     fn as_refs_mut<'__s>(&'__s mut self) -> Self::Target<'__s> {
758    //         let usage_tracker = borrow::UsageTracker::new();
759    //         let struct_ref = CtxRef {
760    //             version: borrow::Field::new(
761    //                 "version",
762    //                 Some(borrow::Usage::Mut),
763    //                 &mut self.version,
764    //                 usage_tracker.clone()
765    //             ),
766    //             geometry: borrow::Field::new(
767    //                 "geometry",
768    //                 Some(borrow::Usage::Mut),
769    //                 &mut self.geometry,
770    //                 usage_tracker.clone()
771    //             ),
772    //             material: borrow::Field::new(
773    //                 "material",
774    //                 Some(borrow::Usage::Mut),
775    //                 &mut self.material,
776    //                 usage_tracker.clone()
777    //             ),
778    //             mesh: borrow::Field::new(
779    //                 "mesh",
780    //                 Some(borrow::Usage::Mut),
781    //                 &mut self.mesh,
782    //                 usage_tracker.clone()
783    //             ),
784    //             scene: borrow::Field::new(
785    //                 "scene",
786    //                 Some(borrow::Usage::Mut),
787    //                 &mut self.scene,
788    //                 usage_tracker.clone()
789    //             ),
790    //             marker: std::marker::PhantomData,
791    //             usage_tracker,
792    //         };
793    //         borrow::HasUsageTrackedFields::disable_field_usage_tracking(&struct_ref);
794    //         struct_ref
795    //     }
796    // }
797    // ```
798    out.push(quote! {
799        impl<#params> borrow::AsRefsMut for #ident<#params>
800        where #bounds {
801            type Target<'__s> =
802                borrow::RefWithFields<#ident<#params>, borrow::FieldsAsMut<'__s, #ident<#params>>>
803            where Self: '__s;
804            #[track_caller]
805            #[inline(always)]
806            fn as_refs_mut<'__s>(&'__s mut self) -> Self::Target<'__s> {
807                let usage_tracker = borrow::UsageTracker::new();
808                let struct_ref = #ref_ident {
809                    #(
810                        #fields_ident: borrow::Field::new(
811                            stringify!(#fields_ident),
812                            Some(borrow::Usage::Mut),
813                            &mut self.#fields_ident,
814                            usage_tracker.clone(),
815                        ),
816                    )*
817                    marker: std::marker::PhantomData,
818                    usage_tracker
819                };
820                borrow::HasUsageTrackedFields::disable_field_usage_tracking(&struct_ref);
821                struct_ref
822            }
823        }
824    });
825
826    let output = quote! {
827        #(#out)*
828    };
829
830    // println!("OUTPUT:\n{}", output);
831    output.into()
832}
833
834// ======================
835// === partial! Macro ===
836// ======================
837
838#[derive(Debug)]
839enum Selector {
840    Ident { lifetime: Option<TokenStream>, is_mut: bool, ident: Ident },
841    Star { lifetime: Option<TokenStream>, is_mut: bool }
842}
843
844enum Selectors {
845    List(Vec<Selector>),
846    All
847}
848
849// #[derive(Debug)]
850struct MyInput {
851    has_underscore: bool,
852    has_amp: bool,
853    lifetime: Option<TokenStream>,
854    selectors: Selectors,
855    target: Type,
856}
857
858fn parse_angled_list<T: Parse>(input: ParseStream) -> Vec<T> {
859    let mut params = vec![];
860    while !input.peek(Token![>]) {
861        if let Ok(value) = input.parse::<T>() {
862            params.push(value);
863        } else {
864            break
865        }
866        if input.peek(Token![>]) {
867            break;
868        }
869        input.parse::<Token![,]>().ok();
870    }
871    params
872}
873
874
875impl Parse for Selector {
876    fn parse(input: ParseStream) -> syn::Result<Self> {
877        let lifetime = input.parse::<syn::Lifetime>().ok().map(|t| quote! { #t });
878        let is_mut = input.parse::<Token![mut]>().is_ok();
879        if input.parse::<Token![*]>().is_ok() {
880            Ok(Selector::Star{ lifetime, is_mut })
881        } else {
882            let ident: Ident = input.parse()?;
883            Ok(Selector::Ident{ lifetime, is_mut, ident })
884        }
885    }
886}
887
888impl Parse for MyInput {
889    fn parse(input: ParseStream) -> syn::Result<Self> {
890        let has_underscore = input.parse::<Token![_]>().is_ok();
891        let has_amp = input.parse::<Token![&]>().is_ok();
892
893        let lifetime = input.parse::<syn::Lifetime>().ok().map(|t| quote! { #t });
894
895        let selectors = if input.parse::<Token![mut]>().is_ok() {
896            Selectors::All
897        } else if input.parse::<Token![<]>().is_ok() {
898            let selectors = parse_angled_list::<Selector>(input);
899            input.parse::<Token![>]>()?;
900            Selectors::List(selectors)
901        } else {
902            Selectors::List(vec![])
903        };
904
905        let target: Type = input.parse()?;
906
907        Ok(MyInput {
908            has_underscore,
909            has_amp,
910            lifetime,
911            selectors,
912            target,
913        })
914    }
915}
916
917#[allow(clippy::cognitive_complexity)]
918#[proc_macro]
919pub fn partial(input_raw: proc_macro::TokenStream) -> proc_macro::TokenStream {
920    let input = parse_macro_input!(input_raw as MyInput);
921
922    let target_ident = match &input.target {
923        Type::Path(type_path) if type_path.path.segments.len() == 1 => {
924            let ident = &type_path.path.segments[0].ident;
925            let is_lower = ident.to_string().chars().next().is_some_and(|c| c.is_lowercase());
926            is_lower.then_some(&type_path.path.segments[0].ident)
927        }
928        _ => None,
929    };
930
931    let out = if let Some(target_ident) = target_ident {
932        quote! {
933            &mut #target_ident.partial_borrow()
934        }
935    } else {
936        let target_ident = match &input.target {
937            Type::Path(type_path) if type_path.path.segments.len() == 1 => {
938                &type_path.path.segments[0].ident
939            }
940            _ => panic!()
941        };
942
943        let target = &input.target;
944        let default_lifetime = input.lifetime.unwrap_or_else(|| quote!{ '_ });
945        let mut out = quote! { };
946        match &input.selectors {
947            Selectors::All => out = quote! {
948                borrow::FieldsAsMut <#default_lifetime, #target>
949            },
950            Selectors::List(selectors) => {
951                for selector in selectors {
952                    out = match selector {
953                        Selector::Ident { lifetime, is_mut, ident } => {
954                            let lt = lifetime.as_ref().unwrap_or(&default_lifetime);
955                            if *is_mut {
956                                quote! { #out #ident [& #lt mut]   }
957                            } else {
958                                quote! { #out #ident [& #lt]   }
959                            }
960                        }
961                        Selector::Star { lifetime, is_mut } => {
962                            let lt = lifetime.as_ref().unwrap_or(&default_lifetime);
963                            if *is_mut {
964                                quote! { * [& #lt mut]    }
965                            } else {
966                                quote! { * [& #lt]   }
967                            }
968                        }
969                    }
970                }
971            }
972        }
973
974        let track = if input.has_underscore {
975            quote! { borrow::False }
976        } else {
977            quote! { borrow::True }
978        };
979        let pfx = if input.has_amp {
980            quote! { [& #default_lifetime mut] }
981        } else {
982            quote! { [] }
983        };
984
985        out = quote! {
986            #target_ident!{@0 #pfx [#track] [#target] #out}
987        };
988        out
989    };
990
991    // println!("{}", out);
992    out.into()
993}