Skip to main content

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}