key_paths_derive/lib.rs
1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{Data, DeriveInput, Fields, Type, parse_macro_input, spanned::Spanned};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6enum WrapperKind {
7 None,
8 Option,
9 Box,
10 Rc,
11 Arc,
12 Vec,
13 HashMap,
14 BTreeMap,
15 HashSet,
16 BTreeSet,
17 VecDeque,
18 LinkedList,
19 BinaryHeap,
20 // Error handling containers
21 Result,
22 // Reference counting with weak references
23 Weak,
24 // String types (currently unused)
25 // String,
26 // OsString,
27 // PathBuf,
28 // Nested container support
29 OptionBox,
30 OptionRc,
31 OptionArc,
32 BoxOption,
33 RcOption,
34 ArcOption,
35 VecOption,
36 OptionVec,
37 HashMapOption,
38 OptionHashMap,
39 // Arc with synchronization primitives (default)
40 StdArcMutex,
41 StdArcRwLock,
42 OptionStdArcMutex,
43 OptionStdArcRwLock,
44 // Synchronization primitives default
45 StdMutex,
46 StdRwLock,
47 OptionStdMutex,
48 OptionStdRwLock,
49 // Synchronization primitives (parking_lot)
50 Mutex,
51 RwLock,
52 OptionMutex,
53 OptionRwLock,
54 // Synchronization primitives (tokio::sync - requires tokio feature)
55 TokioMutex,
56 TokioRwLock,
57 // parking_lot
58 ArcMutex,
59 ArcRwLock,
60 OptionArcMutex,
61 OptionArcRwLock,
62 // Arc with synchronization primitives (tokio::sync - requires tokio feature)
63 TokioArcMutex,
64 TokioArcRwLock,
65 OptionTokioArcMutex,
66 OptionTokioArcRwLock,
67 // Tagged types
68 Tagged,
69 // Clone-on-write (std::borrow::Cow)
70 Cow,
71 OptionCow,
72}
73
74/// Helper function to check if a type path includes std::sync module
75fn is_std_sync_type(path: &syn::Path) -> bool {
76 // Check for paths like std::sync::Mutex, std::sync::RwLock
77 let segments: Vec<_> = path.segments.iter().map(|s| s.ident.to_string()).collect();
78 segments.len() >= 2
79 && segments.contains(&"std".to_string())
80 && segments.contains(&"sync".to_string())
81}
82
83/// Helper function to check if a type path includes tokio::sync module
84fn is_tokio_sync_type(path: &syn::Path) -> bool {
85 // Check for paths like tokio::sync::Mutex, tokio::sync::RwLock
86 let segments: Vec<_> = path.segments.iter().map(|s| s.ident.to_string()).collect();
87 segments.len() >= 2
88 && segments.contains(&"tokio".to_string())
89 && segments.contains(&"sync".to_string())
90}
91
92fn extract_wrapper_inner_type(ty: &Type) -> (WrapperKind, Option<Type>) {
93 use syn::{GenericArgument, PathArguments};
94
95 if let Type::Path(tp) = ty {
96 // Check if this is explicitly a std::sync type
97 let is_std_sync = is_std_sync_type(&tp.path);
98 // Check if this is explicitly a tokio::sync type
99 let is_tokio_sync = is_tokio_sync_type(&tp.path);
100
101 if let Some(seg) = tp.path.segments.last() {
102 let ident_str = seg.ident.to_string();
103
104 if let PathArguments::AngleBracketed(ab) = &seg.arguments {
105 let args: Vec<_> = ab.args.iter().collect();
106
107 // Handle map types (HashMap, BTreeMap) - they have K, V parameters
108 if ident_str == "HashMap" || ident_str == "BTreeMap" {
109 if let (Some(_key_arg), Some(value_arg)) = (args.get(0), args.get(1)) {
110 if let GenericArgument::Type(inner) = value_arg {
111 // Check for nested Option in map values
112 let (inner_kind, inner_inner) = extract_wrapper_inner_type(inner);
113 match (ident_str.as_str(), inner_kind) {
114 ("HashMap", WrapperKind::Option) => {
115 return (WrapperKind::HashMapOption, inner_inner);
116 }
117 _ => {
118 return match ident_str.as_str() {
119 "HashMap" => (WrapperKind::HashMap, Some(inner.clone())),
120 "BTreeMap" => (WrapperKind::BTreeMap, Some(inner.clone())),
121 _ => (WrapperKind::None, None),
122 };
123 }
124 }
125 }
126 }
127 }
128 // Handle Cow<'a, B> - has lifetime then type parameter
129 else if ident_str == "Cow" {
130 if let Some(inner) = args.iter().find_map(|arg| {
131 if let GenericArgument::Type(t) = arg {
132 Some(t.clone())
133 } else {
134 None
135 }
136 }) {
137 return (WrapperKind::Cow, Some(inner));
138 }
139 }
140 // Handle single-parameter container types
141 else if let Some(arg) = args.get(0) {
142 if let GenericArgument::Type(inner) = arg {
143 // Check for nested containers first
144 let (inner_kind, inner_inner) = extract_wrapper_inner_type(inner);
145
146 // Handle nested combinations
147 match (ident_str.as_str(), inner_kind) {
148 ("Option", WrapperKind::Box) => {
149 return (WrapperKind::OptionBox, inner_inner);
150 }
151 ("Option", WrapperKind::Rc) => {
152 return (WrapperKind::OptionRc, inner_inner);
153 }
154 ("Option", WrapperKind::Arc) => {
155 return (WrapperKind::OptionArc, inner_inner);
156 }
157 ("Option", WrapperKind::Vec) => {
158 return (WrapperKind::OptionVec, inner_inner);
159 }
160 ("Option", WrapperKind::HashMap) => {
161 return (WrapperKind::OptionHashMap, inner_inner);
162 }
163 ("Option", WrapperKind::StdArcMutex) => {
164 return (WrapperKind::OptionStdArcMutex, inner_inner);
165 }
166 ("Option", WrapperKind::StdArcRwLock) => {
167 return (WrapperKind::OptionStdArcRwLock, inner_inner);
168 }
169 ("Option", WrapperKind::ArcMutex) => {
170 return (WrapperKind::OptionArcMutex, inner_inner);
171 }
172 ("Option", WrapperKind::ArcRwLock) => {
173 return (WrapperKind::OptionArcRwLock, inner_inner);
174 }
175 ("Option", WrapperKind::StdMutex) => {
176 return (WrapperKind::OptionStdMutex, inner_inner);
177 }
178 ("Option", WrapperKind::StdRwLock) => {
179 return (WrapperKind::OptionStdRwLock, inner_inner);
180 }
181 ("Option", WrapperKind::Mutex) => {
182 return (WrapperKind::OptionMutex, inner_inner);
183 }
184 ("Option", WrapperKind::RwLock) => {
185 return (WrapperKind::OptionRwLock, inner_inner);
186 }
187 ("Option", WrapperKind::TokioArcMutex) => {
188 return (WrapperKind::OptionTokioArcMutex, inner_inner);
189 }
190 ("Option", WrapperKind::TokioArcRwLock) => {
191 return (WrapperKind::OptionTokioArcRwLock, inner_inner);
192 }
193 ("Option", WrapperKind::Cow) => {
194 return (WrapperKind::OptionCow, inner_inner);
195 }
196 ("Box", WrapperKind::Option) => {
197 return (WrapperKind::BoxOption, inner_inner);
198 }
199 ("Rc", WrapperKind::Option) => {
200 return (WrapperKind::RcOption, inner_inner);
201 }
202 ("Arc", WrapperKind::Option) => {
203 return (WrapperKind::ArcOption, inner_inner);
204 }
205 ("Vec", WrapperKind::Option) => {
206 return (WrapperKind::VecOption, inner_inner);
207 }
208 ("HashMap", WrapperKind::Option) => {
209 return (WrapperKind::HashMapOption, inner_inner);
210 }
211 // std::sync variants (when inner is StdMutex/StdRwLock)
212 ("Arc", WrapperKind::StdMutex) => {
213 return (WrapperKind::StdArcMutex, inner_inner);
214 }
215 ("Arc", WrapperKind::StdRwLock) => {
216 return (WrapperKind::StdArcRwLock, inner_inner);
217 }
218 // parking_lot variants (default - when inner is Mutex/RwLock without std::sync prefix)
219 ("Arc", WrapperKind::Mutex) => {
220 return (WrapperKind::ArcMutex, inner_inner);
221 }
222 ("Arc", WrapperKind::RwLock) => {
223 return (WrapperKind::ArcRwLock, inner_inner);
224 }
225 // tokio::sync variants (when inner is TokioMutex/TokioRwLock)
226 ("Arc", WrapperKind::TokioMutex) => {
227 return (WrapperKind::TokioArcMutex, inner_inner);
228 }
229 ("Arc", WrapperKind::TokioRwLock) => {
230 return (WrapperKind::TokioArcRwLock, inner_inner);
231 }
232 _ => {
233 // Handle single-level containers
234 // For Mutex and RwLock:
235 // - If path contains std::sync, it's std::sync (StdMutex/StdRwLock)
236 // - Otherwise, default to parking_lot (Mutex/RwLock)
237 return match ident_str.as_str() {
238 "Option" => (WrapperKind::Option, Some(inner.clone())),
239 "Box" => (WrapperKind::Box, Some(inner.clone())),
240 "Rc" => (WrapperKind::Rc, Some(inner.clone())),
241 "Arc" => (WrapperKind::Arc, Some(inner.clone())),
242 "Vec" => (WrapperKind::Vec, Some(inner.clone())),
243 "HashSet" => (WrapperKind::HashSet, Some(inner.clone())),
244 "BTreeSet" => (WrapperKind::BTreeSet, Some(inner.clone())),
245 "VecDeque" => (WrapperKind::VecDeque, Some(inner.clone())),
246 "LinkedList" => (WrapperKind::LinkedList, Some(inner.clone())),
247 "BinaryHeap" => (WrapperKind::BinaryHeap, Some(inner.clone())),
248 "Result" => (WrapperKind::Result, Some(inner.clone())),
249 // For std::sync::Mutex and std::sync::RwLock, use Std variants
250 "Mutex" if is_std_sync => {
251 (WrapperKind::StdMutex, Some(inner.clone()))
252 }
253 "RwLock" if is_std_sync => {
254 (WrapperKind::StdRwLock, Some(inner.clone()))
255 }
256 // For tokio::sync::Mutex and tokio::sync::RwLock, use Tokio variants
257 "Mutex" if is_tokio_sync => {
258 (WrapperKind::TokioMutex, Some(inner.clone()))
259 }
260 "RwLock" if is_tokio_sync => {
261 (WrapperKind::TokioRwLock, Some(inner.clone()))
262 }
263 // Default: parking_lot (no std::sync or tokio::sync prefix)
264 "Mutex" => (WrapperKind::Mutex, Some(inner.clone())),
265 "RwLock" => (WrapperKind::RwLock, Some(inner.clone())),
266 "Weak" => (WrapperKind::Weak, Some(inner.clone())),
267 "Tagged" => (WrapperKind::Tagged, Some(inner.clone())),
268 "Cow" => (WrapperKind::Cow, Some(inner.clone())),
269 _ => (WrapperKind::None, None),
270 };
271 }
272 }
273 }
274 }
275 }
276 }
277 }
278 (WrapperKind::None, None)
279}
280
281/// For HashMap<K,V> or BTreeMap<K,V>, returns Some((key_ty, value_ty)).
282fn extract_map_key_value(ty: &Type) -> Option<(Type, Type)> {
283 use syn::{GenericArgument, PathArguments};
284
285 if let Type::Path(tp) = ty {
286 if let Some(seg) = tp.path.segments.last() {
287 let ident_str = seg.ident.to_string();
288 if ident_str == "HashMap" || ident_str == "BTreeMap" {
289 if let PathArguments::AngleBracketed(ab) = &seg.arguments {
290 let args: Vec<_> = ab.args.iter().collect();
291 if let (Some(key_arg), Some(value_arg)) = (args.get(0), args.get(1)) {
292 if let (GenericArgument::Type(key_ty), GenericArgument::Type(value_ty)) =
293 (key_arg, value_arg)
294 {
295 return Some((key_ty.clone(), value_ty.clone()));
296 }
297 }
298 }
299 }
300 }
301 }
302 None
303}
304
305fn to_snake_case(name: &str) -> String {
306 let mut out = String::new();
307 for (i, c) in name.chars().enumerate() {
308 if c.is_uppercase() {
309 if i != 0 {
310 out.push('_');
311 }
312 out.push(c.to_ascii_lowercase());
313 } else {
314 out.push(c);
315 }
316 }
317 out
318}
319
320/// Derive macro for generating simple keypath methods.
321///
322/// Generates one method per field: `StructName::field_name()` that returns a `Kp`.
323/// Intelligently handles wrapper types (Option, Vec, Box, Arc, etc.) to generate appropriate keypaths.
324///
325/// # Example
326///
327/// ```ignore
328/// #[derive(Kp)]
329/// struct Person {
330/// name: String,
331/// age: i32,
332/// email: Option<String>,
333/// addresses: Vec<String>,
334/// }
335///
336/// // Generates:
337/// // impl Person {
338/// // pub fn name() -> Kp<...> { ... }
339/// // pub fn age() -> Kp<...> { ... }
340/// // pub fn email() -> Kp<...> { ... } // unwraps Option
341/// // pub fn addresses() -> Kp<...> { ... } // accesses first element
342/// // }
343/// ```
344#[proc_macro_derive(Kp)]
345pub fn derive_keypaths(input: TokenStream) -> TokenStream {
346 let input = parse_macro_input!(input as DeriveInput);
347 let name = &input.ident;
348 let input_span = input.span();
349
350 let methods = match input.data {
351 Data::Struct(data_struct) => match data_struct.fields {
352 Fields::Named(fields_named) => {
353 let mut tokens = proc_macro2::TokenStream::new();
354
355 // Generate identity methods for the struct
356 tokens.extend(quote! {
357 /// Returns a generic identity keypath for this type
358 #[inline]
359 pub fn identity_typed<Root, MutRoot>() -> rust_key_paths::Kp<
360 #name,
361 #name,
362 Root,
363 Root,
364 MutRoot,
365 MutRoot,
366 fn(Root) -> Option<Root>,
367 fn(MutRoot) -> Option<MutRoot>,
368 >
369 where
370 Root: std::borrow::Borrow<#name>,
371 MutRoot: std::borrow::BorrowMut<#name>,
372 {
373 rust_key_paths::Kp::new(
374 |r: Root| Some(r),
375 |r: MutRoot| Some(r)
376 )
377 }
378
379 /// Returns a simple identity keypath for this type
380 #[inline]
381 pub fn identity() -> rust_key_paths::KpType<'static, #name, #name> {
382 rust_key_paths::Kp::new(
383 |r: &#name| Some(r),
384 |r: &mut #name| Some(r)
385 )
386 }
387 });
388
389 for field in fields_named.named.iter() {
390 let field_ident = field.ident.as_ref().unwrap();
391 let ty = &field.ty;
392 // Centralized keypath method names – change here to adjust for all types
393 let kp_fn = format_ident!("{}", field_ident);
394 let kp_at_fn = format_ident!("{}_at", field_ident);
395
396 let (kind, inner_ty) = extract_wrapper_inner_type(ty);
397
398 match (kind, inner_ty.clone()) {
399 (WrapperKind::Option, Some(inner_ty)) => {
400 // For Option<T>, unwrap and access inner type
401 tokens.extend(quote! {
402 #[inline]
403 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
404 rust_key_paths::Kp::new(
405 |root: &#name| root.#field_ident.as_ref(),
406 |root: &mut #name| root.#field_ident.as_mut(),
407 )
408 }
409 });
410 }
411 (WrapperKind::Vec, Some(inner_ty)) => {
412 tokens.extend(quote! {
413 #[inline]
414 #[inline]
415 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
416 rust_key_paths::Kp::new(
417 |root: &#name| Some(&root.#field_ident),
418 |root: &mut #name| Some(&mut root.#field_ident),
419 )
420 }
421 #[inline]
422 pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
423 rust_key_paths::Kp::new(
424 Box::new(move |root: &#name| root.#field_ident.get(index)),
425 Box::new(move |root: &mut #name| root.#field_ident.get_mut(index)),
426 )
427 }
428 });
429 }
430 (WrapperKind::HashMap, Some(inner_ty)) => {
431 if let Some((key_ty, _)) = extract_map_key_value(ty) {
432 tokens.extend(quote! {
433 #[inline]
434 #[inline]
435 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
436 rust_key_paths::Kp::new(
437 |root: &#name| Some(&root.#field_ident),
438 |root: &mut #name| Some(&mut root.#field_ident),
439 )
440 }
441 #[inline]
442 pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
443 where
444 #key_ty: Clone + std::hash::Hash + Eq + 'static,
445 {
446 let key2 = key.clone();
447 rust_key_paths::Kp::new(
448 Box::new(move |root: &#name| root.#field_ident.get(&key)),
449 Box::new(move |root: &mut #name| root.#field_ident.get_mut(&key2)),
450 )
451 }
452 });
453 } else {
454 tokens.extend(quote! {
455 #[inline]
456 #[inline]
457 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
458 rust_key_paths::Kp::new(
459 |root: &#name| Some(&root.#field_ident),
460 |root: &mut #name| Some(&mut root.#field_ident),
461 )
462 }
463 });
464 }
465 }
466 (WrapperKind::BTreeMap, Some(inner_ty)) => {
467 if let Some((key_ty, _)) = extract_map_key_value(ty) {
468 tokens.extend(quote! {
469 #[inline]
470 #[inline]
471 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
472 rust_key_paths::Kp::new(
473 |root: &#name| Some(&root.#field_ident),
474 |root: &mut #name| Some(&mut root.#field_ident),
475 )
476 }
477 #[inline]
478 pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
479 where
480 #key_ty: Clone + Ord + 'static,
481 {
482 let key2 = key.clone();
483 rust_key_paths::Kp::new(
484 Box::new(move |root: &#name| root.#field_ident.get(&key)),
485 Box::new(move |root: &mut #name| root.#field_ident.get_mut(&key2)),
486 )
487 }
488 });
489 } else {
490 tokens.extend(quote! {
491 #[inline]
492 #[inline]
493 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
494 rust_key_paths::Kp::new(
495 |root: &#name| Some(&root.#field_ident),
496 |root: &mut #name| Some(&mut root.#field_ident),
497 )
498 }
499 });
500 }
501 }
502 (WrapperKind::Box, Some(inner_ty)) => {
503 // For Box<T>, deref to inner type (returns &T / &mut T, not &Box<T>)
504 // Matches reference: WritableKeyPath::new(|s: &mut #name| &mut *s.#field_ident)
505 tokens.extend(quote! {
506 #[inline]
507 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
508 rust_key_paths::Kp::new(
509 |root: &#name| Some(&*root.#field_ident),
510 |root: &mut #name| Some(&mut *root.#field_ident),
511 )
512 }
513 });
514 }
515 (WrapperKind::Rc, Some(inner_ty)) => {
516 // For Rc<T>, deref to inner type (returns &T; get_mut when uniquely owned)
517 tokens.extend(quote! {
518 #[inline]
519 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
520 rust_key_paths::Kp::new(
521 |root: &#name| Some(root.#field_ident.as_ref()),
522 |root: &mut #name| std::rc::Rc::get_mut(&mut root.#field_ident),
523 )
524 }
525 });
526 }
527 (WrapperKind::Arc, Some(inner_ty)) => {
528 // For Arc<T>, deref to inner type (returns &T; get_mut when uniquely owned)
529 tokens.extend(quote! {
530 #[inline]
531 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
532 rust_key_paths::Kp::new(
533 |root: &#name| Some(root.#field_ident.as_ref()),
534 |root: &mut #name| std::sync::Arc::get_mut(&mut root.#field_ident),
535 )
536 }
537 });
538 }
539 (WrapperKind::Cow, Some(inner_ty)) => {
540 // For Cow<'_, B>, deref to inner type (as_ref/to_mut)
541 tokens.extend(quote! {
542 #[inline]
543 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
544 rust_key_paths::Kp::new(
545 |root: &#name| Some(root.#field_ident.as_ref()),
546 |root: &mut #name| Some(root.#field_ident.to_mut()),
547 )
548 }
549 });
550 }
551
552 (WrapperKind::OptionCow, Some(inner_ty)) => {
553 // For Option<Cow<'_, B>>
554 tokens.extend(quote! {
555 #[inline]
556 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
557 rust_key_paths::Kp::new(
558 |root: &#name| root.#field_ident.as_ref().map(|c| c.as_ref()),
559 |root: &mut #name| root.#field_ident.as_mut().map(|c| c.to_mut()),
560 )
561 }
562 });
563 }
564 (WrapperKind::HashSet, Some(_inner_ty)) => {
565 tokens.extend(quote! {
566 #[inline]
567 #[inline]
568 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
569 rust_key_paths::Kp::new(
570 |root: &#name| Some(&root.#field_ident),
571 |root: &mut #name| Some(&mut root.#field_ident),
572 )
573 }
574 });
575 }
576 (WrapperKind::BTreeSet, Some(_inner_ty)) => {
577 tokens.extend(quote! {
578 #[inline]
579 #[inline]
580 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
581 rust_key_paths::Kp::new(
582 |root: &#name| Some(&root.#field_ident),
583 |root: &mut #name| Some(&mut root.#field_ident),
584 )
585 }
586 });
587 }
588 (WrapperKind::VecDeque, Some(inner_ty)) => {
589 tokens.extend(quote! {
590 #[inline]
591 #[inline]
592 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
593 rust_key_paths::Kp::new(
594 |root: &#name| Some(&root.#field_ident),
595 |root: &mut #name| Some(&mut root.#field_ident),
596 )
597 }
598 #[inline]
599 pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
600 rust_key_paths::Kp::new(
601 Box::new(move |root: &#name| root.#field_ident.get(index)),
602 Box::new(move |root: &mut #name| root.#field_ident.get_mut(index)),
603 )
604 }
605 });
606 }
607 (WrapperKind::LinkedList, Some(_inner_ty)) => {
608 tokens.extend(quote! {
609 #[inline]
610 #[inline]
611 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
612 rust_key_paths::Kp::new(
613 |root: &#name| Some(&root.#field_ident),
614 |root: &mut #name| Some(&mut root.#field_ident),
615 )
616 }
617 });
618 }
619 (WrapperKind::BinaryHeap, Some(_inner_ty)) => {
620 tokens.extend(quote! {
621 #[inline]
622 #[inline]
623 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
624 rust_key_paths::Kp::new(
625 |root: &#name| Some(&root.#field_ident),
626 |root: &mut #name| Some(&mut root.#field_ident),
627 )
628 }
629 });
630 }
631 (WrapperKind::Result, Some(inner_ty)) => {
632 // For Result<T, E>, access Ok value
633 tokens.extend(quote! {
634 #[inline]
635 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
636 rust_key_paths::Kp::new(
637 |root: &#name| root.#field_ident.as_ref().ok(),
638 |root: &mut #name| root.#field_ident.as_mut().ok(),
639 )
640 }
641 });
642 }
643 (WrapperKind::StdArcMutex, Some(inner_ty)) => {
644 // For Arc<std::sync::Mutex<T>>
645 let kp_lock_fn = format_ident!("{}_lock", field_ident);
646 tokens.extend(quote! {
647 #[inline]
648 #[inline]
649 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
650 rust_key_paths::Kp::new(
651 |root: &#name| Some(&root.#field_ident),
652 |root: &mut #name| Some(&mut root.#field_ident),
653 )
654 }
655 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcMutexFor<#name, #ty, #inner_ty> {
656 rust_key_paths::lock::LockKp::new(
657 rust_key_paths::Kp::new(
658 |root: &#name| Some(&root.#field_ident),
659 |root: &mut #name| Some(&mut root.#field_ident),
660 ),
661 rust_key_paths::lock::ArcMutexAccess::new(),
662 rust_key_paths::Kp::new(
663 |v: &#inner_ty| Some(v),
664 |v: &mut #inner_ty| Some(v),
665 ),
666 )
667 }
668 });
669 }
670 (WrapperKind::StdArcRwLock, Some(inner_ty)) => {
671 // For Arc<std::sync::RwLock<T>>
672 let kp_lock_fn = format_ident!("{}_lock", field_ident);
673 tokens.extend(quote! {
674 #[inline]
675 #[inline]
676 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
677 rust_key_paths::Kp::new(
678 |root: &#name| Some(&root.#field_ident),
679 |root: &mut #name| Some(&mut root.#field_ident),
680 )
681 }
682 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcRwLockFor<#name, #ty, #inner_ty> {
683 rust_key_paths::lock::LockKp::new(
684 rust_key_paths::Kp::new(
685 |root: &#name| Some(&root.#field_ident),
686 |root: &mut #name| Some(&mut root.#field_ident),
687 ),
688 rust_key_paths::lock::ArcRwLockAccess::new(),
689 rust_key_paths::Kp::new(
690 |v: &#inner_ty| Some(v),
691 |v: &mut #inner_ty| Some(v),
692 ),
693 )
694 }
695 });
696 }
697 (WrapperKind::ArcRwLock, Some(inner_ty)) => {
698 // For Arc<parking_lot::RwLock<T>> (requires rust-key-paths "parking_lot" feature)
699 let kp_lock_fn = format_ident!("{}_lock", field_ident);
700 tokens.extend(quote! {
701 #[inline]
702 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
703 rust_key_paths::Kp::new(
704 |root: &#name| Some(&root.#field_ident),
705 |root: &mut #name| Some(&mut root.#field_ident),
706 )
707 }
708 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpParkingLotRwLockFor<#name, #ty, #inner_ty> {
709 rust_key_paths::lock::LockKp::new(
710 rust_key_paths::Kp::new(
711 |root: &#name| Some(&root.#field_ident),
712 |root: &mut #name| Some(&mut root.#field_ident),
713 ),
714 rust_key_paths::lock::ParkingLotRwLockAccess::new(),
715 rust_key_paths::Kp::new(
716 |v: &#inner_ty| Some(v),
717 |v: &mut #inner_ty| Some(v),
718 ),
719 )
720 }
721 });
722 }
723 (WrapperKind::ArcMutex, Some(inner_ty)) => {
724 // For Arc<parking_lot::Mutex<T>> (requires rust-key-paths "parking_lot" feature)
725 let kp_lock_fn = format_ident!("{}_lock", field_ident);
726 tokens.extend(quote! {
727 #[inline]
728 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
729 rust_key_paths::Kp::new(
730 |root: &#name| Some(&root.#field_ident),
731 |root: &mut #name| Some(&mut root.#field_ident),
732 )
733 }
734 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpParkingLotMutexFor<#name, #ty, #inner_ty> {
735 rust_key_paths::lock::LockKp::new(
736 rust_key_paths::Kp::new(
737 |root: &#name| Some(&root.#field_ident),
738 |root: &mut #name| Some(&mut root.#field_ident),
739 ),
740 rust_key_paths::lock::ParkingLotMutexAccess::new(),
741 rust_key_paths::Kp::new(
742 |v: &#inner_ty| Some(v),
743 |v: &mut #inner_ty| Some(v),
744 ),
745 )
746 }
747 });
748 }
749 (WrapperKind::Mutex, Some(_inner_ty))
750 | (WrapperKind::StdMutex, Some(_inner_ty)) => {
751 // For Mutex<T>, return keypath to container
752 tokens.extend(quote! {
753 #[inline]
754 #[inline]
755 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
756 rust_key_paths::Kp::new(
757 |root: &#name| Some(&root.#field_ident),
758 |root: &mut #name| Some(&mut root.#field_ident),
759 )
760 }
761 });
762 }
763 (WrapperKind::RwLock, Some(_inner_ty))
764 | (WrapperKind::StdRwLock, Some(_inner_ty)) => {
765 // For RwLock<T>, return keypath to container
766 tokens.extend(quote! {
767 #[inline]
768 #[inline]
769 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
770 rust_key_paths::Kp::new(
771 |root: &#name| Some(&root.#field_ident),
772 |root: &mut #name| Some(&mut root.#field_ident),
773 )
774 }
775 });
776 }
777 (WrapperKind::TokioArcMutex, Some(inner_ty)) => {
778 let kp_async_fn = format_ident!("{}_async", field_ident);
779 tokens.extend(quote! {
780 #[inline]
781 #[inline]
782 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
783 rust_key_paths::Kp::new(
784 |root: &#name| Some(&root.#field_ident),
785 |root: &mut #name| Some(&mut root.#field_ident),
786 )
787 }
788 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, #ty, #inner_ty> {
789 rust_key_paths::async_lock::AsyncLockKp::new(
790 rust_key_paths::Kp::new(
791 |root: &#name| Some(&root.#field_ident),
792 |root: &mut #name| Some(&mut root.#field_ident),
793 ),
794 rust_key_paths::async_lock::TokioMutexAccess::new(),
795 rust_key_paths::Kp::new(
796 |v: &#inner_ty| Some(v),
797 |v: &mut #inner_ty| Some(v),
798 ),
799 )
800 }
801 });
802 }
803 (WrapperKind::TokioArcRwLock, Some(inner_ty)) => {
804 let kp_async_fn = format_ident!("{}_async", field_ident);
805 tokens.extend(quote! {
806 #[inline]
807 #[inline]
808 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
809 rust_key_paths::Kp::new(
810 |root: &#name| Some(&root.#field_ident),
811 |root: &mut #name| Some(&mut root.#field_ident),
812 )
813 }
814 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, #ty, #inner_ty> {
815 rust_key_paths::async_lock::AsyncLockKp::new(
816 rust_key_paths::Kp::new(
817 |root: &#name| Some(&root.#field_ident),
818 |root: &mut #name| Some(&mut root.#field_ident),
819 ),
820 rust_key_paths::async_lock::TokioRwLockAccess::new(),
821 rust_key_paths::Kp::new(
822 |v: &#inner_ty| Some(v),
823 |v: &mut #inner_ty| Some(v),
824 ),
825 )
826 }
827 });
828 }
829 (WrapperKind::OptionTokioArcMutex, Some(inner_ty)) => {
830 let kp_async_fn = format_ident!("{}_async", field_ident);
831 tokens.extend(quote! {
832 #[inline]
833 #[inline]
834 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
835 rust_key_paths::Kp::new(
836 |root: &#name| Some(&root.#field_ident),
837 |root: &mut #name| Some(&mut root.#field_ident),
838 )
839 }
840 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, std::sync::Arc<tokio::sync::Mutex<#inner_ty>>, #inner_ty> {
841 rust_key_paths::async_lock::AsyncLockKp::new(
842 rust_key_paths::Kp::new(
843 |root: &#name| root.#field_ident.as_ref(),
844 |root: &mut #name| root.#field_ident.as_mut(),
845 ),
846 rust_key_paths::async_lock::TokioMutexAccess::new(),
847 rust_key_paths::Kp::new(
848 |v: &#inner_ty| Some(v),
849 |v: &mut #inner_ty| Some(v),
850 ),
851 )
852 }
853 });
854 }
855 (WrapperKind::OptionTokioArcRwLock, Some(inner_ty)) => {
856 let kp_async_fn = format_ident!("{}_async", field_ident);
857 tokens.extend(quote! {
858 #[inline]
859 #[inline]
860 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
861 rust_key_paths::Kp::new(
862 |root: &#name| Some(&root.#field_ident),
863 |root: &mut #name| Some(&mut root.#field_ident),
864 )
865 }
866 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, std::sync::Arc<tokio::sync::RwLock<#inner_ty>>, #inner_ty> {
867 rust_key_paths::async_lock::AsyncLockKp::new(
868 rust_key_paths::Kp::new(
869 |root: &#name| root.#field_ident.as_ref(),
870 |root: &mut #name| root.#field_ident.as_mut(),
871 ),
872 rust_key_paths::async_lock::TokioRwLockAccess::new(),
873 rust_key_paths::Kp::new(
874 |v: &#inner_ty| Some(v),
875 |v: &mut #inner_ty| Some(v),
876 ),
877 )
878 }
879 });
880 }
881 (WrapperKind::OptionStdArcMutex, Some(inner_ty))
882 | (WrapperKind::OptionArcMutex, Some(inner_ty)) => {
883 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
884 let kp_lock_fn = format_ident!("{}_lock", field_ident);
885 tokens.extend(quote! {
886 #[inline]
887 #[inline]
888 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
889 rust_key_paths::Kp::new(
890 |root: &#name| Some(&root.#field_ident),
891 |root: &mut #name| Some(&mut root.#field_ident),
892 )
893 }
894 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::Mutex<#inner_ty>>> {
895 rust_key_paths::Kp::new(
896 |root: &#name| root.#field_ident.as_ref(),
897 |root: &mut #name| root.#field_ident.as_mut(),
898 )
899 }
900 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcMutexFor<#name, std::sync::Arc<std::sync::Mutex<#inner_ty>>, #inner_ty> {
901 rust_key_paths::lock::LockKp::new(
902 rust_key_paths::Kp::new(
903 |root: &#name| root.#field_ident.as_ref(),
904 |root: &mut #name| root.#field_ident.as_mut(),
905 ),
906 rust_key_paths::lock::ArcMutexAccess::new(),
907 rust_key_paths::Kp::new(
908 |v: &#inner_ty| Some(v),
909 |v: &mut #inner_ty| Some(v),
910 ),
911 )
912 }
913 });
914 }
915 (WrapperKind::OptionStdArcRwLock, Some(inner_ty))
916 | (WrapperKind::OptionArcRwLock, Some(inner_ty)) => {
917 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
918 let kp_lock_fn = format_ident!("{}_lock", field_ident);
919 tokens.extend(quote! {
920 #[inline]
921 #[inline]
922 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
923 rust_key_paths::Kp::new(
924 |root: &#name| Some(&root.#field_ident),
925 |root: &mut #name| Some(&mut root.#field_ident),
926 )
927 }
928 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::RwLock<#inner_ty>>> {
929 rust_key_paths::Kp::new(
930 |root: &#name| root.#field_ident.as_ref(),
931 |root: &mut #name| root.#field_ident.as_mut(),
932 )
933 }
934 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcRwLockFor<#name, std::sync::Arc<std::sync::RwLock<#inner_ty>>, #inner_ty> {
935 rust_key_paths::lock::LockKp::new(
936 rust_key_paths::Kp::new(
937 |root: &#name| root.#field_ident.as_ref(),
938 |root: &mut #name| root.#field_ident.as_mut(),
939 ),
940 rust_key_paths::lock::ArcRwLockAccess::new(),
941 rust_key_paths::Kp::new(
942 |v: &#inner_ty| Some(v),
943 |v: &mut #inner_ty| Some(v),
944 ),
945 )
946 }
947 });
948 }
949 (WrapperKind::OptionStdMutex, Some(inner_ty))
950 | (WrapperKind::OptionMutex, Some(inner_ty)) => {
951 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
952 tokens.extend(quote! {
953 #[inline]
954 #[inline]
955 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
956 rust_key_paths::Kp::new(
957 |root: &#name| Some(&root.#field_ident),
958 |root: &mut #name| Some(&mut root.#field_ident),
959 )
960 }
961 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Mutex<#inner_ty>> {
962 rust_key_paths::Kp::new(
963 |root: &#name| root.#field_ident.as_ref(),
964 |root: &mut #name| root.#field_ident.as_mut(),
965 )
966 }
967 });
968 }
969 (WrapperKind::OptionStdRwLock, Some(inner_ty))
970 | (WrapperKind::OptionRwLock, Some(inner_ty)) => {
971 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
972 tokens.extend(quote! {
973 #[inline]
974 #[inline]
975 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
976 rust_key_paths::Kp::new(
977 |root: &#name| Some(&root.#field_ident),
978 |root: &mut #name| Some(&mut root.#field_ident),
979 )
980 }
981 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::RwLock<#inner_ty>> {
982 rust_key_paths::Kp::new(
983 |root: &#name| root.#field_ident.as_ref(),
984 |root: &mut #name| root.#field_ident.as_mut(),
985 )
986 }
987 });
988 }
989 (WrapperKind::Weak, Some(_inner_ty)) => {
990 // For Weak<T>, return keypath to container
991 tokens.extend(quote! {
992 #[inline]
993 #[inline]
994 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
995 rust_key_paths::Kp::new(
996 |root: &#name| Some(&root.#field_ident),
997 |_root: &mut #name| None, // Weak doesn't support mutable access
998 )
999 }
1000 });
1001 }
1002 (WrapperKind::None, None) => {
1003 // For basic types, direct access
1004 tokens.extend(quote! {
1005 #[inline]
1006 #[inline]
1007 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1008 rust_key_paths::Kp::new(
1009 |root: &#name| Some(&root.#field_ident),
1010 |root: &mut #name| Some(&mut root.#field_ident),
1011 )
1012 }
1013 });
1014 }
1015 _ => {
1016 // For unknown/complex nested types, return keypath to field itself
1017 tokens.extend(quote! {
1018 #[inline]
1019 #[inline]
1020 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1021 rust_key_paths::Kp::new(
1022 |root: &#name| Some(&root.#field_ident),
1023 |root: &mut #name| Some(&mut root.#field_ident),
1024 )
1025 }
1026 });
1027 }
1028 }
1029 }
1030
1031 tokens
1032 }
1033 Fields::Unnamed(unnamed) => {
1034 let mut tokens = proc_macro2::TokenStream::new();
1035
1036 // Generate identity methods for the tuple struct
1037 tokens.extend(quote! {
1038 /// Returns a generic identity keypath for this type
1039 #[inline]
1040 pub fn identity_typed<Root, MutRoot>() -> rust_key_paths::Kp<
1041 #name,
1042 #name,
1043 Root,
1044 Root,
1045 MutRoot,
1046 MutRoot,
1047 fn(Root) -> Option<Root>,
1048 fn(MutRoot) -> Option<MutRoot>,
1049 >
1050 where
1051 Root: std::borrow::Borrow<#name>,
1052 MutRoot: std::borrow::BorrowMut<#name>,
1053 {
1054 rust_key_paths::Kp::new(
1055 |r: Root| Some(r),
1056 |r: MutRoot| Some(r)
1057 )
1058 }
1059
1060 /// Returns a simple identity keypath for this type
1061 #[inline]
1062 pub fn identity() -> rust_key_paths::KpType<'static, #name, #name> {
1063 rust_key_paths::Kp::new(
1064 |r: &#name| Some(r),
1065 |r: &mut #name| Some(r)
1066 )
1067 }
1068 });
1069
1070 for (idx, field) in unnamed.unnamed.iter().enumerate() {
1071 let idx_lit = syn::Index::from(idx);
1072 let ty = &field.ty;
1073 // Centralized keypath method names for tuple fields – change here to adjust for all types
1074 let kp_fn = format_ident!("f{}", idx);
1075 let kp_at_fn = format_ident!("f{}_at", idx);
1076
1077 let (kind, inner_ty) = extract_wrapper_inner_type(ty);
1078
1079 match (kind, inner_ty.clone()) {
1080 (WrapperKind::Option, Some(inner_ty)) => {
1081 tokens.extend(quote! {
1082 #[inline]
1083 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1084 rust_key_paths::Kp::new(
1085 |root: &#name| root.#idx_lit.as_ref(),
1086 |root: &mut #name| root.#idx_lit.as_mut(),
1087 )
1088 }
1089 });
1090 }
1091 (WrapperKind::Vec, Some(inner_ty)) => {
1092 tokens.extend(quote! {
1093 #[inline]
1094 #[inline]
1095 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1096 rust_key_paths::Kp::new(
1097 |root: &#name| Some(&root.#idx_lit),
1098 |root: &mut #name| Some(&mut root.#idx_lit),
1099 )
1100 }
1101 #[inline]
1102 pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
1103 rust_key_paths::Kp::new(
1104 Box::new(move |root: &#name| root.#idx_lit.get(index)),
1105 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(index)),
1106 )
1107 }
1108 });
1109 }
1110 (WrapperKind::HashMap, Some(inner_ty)) => {
1111 if let Some((key_ty, _)) = extract_map_key_value(ty) {
1112 tokens.extend(quote! {
1113 #[inline]
1114 #[inline]
1115 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1116 rust_key_paths::Kp::new(
1117 |root: &#name| Some(&root.#idx_lit),
1118 |root: &mut #name| Some(&mut root.#idx_lit),
1119 )
1120 }
1121 #[inline]
1122 pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
1123 where
1124 #key_ty: Clone + std::hash::Hash + Eq + 'static,
1125 {
1126 let key2 = key.clone();
1127 rust_key_paths::Kp::new(
1128 Box::new(move |root: &#name| root.#idx_lit.get(&key)),
1129 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(&key2)),
1130 )
1131 }
1132 });
1133 } else {
1134 tokens.extend(quote! {
1135 #[inline]
1136 #[inline]
1137 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1138 rust_key_paths::Kp::new(
1139 |root: &#name| Some(&root.#idx_lit),
1140 |root: &mut #name| Some(&mut root.#idx_lit),
1141 )
1142 }
1143 });
1144 }
1145 }
1146 (WrapperKind::BTreeMap, Some(inner_ty)) => {
1147 if let Some((key_ty, _)) = extract_map_key_value(ty) {
1148 tokens.extend(quote! {
1149 #[inline]
1150 #[inline]
1151 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1152 rust_key_paths::Kp::new(
1153 |root: &#name| Some(&root.#idx_lit),
1154 |root: &mut #name| Some(&mut root.#idx_lit),
1155 )
1156 }
1157 #[inline]
1158 pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
1159 where
1160 #key_ty: Clone + Ord + 'static,
1161 {
1162 let key2 = key.clone();
1163 rust_key_paths::Kp::new(
1164 Box::new(move |root: &#name| root.#idx_lit.get(&key)),
1165 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(&key2)),
1166 )
1167 }
1168 });
1169 } else {
1170 tokens.extend(quote! {
1171 #[inline]
1172 #[inline]
1173 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1174 rust_key_paths::Kp::new(
1175 |root: &#name| Some(&root.#idx_lit),
1176 |root: &mut #name| Some(&mut root.#idx_lit),
1177 )
1178 }
1179 });
1180 }
1181 }
1182 (WrapperKind::Box, Some(inner_ty)) => {
1183 // Box: deref to inner (returns &T / &mut T)
1184 tokens.extend(quote! {
1185 #[inline]
1186 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1187 rust_key_paths::Kp::new(
1188 |root: &#name| Some(&*root.#idx_lit),
1189 |root: &mut #name| Some(&mut *root.#idx_lit),
1190 )
1191 }
1192 });
1193 }
1194 (WrapperKind::Rc, Some(inner_ty)) => {
1195 tokens.extend(quote! {
1196 #[inline]
1197 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1198 rust_key_paths::Kp::new(
1199 |root: &#name| Some(root.#idx_lit.as_ref()),
1200 |root: &mut #name| std::rc::Rc::get_mut(&mut root.#idx_lit),
1201 )
1202 }
1203 });
1204 }
1205 (WrapperKind::Arc, Some(inner_ty)) => {
1206 tokens.extend(quote! {
1207 #[inline]
1208 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1209 rust_key_paths::Kp::new(
1210 |root: &#name| Some(root.#idx_lit.as_ref()),
1211 |root: &mut #name| std::sync::Arc::get_mut(&mut root.#idx_lit),
1212 )
1213 }
1214 });
1215 }
1216
1217 (WrapperKind::Cow, Some(inner_ty)) => {
1218 tokens.extend(quote! {
1219 #[inline]
1220 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1221 rust_key_paths::Kp::new(
1222 |root: &#name| Some(root.#idx_lit.as_ref()),
1223 |root: &mut #name| Some(root.#idx_lit.to_mut()),
1224 )
1225 }
1226 });
1227 }
1228
1229 (WrapperKind::OptionCow, Some(inner_ty)) => {
1230 tokens.extend(quote! {
1231 #[inline]
1232 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1233 rust_key_paths::Kp::new(
1234 |root: &#name| root.#idx_lit.as_ref().map(|c| c.as_ref()),
1235 |root: &mut #name| root.#idx_lit.as_mut().map(|c| c.to_mut()),
1236 )
1237 }
1238 });
1239 }
1240 (WrapperKind::HashSet, Some(_inner_ty)) => {
1241 tokens.extend(quote! {
1242 #[inline]
1243 #[inline]
1244 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1245 rust_key_paths::Kp::new(
1246 |root: &#name| Some(&root.#idx_lit),
1247 |root: &mut #name| Some(&mut root.#idx_lit),
1248 )
1249 }
1250 });
1251 }
1252 (WrapperKind::BTreeSet, Some(_inner_ty)) => {
1253 tokens.extend(quote! {
1254 #[inline]
1255 #[inline]
1256 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1257 rust_key_paths::Kp::new(
1258 |root: &#name| Some(&root.#idx_lit),
1259 |root: &mut #name| Some(&mut root.#idx_lit),
1260 )
1261 }
1262 });
1263 }
1264 (WrapperKind::VecDeque, Some(inner_ty)) => {
1265 tokens.extend(quote! {
1266 #[inline]
1267 #[inline]
1268 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1269 rust_key_paths::Kp::new(
1270 |root: &#name| Some(&root.#idx_lit),
1271 |root: &mut #name| Some(&mut root.#idx_lit),
1272 )
1273 }
1274 #[inline]
1275 pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
1276 rust_key_paths::Kp::new(
1277 Box::new(move |root: &#name| root.#idx_lit.get(index)),
1278 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(index)),
1279 )
1280 }
1281 });
1282 }
1283 (WrapperKind::LinkedList, Some(_inner_ty)) => {
1284 tokens.extend(quote! {
1285 #[inline]
1286 #[inline]
1287 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1288 rust_key_paths::Kp::new(
1289 |root: &#name| Some(&root.#idx_lit),
1290 |root: &mut #name| Some(&mut root.#idx_lit),
1291 )
1292 }
1293 });
1294 }
1295 (WrapperKind::BinaryHeap, Some(_inner_ty)) => {
1296 tokens.extend(quote! {
1297 #[inline]
1298 #[inline]
1299 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1300 rust_key_paths::Kp::new(
1301 |root: &#name| Some(&root.#idx_lit),
1302 |root: &mut #name| Some(&mut root.#idx_lit),
1303 )
1304 }
1305 });
1306 }
1307 (WrapperKind::Result, Some(inner_ty)) => {
1308 tokens.extend(quote! {
1309 #[inline]
1310 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1311 rust_key_paths::Kp::new(
1312 |root: &#name| root.#idx_lit.as_ref().ok(),
1313 |root: &mut #name| root.#idx_lit.as_mut().ok(),
1314 )
1315 }
1316 });
1317 }
1318 (WrapperKind::Mutex, Some(_inner_ty))
1319 | (WrapperKind::StdMutex, Some(_inner_ty)) => {
1320 tokens.extend(quote! {
1321 #[inline]
1322 #[inline]
1323 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1324 rust_key_paths::Kp::new(
1325 |root: &#name| Some(&root.#idx_lit),
1326 |root: &mut #name| Some(&mut root.#idx_lit),
1327 )
1328 }
1329 });
1330 }
1331 (WrapperKind::RwLock, Some(_inner_ty))
1332 | (WrapperKind::StdRwLock, Some(_inner_ty)) => {
1333 tokens.extend(quote! {
1334 #[inline]
1335 #[inline]
1336 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1337 rust_key_paths::Kp::new(
1338 |root: &#name| Some(&root.#idx_lit),
1339 |root: &mut #name| Some(&mut root.#idx_lit),
1340 )
1341 }
1342 });
1343 }
1344 (WrapperKind::TokioArcMutex, Some(inner_ty)) => {
1345 let kp_async_fn = format_ident!("f{}_async", idx);
1346 tokens.extend(quote! {
1347 #[inline]
1348 #[inline]
1349 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1350 rust_key_paths::Kp::new(
1351 |root: &#name| Some(&root.#idx_lit),
1352 |root: &mut #name| Some(&mut root.#idx_lit),
1353 )
1354 }
1355 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, #ty, #inner_ty> {
1356 rust_key_paths::async_lock::AsyncLockKp::new(
1357 rust_key_paths::Kp::new(
1358 |root: &#name| Some(&root.#idx_lit),
1359 |root: &mut #name| Some(&mut root.#idx_lit),
1360 ),
1361 rust_key_paths::async_lock::TokioMutexAccess::new(),
1362 rust_key_paths::Kp::new(
1363 |v: &#inner_ty| Some(v),
1364 |v: &mut #inner_ty| Some(v),
1365 ),
1366 )
1367 }
1368 });
1369 }
1370 (WrapperKind::TokioArcRwLock, Some(inner_ty)) => {
1371 let kp_async_fn = format_ident!("f{}_async", idx);
1372 tokens.extend(quote! {
1373 #[inline]
1374 #[inline]
1375 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1376 rust_key_paths::Kp::new(
1377 |root: &#name| Some(&root.#idx_lit),
1378 |root: &mut #name| Some(&mut root.#idx_lit),
1379 )
1380 }
1381 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, #ty, #inner_ty> {
1382 rust_key_paths::async_lock::AsyncLockKp::new(
1383 rust_key_paths::Kp::new(
1384 |root: &#name| Some(&root.#idx_lit),
1385 |root: &mut #name| Some(&mut root.#idx_lit),
1386 ),
1387 rust_key_paths::async_lock::TokioRwLockAccess::new(),
1388 rust_key_paths::Kp::new(
1389 |v: &#inner_ty| Some(v),
1390 |v: &mut #inner_ty| Some(v),
1391 ),
1392 )
1393 }
1394 });
1395 }
1396 (WrapperKind::OptionTokioArcMutex, Some(inner_ty)) => {
1397 let kp_async_fn = format_ident!("f{}_async", idx);
1398 tokens.extend(quote! {
1399 #[inline]
1400 #[inline]
1401 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1402 rust_key_paths::Kp::new(
1403 |root: &#name| Some(&root.#idx_lit),
1404 |root: &mut #name| Some(&mut root.#idx_lit),
1405 )
1406 }
1407 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, std::sync::Arc<tokio::sync::Mutex<#inner_ty>>, #inner_ty> {
1408 rust_key_paths::async_lock::AsyncLockKp::new(
1409 rust_key_paths::Kp::new(
1410 |root: &#name| root.#idx_lit.as_ref(),
1411 |root: &mut #name| root.#idx_lit.as_mut(),
1412 ),
1413 rust_key_paths::async_lock::TokioMutexAccess::new(),
1414 rust_key_paths::Kp::new(
1415 |v: &#inner_ty| Some(v),
1416 |v: &mut #inner_ty| Some(v),
1417 ),
1418 )
1419 }
1420 });
1421 }
1422 (WrapperKind::OptionTokioArcRwLock, Some(inner_ty)) => {
1423 let kp_async_fn = format_ident!("f{}_async", idx);
1424 tokens.extend(quote! {
1425 #[inline]
1426 #[inline]
1427 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1428 rust_key_paths::Kp::new(
1429 |root: &#name| Some(&root.#idx_lit),
1430 |root: &mut #name| Some(&mut root.#idx_lit),
1431 )
1432 }
1433 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, std::sync::Arc<tokio::sync::RwLock<#inner_ty>>, #inner_ty> {
1434 rust_key_paths::async_lock::AsyncLockKp::new(
1435 rust_key_paths::Kp::new(
1436 |root: &#name| root.#idx_lit.as_ref(),
1437 |root: &mut #name| root.#idx_lit.as_mut(),
1438 ),
1439 rust_key_paths::async_lock::TokioRwLockAccess::new(),
1440 rust_key_paths::Kp::new(
1441 |v: &#inner_ty| Some(v),
1442 |v: &mut #inner_ty| Some(v),
1443 ),
1444 )
1445 }
1446 });
1447 }
1448 (WrapperKind::OptionStdArcMutex, Some(inner_ty))
1449 | (WrapperKind::OptionArcMutex, Some(inner_ty)) => {
1450 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1451 tokens.extend(quote! {
1452 #[inline]
1453 #[inline]
1454 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1455 rust_key_paths::Kp::new(
1456 |root: &#name| Some(&root.#idx_lit),
1457 |root: &mut #name| Some(&mut root.#idx_lit),
1458 )
1459 }
1460 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::Mutex<#inner_ty>>> {
1461 rust_key_paths::Kp::new(
1462 |root: &#name| root.#idx_lit.as_ref(),
1463 |root: &mut #name| root.#idx_lit.as_mut(),
1464 )
1465 }
1466 });
1467 }
1468 (WrapperKind::OptionStdArcRwLock, Some(inner_ty))
1469 | (WrapperKind::OptionArcRwLock, Some(inner_ty)) => {
1470 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1471 tokens.extend(quote! {
1472 #[inline]
1473 #[inline]
1474 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1475 rust_key_paths::Kp::new(
1476 |root: &#name| Some(&root.#idx_lit),
1477 |root: &mut #name| Some(&mut root.#idx_lit),
1478 )
1479 }
1480 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::RwLock<#inner_ty>>> {
1481 rust_key_paths::Kp::new(
1482 |root: &#name| root.#idx_lit.as_ref(),
1483 |root: &mut #name| root.#idx_lit.as_mut(),
1484 )
1485 }
1486 });
1487 }
1488 (WrapperKind::OptionStdMutex, Some(inner_ty))
1489 | (WrapperKind::OptionMutex, Some(inner_ty)) => {
1490 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1491 tokens.extend(quote! {
1492 #[inline]
1493 #[inline]
1494 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1495 rust_key_paths::Kp::new(
1496 |root: &#name| Some(&root.#idx_lit),
1497 |root: &mut #name| Some(&mut root.#idx_lit),
1498 )
1499 }
1500 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Mutex<#inner_ty>> {
1501 rust_key_paths::Kp::new(
1502 |root: &#name| root.#idx_lit.as_ref(),
1503 |root: &mut #name| root.#idx_lit.as_mut(),
1504 )
1505 }
1506 });
1507 }
1508 (WrapperKind::OptionStdRwLock, Some(inner_ty))
1509 | (WrapperKind::OptionRwLock, Some(inner_ty)) => {
1510 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1511 tokens.extend(quote! {
1512 #[inline]
1513 #[inline]
1514 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1515 rust_key_paths::Kp::new(
1516 |root: &#name| Some(&root.#idx_lit),
1517 |root: &mut #name| Some(&mut root.#idx_lit),
1518 )
1519 }
1520 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::RwLock<#inner_ty>> {
1521 rust_key_paths::Kp::new(
1522 |root: &#name| root.#idx_lit.as_ref(),
1523 |root: &mut #name| root.#idx_lit.as_mut(),
1524 )
1525 }
1526 });
1527 }
1528 (WrapperKind::Weak, Some(_inner_ty)) => {
1529 tokens.extend(quote! {
1530 #[inline]
1531 #[inline]
1532 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1533 rust_key_paths::Kp::new(
1534 |root: &#name| Some(&root.#idx_lit),
1535 |_root: &mut #name| None,
1536 )
1537 }
1538 });
1539 }
1540 (WrapperKind::None, None) => {
1541 tokens.extend(quote! {
1542 #[inline]
1543 #[inline]
1544 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1545 rust_key_paths::Kp::new(
1546 |root: &#name| Some(&root.#idx_lit),
1547 |root: &mut #name| Some(&mut root.#idx_lit),
1548 )
1549 }
1550 });
1551 }
1552 _ => {
1553 tokens.extend(quote! {
1554 #[inline]
1555 #[inline]
1556 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1557 rust_key_paths::Kp::new(
1558 |root: &#name| Some(&root.#idx_lit),
1559 |root: &mut #name| Some(&mut root.#idx_lit),
1560 )
1561 }
1562 });
1563 }
1564 }
1565 }
1566
1567 tokens
1568 }
1569 Fields::Unit => {
1570 return syn::Error::new(input_span, "Kp derive does not support unit structs")
1571 .to_compile_error()
1572 .into();
1573 }
1574 },
1575 Data::Enum(data_enum) => {
1576 let mut tokens = proc_macro2::TokenStream::new();
1577
1578 // Generate identity methods for the enum
1579 tokens.extend(quote! {
1580 /// Returns a generic identity keypath for this type
1581 #[inline]
1582 pub fn identity_typed<Root, MutRoot>() -> rust_key_paths::Kp<
1583 #name,
1584 #name,
1585 Root,
1586 Root,
1587 MutRoot,
1588 MutRoot,
1589 fn(Root) -> Option<Root>,
1590 fn(MutRoot) -> Option<MutRoot>,
1591 >
1592 where
1593 Root: std::borrow::Borrow<#name>,
1594 MutRoot: std::borrow::BorrowMut<#name>,
1595 {
1596 rust_key_paths::Kp::new(
1597 |r: Root| Some(r),
1598 |r: MutRoot| Some(r)
1599 )
1600 }
1601
1602 /// Returns a simple identity keypath for this type
1603 #[inline]
1604 pub fn identity() -> rust_key_paths::KpType<'static, #name, #name> {
1605 rust_key_paths::Kp::new(
1606 |r: &#name| Some(r),
1607 |r: &mut #name| Some(r)
1608 )
1609 }
1610 });
1611
1612 for variant in data_enum.variants.iter() {
1613 let v_ident = &variant.ident;
1614 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
1615
1616 match &variant.fields {
1617 Fields::Unit => {
1618 // Unit variant - return keypath that checks if enum matches variant
1619 tokens.extend(quote! {
1620 #[inline]
1621 pub fn #snake() -> rust_key_paths::KpType<'static, #name, ()> {
1622 rust_key_paths::Kp::new(
1623 |root: &#name| match root {
1624 #name::#v_ident => {
1625 static UNIT: () = ();
1626 Some(&UNIT)
1627 },
1628 _ => None,
1629 },
1630 |_root: &mut #name| None, // Can't mutate unit variant
1631 )
1632 }
1633 });
1634 }
1635 Fields::Unnamed(unnamed) => {
1636 if unnamed.unnamed.len() == 1 {
1637 // Single-field tuple variant
1638 let field_ty = &unnamed.unnamed[0].ty;
1639 let (kind, inner_ty) = extract_wrapper_inner_type(field_ty);
1640
1641 match (kind, inner_ty.clone()) {
1642 (WrapperKind::Option, Some(inner_ty)) => {
1643 tokens.extend(quote! {
1644 #[inline]
1645 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1646 rust_key_paths::Kp::new(
1647 |root: &#name| match root {
1648 #name::#v_ident(inner) => inner.as_ref(),
1649 _ => None,
1650 },
1651 |root: &mut #name| match root {
1652 #name::#v_ident(inner) => inner.as_mut(),
1653 _ => None,
1654 },
1655 )
1656 }
1657 });
1658 }
1659 (WrapperKind::Vec, Some(inner_ty)) => {
1660 tokens.extend(quote! {
1661 #[inline]
1662 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1663 rust_key_paths::Kp::new(
1664 |root: &#name| match root {
1665 #name::#v_ident(inner) => inner.first(),
1666 _ => None,
1667 },
1668 |root: &mut #name| match root {
1669 #name::#v_ident(inner) => inner.first_mut(),
1670 _ => None,
1671 },
1672 )
1673 }
1674 });
1675 }
1676 (WrapperKind::Box, Some(inner_ty)) => {
1677 // Box in enum: deref to inner (&T / &mut T)
1678 tokens.extend(quote! {
1679 #[inline]
1680 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1681 rust_key_paths::Kp::new(
1682 |root: &#name| match root {
1683 #name::#v_ident(inner) => Some(&**inner),
1684 _ => None,
1685 },
1686 |root: &mut #name| match root {
1687 #name::#v_ident(inner) => Some(&mut **inner),
1688 _ => None,
1689 },
1690 )
1691 }
1692 });
1693 }
1694 (WrapperKind::Rc, Some(inner_ty)) => {
1695 tokens.extend(quote! {
1696 #[inline]
1697 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1698 rust_key_paths::Kp::new(
1699 |root: &#name| match root {
1700 #name::#v_ident(inner) => Some(inner.as_ref()),
1701 _ => None,
1702 },
1703 |root: &mut #name| match root {
1704 #name::#v_ident(inner) => std::rc::Rc::get_mut(inner),
1705 _ => None,
1706 },
1707 )
1708 }
1709 });
1710 }
1711 (WrapperKind::Arc, Some(inner_ty)) => {
1712 tokens.extend(quote! {
1713 #[inline]
1714 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1715 rust_key_paths::Kp::new(
1716 |root: &#name| match root {
1717 #name::#v_ident(inner) => Some(inner.as_ref()),
1718 _ => None,
1719 },
1720 |root: &mut #name| match root {
1721 #name::#v_ident(inner) => std::sync::Arc::get_mut(inner),
1722 _ => None,
1723 },
1724 )
1725 }
1726 });
1727 }
1728 (WrapperKind::Cow, Some(inner_ty)) => {
1729 tokens.extend(quote! {
1730 #[inline]
1731 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1732 rust_key_paths::Kp::new(
1733 |root: &#name| match root {
1734 #name::#v_ident(inner) => Some(inner.as_ref()),
1735 _ => None,
1736 },
1737 |root: &mut #name| match root {
1738 #name::#v_ident(inner) => Some(inner.to_mut()),
1739 _ => None,
1740 },
1741 )
1742 }
1743 });
1744 }
1745 (WrapperKind::OptionCow, Some(inner_ty)) => {
1746 tokens.extend(quote! {
1747 #[inline]
1748 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1749 rust_key_paths::Kp::new(
1750 |root: &#name| match root {
1751 #name::#v_ident(inner) => inner.as_ref().map(|c| c.as_ref()),
1752 _ => None,
1753 },
1754 |root: &mut #name| match root {
1755 #name::#v_ident(inner) => inner.as_mut().map(|c| c.to_mut()),
1756 _ => None,
1757 },
1758 )
1759 }
1760 });
1761 }
1762 (WrapperKind::None, None) => {
1763 // Basic type
1764 tokens.extend(quote! {
1765 #[inline]
1766 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
1767 rust_key_paths::Kp::new(
1768 |root: &#name| match root {
1769 #name::#v_ident(inner) => Some(inner),
1770 _ => None,
1771 },
1772 |root: &mut #name| match root {
1773 #name::#v_ident(inner) => Some(inner),
1774 _ => None,
1775 },
1776 )
1777 }
1778 });
1779 }
1780 _ => {
1781 // Other wrapper types - return keypath to field
1782 tokens.extend(quote! {
1783 #[inline]
1784 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
1785 rust_key_paths::Kp::new(
1786 |root: &#name| match root {
1787 #name::#v_ident(inner) => Some(inner),
1788 _ => None,
1789 },
1790 |root: &mut #name| match root {
1791 #name::#v_ident(inner) => Some(inner),
1792 _ => None,
1793 },
1794 )
1795 }
1796 });
1797 }
1798 }
1799 } else {
1800 // Multi-field tuple variant - return keypath to variant itself
1801 tokens.extend(quote! {
1802 #[inline]
1803 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #name> {
1804 rust_key_paths::Kp::new(
1805 |root: &#name| match root {
1806 #name::#v_ident(..) => Some(root),
1807 _ => None,
1808 },
1809 |root: &mut #name| match root {
1810 #name::#v_ident(..) => Some(root),
1811 _ => None,
1812 },
1813 )
1814 }
1815 });
1816 }
1817 }
1818 Fields::Named(_) => {
1819 // Named field variant - return keypath to variant itself
1820 tokens.extend(quote! {
1821 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #name> {
1822 rust_key_paths::Kp::new(
1823 |root: &#name| match root {
1824 #name::#v_ident { .. } => Some(root),
1825 _ => None,
1826 },
1827 |root: &mut #name| match root {
1828 #name::#v_ident { .. } => Some(root),
1829 _ => None,
1830 },
1831 )
1832 }
1833 });
1834 }
1835 }
1836 }
1837
1838 tokens
1839 }
1840 Data::Union(_) => {
1841 return syn::Error::new(input_span, "Kp derive does not support unions")
1842 .to_compile_error()
1843 .into();
1844 }
1845 };
1846
1847 let expanded = quote! {
1848 impl #name {
1849 #methods
1850 }
1851 };
1852
1853 TokenStream::from(expanded)
1854}
1855
1856/// Derive macro that generates `partial_kps() -> Vec<PKp<Self>>` returning all field/variant keypaths.
1857/// **Requires `#[derive(Kp)]`** so the keypath accessor methods exist.
1858///
1859/// For structs: returns keypaths for each field. For enums: returns keypaths for each variant
1860/// (using the same methods Kp generates, e.g. `some_variant()`).
1861///
1862/// # Example
1863/// ```
1864/// use key_paths_derive::{Kp, Pkp};
1865/// use rust_key_paths::PKp;
1866///
1867/// #[derive(Kp, Pkp)]
1868/// struct Person {
1869/// name: String,
1870/// age: i32,
1871/// }
1872///
1873/// let kps = Person::partial_kps();
1874/// assert_eq!(kps.len(), 2);
1875/// ```
1876#[proc_macro_derive(Pkp)]
1877pub fn derive_partial_keypaths(input: TokenStream) -> TokenStream {
1878 let input = parse_macro_input!(input as DeriveInput);
1879 let name = &input.ident;
1880
1881 let kp_calls = match &input.data {
1882 Data::Struct(data_struct) => match &data_struct.fields {
1883 Fields::Named(fields_named) => {
1884 let calls: Vec<_> = fields_named
1885 .named
1886 .iter()
1887 .filter_map(|f| f.ident.as_ref())
1888 .map(|field_ident| {
1889 quote! { rust_key_paths::PKp::new(Self::#field_ident()) }
1890 })
1891 .collect();
1892 quote! { #(#calls),* }
1893 }
1894 Fields::Unnamed(unnamed) => {
1895 let calls: Vec<_> = (0..unnamed.unnamed.len())
1896 .map(|idx| {
1897 let kp_fn = format_ident!("f{}", idx);
1898 quote! { rust_key_paths::PKp::new(Self::#kp_fn()) }
1899 })
1900 .collect();
1901 quote! { #(#calls),* }
1902 }
1903 Fields::Unit => quote! {},
1904 },
1905 Data::Enum(data_enum) => {
1906 let calls: Vec<_> = data_enum
1907 .variants
1908 .iter()
1909 .map(|variant| {
1910 let v_ident = &variant.ident;
1911 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
1912 quote! { rust_key_paths::PKp::new(Self::#snake()) }
1913 })
1914 .collect();
1915 quote! { #(#calls),* }
1916 }
1917 Data::Union(_) => {
1918 return syn::Error::new(
1919 input.ident.span(),
1920 "Pkp derive does not support unions",
1921 )
1922 .to_compile_error()
1923 .into();
1924 }
1925 };
1926
1927 let expanded = quote! {
1928 impl #name {
1929 /// Returns a vec of all field keypaths as partial keypaths (type-erased).
1930 #[inline]
1931 pub fn partial_kps() -> Vec<rust_key_paths::PKp<#name>> {
1932 vec![#kp_calls]
1933 }
1934 }
1935 };
1936
1937 TokenStream::from(expanded)
1938}
1939
1940/// Derive macro that generates `any_kps() -> Vec<AKp>` returning all field/variant keypaths as any keypaths.
1941/// **Requires `#[derive(Kp)]`** so the keypath accessor methods exist.
1942/// AKp type-erases both Root and Value, enabling heterogeneous collections of keypaths.
1943///
1944/// For structs: returns keypaths for each field. For enums: returns keypaths for each variant
1945/// (using the same methods Kp generates, e.g. `some_variant()`).
1946///
1947/// # Example
1948/// ```
1949/// use key_paths_derive::{Kp, Akp};
1950/// use rust_key_paths::AKp;
1951///
1952/// #[derive(Kp, Akp)]
1953/// struct Person {
1954/// name: String,
1955/// age: i32,
1956/// }
1957///
1958/// let kps = Person::any_kps();
1959/// assert_eq!(kps.len(), 2);
1960/// let person = Person { name: "Alice".into(), age: 30 };
1961/// let name: Option<&String> = kps[0].get(&person as &dyn std::any::Any).and_then(|v| v.downcast_ref());
1962/// assert_eq!(name, Some(&"Alice".to_string()));
1963/// ```
1964#[proc_macro_derive(Akp)]
1965pub fn derive_any_keypaths(input: TokenStream) -> TokenStream {
1966 let input = parse_macro_input!(input as DeriveInput);
1967 let name = &input.ident;
1968
1969 let kp_calls = match &input.data {
1970 Data::Struct(data_struct) => match &data_struct.fields {
1971 Fields::Named(fields_named) => {
1972 let calls: Vec<_> = fields_named
1973 .named
1974 .iter()
1975 .filter_map(|f| f.ident.as_ref())
1976 .map(|field_ident| {
1977 quote! { rust_key_paths::AKp::new(Self::#field_ident()) }
1978 })
1979 .collect();
1980 quote! { #(#calls),* }
1981 }
1982 Fields::Unnamed(unnamed) => {
1983 let calls: Vec<_> = (0..unnamed.unnamed.len())
1984 .map(|idx| {
1985 let kp_fn = format_ident!("f{}", idx);
1986 quote! { rust_key_paths::AKp::new(Self::#kp_fn()) }
1987 })
1988 .collect();
1989 quote! { #(#calls),* }
1990 }
1991 Fields::Unit => quote! {},
1992 },
1993 Data::Enum(data_enum) => {
1994 let calls: Vec<_> = data_enum
1995 .variants
1996 .iter()
1997 .map(|variant| {
1998 let v_ident = &variant.ident;
1999 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
2000 quote! { rust_key_paths::AKp::new(Self::#snake()) }
2001 })
2002 .collect();
2003 quote! { #(#calls),* }
2004 }
2005 Data::Union(_) => {
2006 return syn::Error::new(
2007 input.ident.span(),
2008 "Akp derive does not support unions",
2009 )
2010 .to_compile_error()
2011 .into();
2012 }
2013 };
2014
2015 let expanded = quote! {
2016 impl #name {
2017 /// Returns a vec of all field keypaths as any keypaths (fully type-erased).
2018 #[inline]
2019 pub fn any_kps() -> Vec<rust_key_paths::AKp> {
2020 vec![#kp_calls]
2021 }
2022 }
2023 };
2024
2025 TokenStream::from(expanded)
2026}