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 let kp_at_fn = format_ident!("{}_at", field_ident);
566
567 tokens.extend(quote! {
568 #[inline]
569 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
570 rust_key_paths::Kp::new(
571 |root: &#name| Some(&root.#field_ident),
572 |root: &mut #name| Some(&mut root.#field_ident),
573 )
574 }
575
576 /// _at: check if element exists and get reference.
577 /// HashSet does not allow mutable element access (would break hash invariant).
578 #[inline]
579 pub fn #kp_at_fn(key: #inner_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
580 where
581 #inner_ty: Clone + std::hash::Hash + Eq + 'static,
582 {
583 rust_key_paths::Kp::new(
584 Box::new(move |root: &#name| root.#field_ident.get(&key)),
585 Box::new(move |_root: &mut #name| None),
586 )
587 }
588 });
589 }
590 (WrapperKind::BTreeSet, Some(inner_ty)) => {
591 let kp_at_fn = format_ident!("{}_at", field_ident);
592
593 tokens.extend(quote! {
594 #[inline]
595 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
596 rust_key_paths::Kp::new(
597 |root: &#name| Some(&root.#field_ident),
598 |root: &mut #name| Some(&mut root.#field_ident),
599 )
600 }
601
602 /// _at: check if element exists and get reference.
603 /// BTreeSet does not allow mutable element access (would break ordering invariant).
604 #[inline]
605 pub fn #kp_at_fn(key: #inner_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
606 where
607 #inner_ty: Clone + Ord + 'static,
608 {
609 rust_key_paths::Kp::new(
610 Box::new(move |root: &#name| root.#field_ident.get(&key)),
611 Box::new(move |_root: &mut #name| None),
612 )
613 }
614 });
615 }
616 (WrapperKind::VecDeque, Some(inner_ty)) => {
617 tokens.extend(quote! {
618 #[inline]
619 #[inline]
620 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
621 rust_key_paths::Kp::new(
622 |root: &#name| Some(&root.#field_ident),
623 |root: &mut #name| Some(&mut root.#field_ident),
624 )
625 }
626 #[inline]
627 pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
628 rust_key_paths::Kp::new(
629 Box::new(move |root: &#name| root.#field_ident.get(index)),
630 Box::new(move |root: &mut #name| root.#field_ident.get_mut(index)),
631 )
632 }
633 });
634 }
635 (WrapperKind::LinkedList, Some(_inner_ty)) => {
636 tokens.extend(quote! {
637 #[inline]
638 #[inline]
639 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
640 rust_key_paths::Kp::new(
641 |root: &#name| Some(&root.#field_ident),
642 |root: &mut #name| Some(&mut root.#field_ident),
643 )
644 }
645 });
646 }
647 (WrapperKind::BinaryHeap, Some(_inner_ty)) => {
648 tokens.extend(quote! {
649 #[inline]
650 #[inline]
651 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
652 rust_key_paths::Kp::new(
653 |root: &#name| Some(&root.#field_ident),
654 |root: &mut #name| Some(&mut root.#field_ident),
655 )
656 }
657 });
658 }
659 (WrapperKind::Result, Some(inner_ty)) => {
660 // For Result<T, E>, access Ok value
661 tokens.extend(quote! {
662 #[inline]
663 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
664 rust_key_paths::Kp::new(
665 |root: &#name| root.#field_ident.as_ref().ok(),
666 |root: &mut #name| root.#field_ident.as_mut().ok(),
667 )
668 }
669 });
670 }
671 (WrapperKind::StdArcMutex, Some(inner_ty)) => {
672 // For Arc<std::sync::Mutex<T>>
673 let kp_lock_fn = format_ident!("{}_lock", field_ident);
674 tokens.extend(quote! {
675 #[inline]
676 #[inline]
677 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
678 rust_key_paths::Kp::new(
679 |root: &#name| Some(&root.#field_ident),
680 |root: &mut #name| Some(&mut root.#field_ident),
681 )
682 }
683 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcMutexFor<#name, #ty, #inner_ty> {
684 rust_key_paths::lock::LockKp::new(
685 rust_key_paths::Kp::new(
686 |root: &#name| Some(&root.#field_ident),
687 |root: &mut #name| Some(&mut root.#field_ident),
688 ),
689 rust_key_paths::lock::ArcMutexAccess::new(),
690 rust_key_paths::Kp::new(
691 |v: &#inner_ty| Some(v),
692 |v: &mut #inner_ty| Some(v),
693 ),
694 )
695 }
696 });
697 }
698 (WrapperKind::StdArcRwLock, Some(inner_ty)) => {
699 // For Arc<std::sync::RwLock<T>>
700 let kp_lock_fn = format_ident!("{}_lock", field_ident);
701 tokens.extend(quote! {
702 #[inline]
703 #[inline]
704 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
705 rust_key_paths::Kp::new(
706 |root: &#name| Some(&root.#field_ident),
707 |root: &mut #name| Some(&mut root.#field_ident),
708 )
709 }
710 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcRwLockFor<#name, #ty, #inner_ty> {
711 rust_key_paths::lock::LockKp::new(
712 rust_key_paths::Kp::new(
713 |root: &#name| Some(&root.#field_ident),
714 |root: &mut #name| Some(&mut root.#field_ident),
715 ),
716 rust_key_paths::lock::ArcRwLockAccess::new(),
717 rust_key_paths::Kp::new(
718 |v: &#inner_ty| Some(v),
719 |v: &mut #inner_ty| Some(v),
720 ),
721 )
722 }
723 });
724 }
725 (WrapperKind::ArcRwLock, Some(inner_ty)) => {
726 // For Arc<parking_lot::RwLock<T>> (requires rust-key-paths "parking_lot" feature)
727 let kp_lock_fn = format_ident!("{}_lock", field_ident);
728 tokens.extend(quote! {
729 #[inline]
730 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
731 rust_key_paths::Kp::new(
732 |root: &#name| Some(&root.#field_ident),
733 |root: &mut #name| Some(&mut root.#field_ident),
734 )
735 }
736 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpParkingLotRwLockFor<#name, #ty, #inner_ty> {
737 rust_key_paths::lock::LockKp::new(
738 rust_key_paths::Kp::new(
739 |root: &#name| Some(&root.#field_ident),
740 |root: &mut #name| Some(&mut root.#field_ident),
741 ),
742 rust_key_paths::lock::ParkingLotRwLockAccess::new(),
743 rust_key_paths::Kp::new(
744 |v: &#inner_ty| Some(v),
745 |v: &mut #inner_ty| Some(v),
746 ),
747 )
748 }
749 });
750 }
751 (WrapperKind::ArcMutex, Some(inner_ty)) => {
752 // For Arc<parking_lot::Mutex<T>> (requires rust-key-paths "parking_lot" feature)
753 let kp_lock_fn = format_ident!("{}_lock", field_ident);
754 tokens.extend(quote! {
755 #[inline]
756 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
757 rust_key_paths::Kp::new(
758 |root: &#name| Some(&root.#field_ident),
759 |root: &mut #name| Some(&mut root.#field_ident),
760 )
761 }
762 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpParkingLotMutexFor<#name, #ty, #inner_ty> {
763 rust_key_paths::lock::LockKp::new(
764 rust_key_paths::Kp::new(
765 |root: &#name| Some(&root.#field_ident),
766 |root: &mut #name| Some(&mut root.#field_ident),
767 ),
768 rust_key_paths::lock::ParkingLotMutexAccess::new(),
769 rust_key_paths::Kp::new(
770 |v: &#inner_ty| Some(v),
771 |v: &mut #inner_ty| Some(v),
772 ),
773 )
774 }
775 });
776 }
777 (WrapperKind::Mutex, Some(_inner_ty))
778 | (WrapperKind::StdMutex, Some(_inner_ty)) => {
779 // For Mutex<T>, return keypath to container
780 tokens.extend(quote! {
781 #[inline]
782 #[inline]
783 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
784 rust_key_paths::Kp::new(
785 |root: &#name| Some(&root.#field_ident),
786 |root: &mut #name| Some(&mut root.#field_ident),
787 )
788 }
789 });
790 }
791 (WrapperKind::RwLock, Some(_inner_ty))
792 | (WrapperKind::StdRwLock, Some(_inner_ty)) => {
793 // For RwLock<T>, return keypath to container
794 tokens.extend(quote! {
795 #[inline]
796 #[inline]
797 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
798 rust_key_paths::Kp::new(
799 |root: &#name| Some(&root.#field_ident),
800 |root: &mut #name| Some(&mut root.#field_ident),
801 )
802 }
803 });
804 }
805 (WrapperKind::TokioArcMutex, Some(inner_ty)) => {
806 let kp_async_fn = format_ident!("{}_async", field_ident);
807 tokens.extend(quote! {
808 #[inline]
809 #[inline]
810 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
811 rust_key_paths::Kp::new(
812 |root: &#name| Some(&root.#field_ident),
813 |root: &mut #name| Some(&mut root.#field_ident),
814 )
815 }
816 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, #ty, #inner_ty> {
817 rust_key_paths::async_lock::AsyncLockKp::new(
818 rust_key_paths::Kp::new(
819 |root: &#name| Some(&root.#field_ident),
820 |root: &mut #name| Some(&mut root.#field_ident),
821 ),
822 rust_key_paths::async_lock::TokioMutexAccess::new(),
823 rust_key_paths::Kp::new(
824 |v: &#inner_ty| Some(v),
825 |v: &mut #inner_ty| Some(v),
826 ),
827 )
828 }
829 });
830 }
831 (WrapperKind::TokioArcRwLock, Some(inner_ty)) => {
832 let kp_async_fn = format_ident!("{}_async", field_ident);
833 tokens.extend(quote! {
834 #[inline]
835 #[inline]
836 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
837 rust_key_paths::Kp::new(
838 |root: &#name| Some(&root.#field_ident),
839 |root: &mut #name| Some(&mut root.#field_ident),
840 )
841 }
842 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, #ty, #inner_ty> {
843 rust_key_paths::async_lock::AsyncLockKp::new(
844 rust_key_paths::Kp::new(
845 |root: &#name| Some(&root.#field_ident),
846 |root: &mut #name| Some(&mut root.#field_ident),
847 ),
848 rust_key_paths::async_lock::TokioRwLockAccess::new(),
849 rust_key_paths::Kp::new(
850 |v: &#inner_ty| Some(v),
851 |v: &mut #inner_ty| Some(v),
852 ),
853 )
854 }
855 });
856 }
857 (WrapperKind::OptionTokioArcMutex, Some(inner_ty)) => {
858 let kp_async_fn = format_ident!("{}_async", field_ident);
859 tokens.extend(quote! {
860 #[inline]
861 #[inline]
862 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
863 rust_key_paths::Kp::new(
864 |root: &#name| Some(&root.#field_ident),
865 |root: &mut #name| Some(&mut root.#field_ident),
866 )
867 }
868 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, std::sync::Arc<tokio::sync::Mutex<#inner_ty>>, #inner_ty> {
869 rust_key_paths::async_lock::AsyncLockKp::new(
870 rust_key_paths::Kp::new(
871 |root: &#name| root.#field_ident.as_ref(),
872 |root: &mut #name| root.#field_ident.as_mut(),
873 ),
874 rust_key_paths::async_lock::TokioMutexAccess::new(),
875 rust_key_paths::Kp::new(
876 |v: &#inner_ty| Some(v),
877 |v: &mut #inner_ty| Some(v),
878 ),
879 )
880 }
881 });
882 }
883 (WrapperKind::OptionTokioArcRwLock, Some(inner_ty)) => {
884 let kp_async_fn = format_ident!("{}_async", 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_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, std::sync::Arc<tokio::sync::RwLock<#inner_ty>>, #inner_ty> {
895 rust_key_paths::async_lock::AsyncLockKp::new(
896 rust_key_paths::Kp::new(
897 |root: &#name| root.#field_ident.as_ref(),
898 |root: &mut #name| root.#field_ident.as_mut(),
899 ),
900 rust_key_paths::async_lock::TokioRwLockAccess::new(),
901 rust_key_paths::Kp::new(
902 |v: &#inner_ty| Some(v),
903 |v: &mut #inner_ty| Some(v),
904 ),
905 )
906 }
907 });
908 }
909 (WrapperKind::OptionStdArcMutex, Some(inner_ty))
910 | (WrapperKind::OptionArcMutex, Some(inner_ty)) => {
911 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
912 let kp_lock_fn = format_ident!("{}_lock", field_ident);
913 tokens.extend(quote! {
914 #[inline]
915 #[inline]
916 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
917 rust_key_paths::Kp::new(
918 |root: &#name| Some(&root.#field_ident),
919 |root: &mut #name| Some(&mut root.#field_ident),
920 )
921 }
922 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::Mutex<#inner_ty>>> {
923 rust_key_paths::Kp::new(
924 |root: &#name| root.#field_ident.as_ref(),
925 |root: &mut #name| root.#field_ident.as_mut(),
926 )
927 }
928 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcMutexFor<#name, std::sync::Arc<std::sync::Mutex<#inner_ty>>, #inner_ty> {
929 rust_key_paths::lock::LockKp::new(
930 rust_key_paths::Kp::new(
931 |root: &#name| root.#field_ident.as_ref(),
932 |root: &mut #name| root.#field_ident.as_mut(),
933 ),
934 rust_key_paths::lock::ArcMutexAccess::new(),
935 rust_key_paths::Kp::new(
936 |v: &#inner_ty| Some(v),
937 |v: &mut #inner_ty| Some(v),
938 ),
939 )
940 }
941 });
942 }
943 (WrapperKind::OptionStdArcRwLock, Some(inner_ty))
944 | (WrapperKind::OptionArcRwLock, Some(inner_ty)) => {
945 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
946 let kp_lock_fn = format_ident!("{}_lock", field_ident);
947 tokens.extend(quote! {
948 #[inline]
949 #[inline]
950 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
951 rust_key_paths::Kp::new(
952 |root: &#name| Some(&root.#field_ident),
953 |root: &mut #name| Some(&mut root.#field_ident),
954 )
955 }
956 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::RwLock<#inner_ty>>> {
957 rust_key_paths::Kp::new(
958 |root: &#name| root.#field_ident.as_ref(),
959 |root: &mut #name| root.#field_ident.as_mut(),
960 )
961 }
962 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcRwLockFor<#name, std::sync::Arc<std::sync::RwLock<#inner_ty>>, #inner_ty> {
963 rust_key_paths::lock::LockKp::new(
964 rust_key_paths::Kp::new(
965 |root: &#name| root.#field_ident.as_ref(),
966 |root: &mut #name| root.#field_ident.as_mut(),
967 ),
968 rust_key_paths::lock::ArcRwLockAccess::new(),
969 rust_key_paths::Kp::new(
970 |v: &#inner_ty| Some(v),
971 |v: &mut #inner_ty| Some(v),
972 ),
973 )
974 }
975 });
976 }
977 (WrapperKind::OptionStdMutex, Some(inner_ty))
978 | (WrapperKind::OptionMutex, Some(inner_ty)) => {
979 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
980 tokens.extend(quote! {
981 #[inline]
982 #[inline]
983 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
984 rust_key_paths::Kp::new(
985 |root: &#name| Some(&root.#field_ident),
986 |root: &mut #name| Some(&mut root.#field_ident),
987 )
988 }
989 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Mutex<#inner_ty>> {
990 rust_key_paths::Kp::new(
991 |root: &#name| root.#field_ident.as_ref(),
992 |root: &mut #name| root.#field_ident.as_mut(),
993 )
994 }
995 });
996 }
997 (WrapperKind::OptionStdRwLock, Some(inner_ty))
998 | (WrapperKind::OptionRwLock, Some(inner_ty)) => {
999 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
1000 tokens.extend(quote! {
1001 #[inline]
1002 #[inline]
1003 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1004 rust_key_paths::Kp::new(
1005 |root: &#name| Some(&root.#field_ident),
1006 |root: &mut #name| Some(&mut root.#field_ident),
1007 )
1008 }
1009 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::RwLock<#inner_ty>> {
1010 rust_key_paths::Kp::new(
1011 |root: &#name| root.#field_ident.as_ref(),
1012 |root: &mut #name| root.#field_ident.as_mut(),
1013 )
1014 }
1015 });
1016 }
1017 (WrapperKind::Weak, Some(_inner_ty)) => {
1018 // For Weak<T>, return keypath to container
1019 tokens.extend(quote! {
1020 #[inline]
1021 #[inline]
1022 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1023 rust_key_paths::Kp::new(
1024 |root: &#name| Some(&root.#field_ident),
1025 |_root: &mut #name| None, // Weak doesn't support mutable access
1026 )
1027 }
1028 });
1029 }
1030 (WrapperKind::None, None) => {
1031 // For basic types, direct access
1032 tokens.extend(quote! {
1033 #[inline]
1034 #[inline]
1035 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1036 rust_key_paths::Kp::new(
1037 |root: &#name| Some(&root.#field_ident),
1038 |root: &mut #name| Some(&mut root.#field_ident),
1039 )
1040 }
1041 });
1042 }
1043 _ => {
1044 // For unknown/complex nested types, return keypath to field itself
1045 tokens.extend(quote! {
1046 #[inline]
1047 #[inline]
1048 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1049 rust_key_paths::Kp::new(
1050 |root: &#name| Some(&root.#field_ident),
1051 |root: &mut #name| Some(&mut root.#field_ident),
1052 )
1053 }
1054 });
1055 }
1056 }
1057 }
1058
1059 tokens
1060 }
1061 Fields::Unnamed(unnamed) => {
1062 let mut tokens = proc_macro2::TokenStream::new();
1063
1064 // Generate identity methods for the tuple struct
1065 tokens.extend(quote! {
1066 /// Returns a generic identity keypath for this type
1067 #[inline]
1068 pub fn identity_typed<Root, MutRoot>() -> rust_key_paths::Kp<
1069 #name,
1070 #name,
1071 Root,
1072 Root,
1073 MutRoot,
1074 MutRoot,
1075 fn(Root) -> Option<Root>,
1076 fn(MutRoot) -> Option<MutRoot>,
1077 >
1078 where
1079 Root: std::borrow::Borrow<#name>,
1080 MutRoot: std::borrow::BorrowMut<#name>,
1081 {
1082 rust_key_paths::Kp::new(
1083 |r: Root| Some(r),
1084 |r: MutRoot| Some(r)
1085 )
1086 }
1087
1088 /// Returns a simple identity keypath for this type
1089 #[inline]
1090 pub fn identity() -> rust_key_paths::KpType<'static, #name, #name> {
1091 rust_key_paths::Kp::new(
1092 |r: &#name| Some(r),
1093 |r: &mut #name| Some(r)
1094 )
1095 }
1096 });
1097
1098 for (idx, field) in unnamed.unnamed.iter().enumerate() {
1099 let idx_lit = syn::Index::from(idx);
1100 let ty = &field.ty;
1101 // Centralized keypath method names for tuple fields – change here to adjust for all types
1102 let kp_fn = format_ident!("f{}", idx);
1103 let kp_at_fn = format_ident!("f{}_at", idx);
1104
1105 let (kind, inner_ty) = extract_wrapper_inner_type(ty);
1106
1107 match (kind, inner_ty.clone()) {
1108 (WrapperKind::Option, Some(inner_ty)) => {
1109 tokens.extend(quote! {
1110 #[inline]
1111 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1112 rust_key_paths::Kp::new(
1113 |root: &#name| root.#idx_lit.as_ref(),
1114 |root: &mut #name| root.#idx_lit.as_mut(),
1115 )
1116 }
1117 });
1118 }
1119 (WrapperKind::Vec, Some(inner_ty)) => {
1120 tokens.extend(quote! {
1121 #[inline]
1122 #[inline]
1123 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1124 rust_key_paths::Kp::new(
1125 |root: &#name| Some(&root.#idx_lit),
1126 |root: &mut #name| Some(&mut root.#idx_lit),
1127 )
1128 }
1129 #[inline]
1130 pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
1131 rust_key_paths::Kp::new(
1132 Box::new(move |root: &#name| root.#idx_lit.get(index)),
1133 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(index)),
1134 )
1135 }
1136 });
1137 }
1138 (WrapperKind::HashMap, Some(inner_ty)) => {
1139 if let Some((key_ty, _)) = extract_map_key_value(ty) {
1140 tokens.extend(quote! {
1141 #[inline]
1142 #[inline]
1143 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1144 rust_key_paths::Kp::new(
1145 |root: &#name| Some(&root.#idx_lit),
1146 |root: &mut #name| Some(&mut root.#idx_lit),
1147 )
1148 }
1149 #[inline]
1150 pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
1151 where
1152 #key_ty: Clone + std::hash::Hash + Eq + 'static,
1153 {
1154 let key2 = key.clone();
1155 rust_key_paths::Kp::new(
1156 Box::new(move |root: &#name| root.#idx_lit.get(&key)),
1157 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(&key2)),
1158 )
1159 }
1160 });
1161 } else {
1162 tokens.extend(quote! {
1163 #[inline]
1164 #[inline]
1165 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1166 rust_key_paths::Kp::new(
1167 |root: &#name| Some(&root.#idx_lit),
1168 |root: &mut #name| Some(&mut root.#idx_lit),
1169 )
1170 }
1171 });
1172 }
1173 }
1174 (WrapperKind::BTreeMap, Some(inner_ty)) => {
1175 if let Some((key_ty, _)) = extract_map_key_value(ty) {
1176 tokens.extend(quote! {
1177 #[inline]
1178 #[inline]
1179 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1180 rust_key_paths::Kp::new(
1181 |root: &#name| Some(&root.#idx_lit),
1182 |root: &mut #name| Some(&mut root.#idx_lit),
1183 )
1184 }
1185 #[inline]
1186 pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
1187 where
1188 #key_ty: Clone + Ord + 'static,
1189 {
1190 let key2 = key.clone();
1191 rust_key_paths::Kp::new(
1192 Box::new(move |root: &#name| root.#idx_lit.get(&key)),
1193 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(&key2)),
1194 )
1195 }
1196 });
1197 } else {
1198 tokens.extend(quote! {
1199 #[inline]
1200 #[inline]
1201 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1202 rust_key_paths::Kp::new(
1203 |root: &#name| Some(&root.#idx_lit),
1204 |root: &mut #name| Some(&mut root.#idx_lit),
1205 )
1206 }
1207 });
1208 }
1209 }
1210 (WrapperKind::Box, Some(inner_ty)) => {
1211 // Box: deref to inner (returns &T / &mut T)
1212 tokens.extend(quote! {
1213 #[inline]
1214 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1215 rust_key_paths::Kp::new(
1216 |root: &#name| Some(&*root.#idx_lit),
1217 |root: &mut #name| Some(&mut *root.#idx_lit),
1218 )
1219 }
1220 });
1221 }
1222 (WrapperKind::Rc, Some(inner_ty)) => {
1223 tokens.extend(quote! {
1224 #[inline]
1225 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1226 rust_key_paths::Kp::new(
1227 |root: &#name| Some(root.#idx_lit.as_ref()),
1228 |root: &mut #name| std::rc::Rc::get_mut(&mut root.#idx_lit),
1229 )
1230 }
1231 });
1232 }
1233 (WrapperKind::Arc, Some(inner_ty)) => {
1234 tokens.extend(quote! {
1235 #[inline]
1236 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1237 rust_key_paths::Kp::new(
1238 |root: &#name| Some(root.#idx_lit.as_ref()),
1239 |root: &mut #name| std::sync::Arc::get_mut(&mut root.#idx_lit),
1240 )
1241 }
1242 });
1243 }
1244
1245 (WrapperKind::Cow, Some(inner_ty)) => {
1246 tokens.extend(quote! {
1247 #[inline]
1248 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1249 rust_key_paths::Kp::new(
1250 |root: &#name| Some(root.#idx_lit.as_ref()),
1251 |root: &mut #name| Some(root.#idx_lit.to_mut()),
1252 )
1253 }
1254 });
1255 }
1256
1257 (WrapperKind::OptionCow, Some(inner_ty)) => {
1258 tokens.extend(quote! {
1259 #[inline]
1260 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1261 rust_key_paths::Kp::new(
1262 |root: &#name| root.#idx_lit.as_ref().map(|c| c.as_ref()),
1263 |root: &mut #name| root.#idx_lit.as_mut().map(|c| c.to_mut()),
1264 )
1265 }
1266 });
1267 }
1268 (WrapperKind::HashSet, Some(inner_ty)) => {
1269 let kp_at_fn = format_ident!("f{}_at", idx);
1270
1271 tokens.extend(quote! {
1272 #[inline]
1273 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1274 rust_key_paths::Kp::new(
1275 |root: &#name| Some(&root.#idx_lit),
1276 |root: &mut #name| Some(&mut root.#idx_lit),
1277 )
1278 }
1279
1280 /// _at: check if element exists and get reference.
1281 /// HashSet does not allow mutable element access (would break hash invariant).
1282 #[inline]
1283 pub fn #kp_at_fn(key: #inner_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
1284 where
1285 #inner_ty: Clone + std::hash::Hash + Eq + 'static,
1286 {
1287 rust_key_paths::Kp::new(
1288 Box::new(move |root: &#name| root.#idx_lit.get(&key)),
1289 Box::new(move |_root: &mut #name| None),
1290 )
1291 }
1292 });
1293 }
1294 (WrapperKind::BTreeSet, Some(inner_ty)) => {
1295 let kp_at_fn = format_ident!("f{}_at", idx);
1296
1297 tokens.extend(quote! {
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 /// _at: check if element exists and get reference.
1307 /// BTreeSet does not allow mutable element access (would break ordering invariant).
1308 #[inline]
1309 pub fn #kp_at_fn(key: #inner_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
1310 where
1311 #inner_ty: Clone + Ord + 'static,
1312 {
1313 rust_key_paths::Kp::new(
1314 Box::new(move |root: &#name| root.#idx_lit.get(&key)),
1315 Box::new(move |_root: &mut #name| None),
1316 )
1317 }
1318 });
1319 }
1320 (WrapperKind::VecDeque, Some(inner_ty)) => {
1321 tokens.extend(quote! {
1322 #[inline]
1323 #[inline]
1324 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1325 rust_key_paths::Kp::new(
1326 |root: &#name| Some(&root.#idx_lit),
1327 |root: &mut #name| Some(&mut root.#idx_lit),
1328 )
1329 }
1330 #[inline]
1331 pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
1332 rust_key_paths::Kp::new(
1333 Box::new(move |root: &#name| root.#idx_lit.get(index)),
1334 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(index)),
1335 )
1336 }
1337 });
1338 }
1339 (WrapperKind::LinkedList, Some(_inner_ty)) => {
1340 tokens.extend(quote! {
1341 #[inline]
1342 #[inline]
1343 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1344 rust_key_paths::Kp::new(
1345 |root: &#name| Some(&root.#idx_lit),
1346 |root: &mut #name| Some(&mut root.#idx_lit),
1347 )
1348 }
1349 });
1350 }
1351 (WrapperKind::BinaryHeap, Some(_inner_ty)) => {
1352 tokens.extend(quote! {
1353 #[inline]
1354 #[inline]
1355 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1356 rust_key_paths::Kp::new(
1357 |root: &#name| Some(&root.#idx_lit),
1358 |root: &mut #name| Some(&mut root.#idx_lit),
1359 )
1360 }
1361 });
1362 }
1363 (WrapperKind::Result, Some(inner_ty)) => {
1364 tokens.extend(quote! {
1365 #[inline]
1366 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1367 rust_key_paths::Kp::new(
1368 |root: &#name| root.#idx_lit.as_ref().ok(),
1369 |root: &mut #name| root.#idx_lit.as_mut().ok(),
1370 )
1371 }
1372 });
1373 }
1374 (WrapperKind::Mutex, Some(_inner_ty))
1375 | (WrapperKind::StdMutex, Some(_inner_ty)) => {
1376 tokens.extend(quote! {
1377 #[inline]
1378 #[inline]
1379 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1380 rust_key_paths::Kp::new(
1381 |root: &#name| Some(&root.#idx_lit),
1382 |root: &mut #name| Some(&mut root.#idx_lit),
1383 )
1384 }
1385 });
1386 }
1387 (WrapperKind::RwLock, Some(_inner_ty))
1388 | (WrapperKind::StdRwLock, Some(_inner_ty)) => {
1389 tokens.extend(quote! {
1390 #[inline]
1391 #[inline]
1392 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1393 rust_key_paths::Kp::new(
1394 |root: &#name| Some(&root.#idx_lit),
1395 |root: &mut #name| Some(&mut root.#idx_lit),
1396 )
1397 }
1398 });
1399 }
1400 (WrapperKind::TokioArcMutex, Some(inner_ty)) => {
1401 let kp_async_fn = format_ident!("f{}_async", idx);
1402 tokens.extend(quote! {
1403 #[inline]
1404 #[inline]
1405 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1406 rust_key_paths::Kp::new(
1407 |root: &#name| Some(&root.#idx_lit),
1408 |root: &mut #name| Some(&mut root.#idx_lit),
1409 )
1410 }
1411 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, #ty, #inner_ty> {
1412 rust_key_paths::async_lock::AsyncLockKp::new(
1413 rust_key_paths::Kp::new(
1414 |root: &#name| Some(&root.#idx_lit),
1415 |root: &mut #name| Some(&mut root.#idx_lit),
1416 ),
1417 rust_key_paths::async_lock::TokioMutexAccess::new(),
1418 rust_key_paths::Kp::new(
1419 |v: &#inner_ty| Some(v),
1420 |v: &mut #inner_ty| Some(v),
1421 ),
1422 )
1423 }
1424 });
1425 }
1426 (WrapperKind::TokioArcRwLock, Some(inner_ty)) => {
1427 let kp_async_fn = format_ident!("f{}_async", idx);
1428 tokens.extend(quote! {
1429 #[inline]
1430 #[inline]
1431 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1432 rust_key_paths::Kp::new(
1433 |root: &#name| Some(&root.#idx_lit),
1434 |root: &mut #name| Some(&mut root.#idx_lit),
1435 )
1436 }
1437 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, #ty, #inner_ty> {
1438 rust_key_paths::async_lock::AsyncLockKp::new(
1439 rust_key_paths::Kp::new(
1440 |root: &#name| Some(&root.#idx_lit),
1441 |root: &mut #name| Some(&mut root.#idx_lit),
1442 ),
1443 rust_key_paths::async_lock::TokioRwLockAccess::new(),
1444 rust_key_paths::Kp::new(
1445 |v: &#inner_ty| Some(v),
1446 |v: &mut #inner_ty| Some(v),
1447 ),
1448 )
1449 }
1450 });
1451 }
1452 (WrapperKind::OptionTokioArcMutex, Some(inner_ty)) => {
1453 let kp_async_fn = format_ident!("f{}_async", idx);
1454 tokens.extend(quote! {
1455 #[inline]
1456 #[inline]
1457 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1458 rust_key_paths::Kp::new(
1459 |root: &#name| Some(&root.#idx_lit),
1460 |root: &mut #name| Some(&mut root.#idx_lit),
1461 )
1462 }
1463 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, std::sync::Arc<tokio::sync::Mutex<#inner_ty>>, #inner_ty> {
1464 rust_key_paths::async_lock::AsyncLockKp::new(
1465 rust_key_paths::Kp::new(
1466 |root: &#name| root.#idx_lit.as_ref(),
1467 |root: &mut #name| root.#idx_lit.as_mut(),
1468 ),
1469 rust_key_paths::async_lock::TokioMutexAccess::new(),
1470 rust_key_paths::Kp::new(
1471 |v: &#inner_ty| Some(v),
1472 |v: &mut #inner_ty| Some(v),
1473 ),
1474 )
1475 }
1476 });
1477 }
1478 (WrapperKind::OptionTokioArcRwLock, Some(inner_ty)) => {
1479 let kp_async_fn = format_ident!("f{}_async", idx);
1480 tokens.extend(quote! {
1481 #[inline]
1482 #[inline]
1483 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1484 rust_key_paths::Kp::new(
1485 |root: &#name| Some(&root.#idx_lit),
1486 |root: &mut #name| Some(&mut root.#idx_lit),
1487 )
1488 }
1489 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, std::sync::Arc<tokio::sync::RwLock<#inner_ty>>, #inner_ty> {
1490 rust_key_paths::async_lock::AsyncLockKp::new(
1491 rust_key_paths::Kp::new(
1492 |root: &#name| root.#idx_lit.as_ref(),
1493 |root: &mut #name| root.#idx_lit.as_mut(),
1494 ),
1495 rust_key_paths::async_lock::TokioRwLockAccess::new(),
1496 rust_key_paths::Kp::new(
1497 |v: &#inner_ty| Some(v),
1498 |v: &mut #inner_ty| Some(v),
1499 ),
1500 )
1501 }
1502 });
1503 }
1504 (WrapperKind::OptionStdArcMutex, Some(inner_ty))
1505 | (WrapperKind::OptionArcMutex, Some(inner_ty)) => {
1506 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1507 tokens.extend(quote! {
1508 #[inline]
1509 #[inline]
1510 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1511 rust_key_paths::Kp::new(
1512 |root: &#name| Some(&root.#idx_lit),
1513 |root: &mut #name| Some(&mut root.#idx_lit),
1514 )
1515 }
1516 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::Mutex<#inner_ty>>> {
1517 rust_key_paths::Kp::new(
1518 |root: &#name| root.#idx_lit.as_ref(),
1519 |root: &mut #name| root.#idx_lit.as_mut(),
1520 )
1521 }
1522 });
1523 }
1524 (WrapperKind::OptionStdArcRwLock, Some(inner_ty))
1525 | (WrapperKind::OptionArcRwLock, Some(inner_ty)) => {
1526 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1527 tokens.extend(quote! {
1528 #[inline]
1529 #[inline]
1530 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1531 rust_key_paths::Kp::new(
1532 |root: &#name| Some(&root.#idx_lit),
1533 |root: &mut #name| Some(&mut root.#idx_lit),
1534 )
1535 }
1536 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::RwLock<#inner_ty>>> {
1537 rust_key_paths::Kp::new(
1538 |root: &#name| root.#idx_lit.as_ref(),
1539 |root: &mut #name| root.#idx_lit.as_mut(),
1540 )
1541 }
1542 });
1543 }
1544 (WrapperKind::OptionStdMutex, Some(inner_ty))
1545 | (WrapperKind::OptionMutex, Some(inner_ty)) => {
1546 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1547 tokens.extend(quote! {
1548 #[inline]
1549 #[inline]
1550 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1551 rust_key_paths::Kp::new(
1552 |root: &#name| Some(&root.#idx_lit),
1553 |root: &mut #name| Some(&mut root.#idx_lit),
1554 )
1555 }
1556 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Mutex<#inner_ty>> {
1557 rust_key_paths::Kp::new(
1558 |root: &#name| root.#idx_lit.as_ref(),
1559 |root: &mut #name| root.#idx_lit.as_mut(),
1560 )
1561 }
1562 });
1563 }
1564 (WrapperKind::OptionStdRwLock, Some(inner_ty))
1565 | (WrapperKind::OptionRwLock, Some(inner_ty)) => {
1566 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1567 tokens.extend(quote! {
1568 #[inline]
1569 #[inline]
1570 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1571 rust_key_paths::Kp::new(
1572 |root: &#name| Some(&root.#idx_lit),
1573 |root: &mut #name| Some(&mut root.#idx_lit),
1574 )
1575 }
1576 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::RwLock<#inner_ty>> {
1577 rust_key_paths::Kp::new(
1578 |root: &#name| root.#idx_lit.as_ref(),
1579 |root: &mut #name| root.#idx_lit.as_mut(),
1580 )
1581 }
1582 });
1583 }
1584 (WrapperKind::Weak, Some(_inner_ty)) => {
1585 tokens.extend(quote! {
1586 #[inline]
1587 #[inline]
1588 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1589 rust_key_paths::Kp::new(
1590 |root: &#name| Some(&root.#idx_lit),
1591 |_root: &mut #name| None,
1592 )
1593 }
1594 });
1595 }
1596 (WrapperKind::None, None) => {
1597 tokens.extend(quote! {
1598 #[inline]
1599 #[inline]
1600 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1601 rust_key_paths::Kp::new(
1602 |root: &#name| Some(&root.#idx_lit),
1603 |root: &mut #name| Some(&mut root.#idx_lit),
1604 )
1605 }
1606 });
1607 }
1608 _ => {
1609 tokens.extend(quote! {
1610 #[inline]
1611 #[inline]
1612 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1613 rust_key_paths::Kp::new(
1614 |root: &#name| Some(&root.#idx_lit),
1615 |root: &mut #name| Some(&mut root.#idx_lit),
1616 )
1617 }
1618 });
1619 }
1620 }
1621 }
1622
1623 tokens
1624 }
1625 Fields::Unit => {
1626 return syn::Error::new(input_span, "Kp derive does not support unit structs")
1627 .to_compile_error()
1628 .into();
1629 }
1630 },
1631 Data::Enum(data_enum) => {
1632 let mut tokens = proc_macro2::TokenStream::new();
1633
1634 // Generate identity methods for the enum
1635 tokens.extend(quote! {
1636 /// Returns a generic identity keypath for this type
1637 #[inline]
1638 pub fn identity_typed<Root, MutRoot>() -> rust_key_paths::Kp<
1639 #name,
1640 #name,
1641 Root,
1642 Root,
1643 MutRoot,
1644 MutRoot,
1645 fn(Root) -> Option<Root>,
1646 fn(MutRoot) -> Option<MutRoot>,
1647 >
1648 where
1649 Root: std::borrow::Borrow<#name>,
1650 MutRoot: std::borrow::BorrowMut<#name>,
1651 {
1652 rust_key_paths::Kp::new(
1653 |r: Root| Some(r),
1654 |r: MutRoot| Some(r)
1655 )
1656 }
1657
1658 /// Returns a simple identity keypath for this type
1659 #[inline]
1660 pub fn identity() -> rust_key_paths::KpType<'static, #name, #name> {
1661 rust_key_paths::Kp::new(
1662 |r: &#name| Some(r),
1663 |r: &mut #name| Some(r)
1664 )
1665 }
1666 });
1667
1668 for variant in data_enum.variants.iter() {
1669 let v_ident = &variant.ident;
1670 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
1671
1672 match &variant.fields {
1673 Fields::Unit => {
1674 // Unit variant - return keypath that checks if enum matches variant
1675 tokens.extend(quote! {
1676 #[inline]
1677 pub fn #snake() -> rust_key_paths::KpType<'static, #name, ()> {
1678 rust_key_paths::Kp::new(
1679 |root: &#name| match root {
1680 #name::#v_ident => {
1681 static UNIT: () = ();
1682 Some(&UNIT)
1683 },
1684 _ => None,
1685 },
1686 |_root: &mut #name| None, // Can't mutate unit variant
1687 )
1688 }
1689 });
1690 }
1691 Fields::Unnamed(unnamed) => {
1692 if unnamed.unnamed.len() == 1 {
1693 // Single-field tuple variant
1694 let field_ty = &unnamed.unnamed[0].ty;
1695 let (kind, inner_ty) = extract_wrapper_inner_type(field_ty);
1696
1697 match (kind, inner_ty.clone()) {
1698 (WrapperKind::Option, Some(inner_ty)) => {
1699 tokens.extend(quote! {
1700 #[inline]
1701 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1702 rust_key_paths::Kp::new(
1703 |root: &#name| match root {
1704 #name::#v_ident(inner) => inner.as_ref(),
1705 _ => None,
1706 },
1707 |root: &mut #name| match root {
1708 #name::#v_ident(inner) => inner.as_mut(),
1709 _ => None,
1710 },
1711 )
1712 }
1713 });
1714 }
1715 (WrapperKind::Vec, Some(inner_ty)) => {
1716 tokens.extend(quote! {
1717 #[inline]
1718 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1719 rust_key_paths::Kp::new(
1720 |root: &#name| match root {
1721 #name::#v_ident(inner) => inner.first(),
1722 _ => None,
1723 },
1724 |root: &mut #name| match root {
1725 #name::#v_ident(inner) => inner.first_mut(),
1726 _ => None,
1727 },
1728 )
1729 }
1730 });
1731 }
1732 (WrapperKind::Box, Some(inner_ty)) => {
1733 // Box in enum: deref to inner (&T / &mut T)
1734 tokens.extend(quote! {
1735 #[inline]
1736 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1737 rust_key_paths::Kp::new(
1738 |root: &#name| match root {
1739 #name::#v_ident(inner) => Some(&**inner),
1740 _ => None,
1741 },
1742 |root: &mut #name| match root {
1743 #name::#v_ident(inner) => Some(&mut **inner),
1744 _ => None,
1745 },
1746 )
1747 }
1748 });
1749 }
1750 (WrapperKind::Rc, Some(inner_ty)) => {
1751 tokens.extend(quote! {
1752 #[inline]
1753 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1754 rust_key_paths::Kp::new(
1755 |root: &#name| match root {
1756 #name::#v_ident(inner) => Some(inner.as_ref()),
1757 _ => None,
1758 },
1759 |root: &mut #name| match root {
1760 #name::#v_ident(inner) => std::rc::Rc::get_mut(inner),
1761 _ => None,
1762 },
1763 )
1764 }
1765 });
1766 }
1767 (WrapperKind::Arc, Some(inner_ty)) => {
1768 tokens.extend(quote! {
1769 #[inline]
1770 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1771 rust_key_paths::Kp::new(
1772 |root: &#name| match root {
1773 #name::#v_ident(inner) => Some(inner.as_ref()),
1774 _ => None,
1775 },
1776 |root: &mut #name| match root {
1777 #name::#v_ident(inner) => std::sync::Arc::get_mut(inner),
1778 _ => None,
1779 },
1780 )
1781 }
1782 });
1783 }
1784 (WrapperKind::Cow, Some(inner_ty)) => {
1785 tokens.extend(quote! {
1786 #[inline]
1787 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1788 rust_key_paths::Kp::new(
1789 |root: &#name| match root {
1790 #name::#v_ident(inner) => Some(inner.as_ref()),
1791 _ => None,
1792 },
1793 |root: &mut #name| match root {
1794 #name::#v_ident(inner) => Some(inner.to_mut()),
1795 _ => None,
1796 },
1797 )
1798 }
1799 });
1800 }
1801 (WrapperKind::OptionCow, Some(inner_ty)) => {
1802 tokens.extend(quote! {
1803 #[inline]
1804 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1805 rust_key_paths::Kp::new(
1806 |root: &#name| match root {
1807 #name::#v_ident(inner) => inner.as_ref().map(|c| c.as_ref()),
1808 _ => None,
1809 },
1810 |root: &mut #name| match root {
1811 #name::#v_ident(inner) => inner.as_mut().map(|c| c.to_mut()),
1812 _ => None,
1813 },
1814 )
1815 }
1816 });
1817 }
1818 (WrapperKind::None, None) => {
1819 // Basic type
1820 tokens.extend(quote! {
1821 #[inline]
1822 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
1823 rust_key_paths::Kp::new(
1824 |root: &#name| match root {
1825 #name::#v_ident(inner) => Some(inner),
1826 _ => None,
1827 },
1828 |root: &mut #name| match root {
1829 #name::#v_ident(inner) => Some(inner),
1830 _ => None,
1831 },
1832 )
1833 }
1834 });
1835 }
1836 _ => {
1837 // Other wrapper types - return keypath to field
1838 tokens.extend(quote! {
1839 #[inline]
1840 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
1841 rust_key_paths::Kp::new(
1842 |root: &#name| match root {
1843 #name::#v_ident(inner) => Some(inner),
1844 _ => None,
1845 },
1846 |root: &mut #name| match root {
1847 #name::#v_ident(inner) => Some(inner),
1848 _ => None,
1849 },
1850 )
1851 }
1852 });
1853 }
1854 }
1855 } else {
1856 // Multi-field tuple variant - return keypath to variant itself
1857 tokens.extend(quote! {
1858 #[inline]
1859 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #name> {
1860 rust_key_paths::Kp::new(
1861 |root: &#name| match root {
1862 #name::#v_ident(..) => Some(root),
1863 _ => None,
1864 },
1865 |root: &mut #name| match root {
1866 #name::#v_ident(..) => Some(root),
1867 _ => None,
1868 },
1869 )
1870 }
1871 });
1872 }
1873 }
1874 Fields::Named(_) => {
1875 // Named field variant - return keypath to variant itself
1876 tokens.extend(quote! {
1877 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #name> {
1878 rust_key_paths::Kp::new(
1879 |root: &#name| match root {
1880 #name::#v_ident { .. } => Some(root),
1881 _ => None,
1882 },
1883 |root: &mut #name| match root {
1884 #name::#v_ident { .. } => Some(root),
1885 _ => None,
1886 },
1887 )
1888 }
1889 });
1890 }
1891 }
1892 }
1893
1894 tokens
1895 }
1896 Data::Union(_) => {
1897 return syn::Error::new(input_span, "Kp derive does not support unions")
1898 .to_compile_error()
1899 .into();
1900 }
1901 };
1902
1903 let expanded = quote! {
1904 impl #name {
1905 #methods
1906 }
1907 };
1908
1909 TokenStream::from(expanded)
1910}
1911
1912/// Derive macro that generates `partial_kps() -> Vec<PKp<Self>>` returning all field/variant keypaths.
1913/// **Requires `#[derive(Kp)]`** so the keypath accessor methods exist.
1914///
1915/// For structs: returns keypaths for each field. For enums: returns keypaths for each variant
1916/// (using the same methods Kp generates, e.g. `some_variant()`).
1917///
1918/// # Example
1919/// ```
1920/// use key_paths_derive::{Kp, Pkp};
1921/// use rust_key_paths::PKp;
1922///
1923/// #[derive(Kp, Pkp)]
1924/// struct Person {
1925/// name: String,
1926/// age: i32,
1927/// }
1928///
1929/// let kps = Person::partial_kps();
1930/// assert_eq!(kps.len(), 2);
1931/// ```
1932#[proc_macro_derive(Pkp)]
1933pub fn derive_partial_keypaths(input: TokenStream) -> TokenStream {
1934 let input = parse_macro_input!(input as DeriveInput);
1935 let name = &input.ident;
1936
1937 let kp_calls = match &input.data {
1938 Data::Struct(data_struct) => match &data_struct.fields {
1939 Fields::Named(fields_named) => {
1940 let calls: Vec<_> = fields_named
1941 .named
1942 .iter()
1943 .filter_map(|f| f.ident.as_ref())
1944 .map(|field_ident| {
1945 quote! { rust_key_paths::PKp::new(Self::#field_ident()) }
1946 })
1947 .collect();
1948 quote! { #(#calls),* }
1949 }
1950 Fields::Unnamed(unnamed) => {
1951 let calls: Vec<_> = (0..unnamed.unnamed.len())
1952 .map(|idx| {
1953 let kp_fn = format_ident!("f{}", idx);
1954 quote! { rust_key_paths::PKp::new(Self::#kp_fn()) }
1955 })
1956 .collect();
1957 quote! { #(#calls),* }
1958 }
1959 Fields::Unit => quote! {},
1960 },
1961 Data::Enum(data_enum) => {
1962 let calls: Vec<_> = data_enum
1963 .variants
1964 .iter()
1965 .map(|variant| {
1966 let v_ident = &variant.ident;
1967 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
1968 quote! { rust_key_paths::PKp::new(Self::#snake()) }
1969 })
1970 .collect();
1971 quote! { #(#calls),* }
1972 }
1973 Data::Union(_) => {
1974 return syn::Error::new(
1975 input.ident.span(),
1976 "Pkp derive does not support unions",
1977 )
1978 .to_compile_error()
1979 .into();
1980 }
1981 };
1982
1983 let expanded = quote! {
1984 impl #name {
1985 /// Returns a vec of all field keypaths as partial keypaths (type-erased).
1986 #[inline]
1987 pub fn partial_kps() -> Vec<rust_key_paths::PKp<#name>> {
1988 vec![#kp_calls]
1989 }
1990 }
1991 };
1992
1993 TokenStream::from(expanded)
1994}
1995
1996/// Derive macro that generates `any_kps() -> Vec<AKp>` returning all field/variant keypaths as any keypaths.
1997/// **Requires `#[derive(Kp)]`** so the keypath accessor methods exist.
1998/// AKp type-erases both Root and Value, enabling heterogeneous collections of keypaths.
1999///
2000/// For structs: returns keypaths for each field. For enums: returns keypaths for each variant
2001/// (using the same methods Kp generates, e.g. `some_variant()`).
2002///
2003/// # Example
2004/// ```
2005/// use key_paths_derive::{Kp, Akp};
2006/// use rust_key_paths::AKp;
2007///
2008/// #[derive(Kp, Akp)]
2009/// struct Person {
2010/// name: String,
2011/// age: i32,
2012/// }
2013///
2014/// let kps = Person::any_kps();
2015/// assert_eq!(kps.len(), 2);
2016/// let person = Person { name: "Alice".into(), age: 30 };
2017/// let name: Option<&String> = kps[0].get(&person as &dyn std::any::Any).and_then(|v| v.downcast_ref());
2018/// assert_eq!(name, Some(&"Alice".to_string()));
2019/// ```
2020#[proc_macro_derive(Akp)]
2021pub fn derive_any_keypaths(input: TokenStream) -> TokenStream {
2022 let input = parse_macro_input!(input as DeriveInput);
2023 let name = &input.ident;
2024
2025 let kp_calls = match &input.data {
2026 Data::Struct(data_struct) => match &data_struct.fields {
2027 Fields::Named(fields_named) => {
2028 let calls: Vec<_> = fields_named
2029 .named
2030 .iter()
2031 .filter_map(|f| f.ident.as_ref())
2032 .map(|field_ident| {
2033 quote! { rust_key_paths::AKp::new(Self::#field_ident()) }
2034 })
2035 .collect();
2036 quote! { #(#calls),* }
2037 }
2038 Fields::Unnamed(unnamed) => {
2039 let calls: Vec<_> = (0..unnamed.unnamed.len())
2040 .map(|idx| {
2041 let kp_fn = format_ident!("f{}", idx);
2042 quote! { rust_key_paths::AKp::new(Self::#kp_fn()) }
2043 })
2044 .collect();
2045 quote! { #(#calls),* }
2046 }
2047 Fields::Unit => quote! {},
2048 },
2049 Data::Enum(data_enum) => {
2050 let calls: Vec<_> = data_enum
2051 .variants
2052 .iter()
2053 .map(|variant| {
2054 let v_ident = &variant.ident;
2055 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
2056 quote! { rust_key_paths::AKp::new(Self::#snake()) }
2057 })
2058 .collect();
2059 quote! { #(#calls),* }
2060 }
2061 Data::Union(_) => {
2062 return syn::Error::new(
2063 input.ident.span(),
2064 "Akp derive does not support unions",
2065 )
2066 .to_compile_error()
2067 .into();
2068 }
2069 };
2070
2071 let expanded = quote! {
2072 impl #name {
2073 /// Returns a vec of all field keypaths as any keypaths (fully type-erased).
2074 #[inline]
2075 pub fn any_kps() -> Vec<rust_key_paths::AKp> {
2076 vec![#kp_calls]
2077 }
2078 }
2079 };
2080
2081 TokenStream::from(expanded)
2082}