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}