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}
70
71/// Helper function to check if a type path includes std::sync module
72fn is_std_sync_type(path: &syn::Path) -> bool {
73    // Check for paths like std::sync::Mutex, std::sync::RwLock
74    let segments: Vec<_> = path.segments.iter().map(|s| s.ident.to_string()).collect();
75    segments.len() >= 2
76        && segments.contains(&"std".to_string())
77        && segments.contains(&"sync".to_string())
78}
79
80/// Helper function to check if a type path includes tokio::sync module
81fn is_tokio_sync_type(path: &syn::Path) -> bool {
82    // Check for paths like tokio::sync::Mutex, tokio::sync::RwLock
83    let segments: Vec<_> = path.segments.iter().map(|s| s.ident.to_string()).collect();
84    segments.len() >= 2
85        && segments.contains(&"tokio".to_string())
86        && segments.contains(&"sync".to_string())
87}
88
89fn extract_wrapper_inner_type(ty: &Type) -> (WrapperKind, Option<Type>) {
90    use syn::{GenericArgument, PathArguments};
91
92    if let Type::Path(tp) = ty {
93        // Check if this is explicitly a std::sync type
94        let is_std_sync = is_std_sync_type(&tp.path);
95        // Check if this is explicitly a tokio::sync type
96        let is_tokio_sync = is_tokio_sync_type(&tp.path);
97
98        if let Some(seg) = tp.path.segments.last() {
99            let ident_str = seg.ident.to_string();
100
101            if let PathArguments::AngleBracketed(ab) = &seg.arguments {
102                let args: Vec<_> = ab.args.iter().collect();
103
104                // Handle map types (HashMap, BTreeMap) - they have K, V parameters
105                if ident_str == "HashMap" || ident_str == "BTreeMap" {
106                    if let (Some(_key_arg), Some(value_arg)) = (args.get(0), args.get(1)) {
107                        if let GenericArgument::Type(inner) = value_arg {
108                            // Check for nested Option in map values
109                            let (inner_kind, inner_inner) = extract_wrapper_inner_type(inner);
110                            match (ident_str.as_str(), inner_kind) {
111                                ("HashMap", WrapperKind::Option) => {
112                                    return (WrapperKind::HashMapOption, inner_inner);
113                                }
114                                _ => {
115                                    return match ident_str.as_str() {
116                                        "HashMap" => (WrapperKind::HashMap, Some(inner.clone())),
117                                        "BTreeMap" => (WrapperKind::BTreeMap, Some(inner.clone())),
118                                        _ => (WrapperKind::None, None),
119                                    };
120                                }
121                            }
122                        }
123                    }
124                }
125                // Handle single-parameter container types
126                else if let Some(arg) = args.get(0) {
127                    if let GenericArgument::Type(inner) = arg {
128                        // Check for nested containers first
129                        let (inner_kind, inner_inner) = extract_wrapper_inner_type(inner);
130
131                        // Handle nested combinations
132                        match (ident_str.as_str(), inner_kind) {
133                            ("Option", WrapperKind::Box) => {
134                                return (WrapperKind::OptionBox, inner_inner);
135                            }
136                            ("Option", WrapperKind::Rc) => {
137                                return (WrapperKind::OptionRc, inner_inner);
138                            }
139                            ("Option", WrapperKind::Arc) => {
140                                return (WrapperKind::OptionArc, inner_inner);
141                            }
142                            ("Option", WrapperKind::Vec) => {
143                                return (WrapperKind::OptionVec, inner_inner);
144                            }
145                            ("Option", WrapperKind::HashMap) => {
146                                return (WrapperKind::OptionHashMap, inner_inner);
147                            }
148                            ("Option", WrapperKind::StdArcMutex) => {
149                                return (WrapperKind::OptionStdArcMutex, inner_inner);
150                            }
151                            ("Option", WrapperKind::StdArcRwLock) => {
152                                return (WrapperKind::OptionStdArcRwLock, inner_inner);
153                            }
154                            ("Option", WrapperKind::ArcMutex) => {
155                                return (WrapperKind::OptionArcMutex, inner_inner);
156                            }
157                            ("Option", WrapperKind::ArcRwLock) => {
158                                return (WrapperKind::OptionArcRwLock, inner_inner);
159                            }
160                            ("Option", WrapperKind::StdMutex) => {
161                                return (WrapperKind::OptionStdMutex, inner_inner);
162                            }
163                            ("Option", WrapperKind::StdRwLock) => {
164                                return (WrapperKind::OptionStdRwLock, inner_inner);
165                            }
166                            ("Option", WrapperKind::Mutex) => {
167                                return (WrapperKind::OptionMutex, inner_inner);
168                            }
169                            ("Option", WrapperKind::RwLock) => {
170                                return (WrapperKind::OptionRwLock, inner_inner);
171                            }
172                            ("Option", WrapperKind::TokioArcMutex) => {
173                                return (WrapperKind::OptionTokioArcMutex, inner_inner);
174                            }
175                            ("Option", WrapperKind::TokioArcRwLock) => {
176                                return (WrapperKind::OptionTokioArcRwLock, inner_inner);
177                            }
178                            ("Box", WrapperKind::Option) => {
179                                return (WrapperKind::BoxOption, inner_inner);
180                            }
181                            ("Rc", WrapperKind::Option) => {
182                                return (WrapperKind::RcOption, inner_inner);
183                            }
184                            ("Arc", WrapperKind::Option) => {
185                                return (WrapperKind::ArcOption, inner_inner);
186                            }
187                            ("Vec", WrapperKind::Option) => {
188                                return (WrapperKind::VecOption, inner_inner);
189                            }
190                            ("HashMap", WrapperKind::Option) => {
191                                return (WrapperKind::HashMapOption, inner_inner);
192                            }
193                            // std::sync variants (when inner is StdMutex/StdRwLock)
194                            ("Arc", WrapperKind::StdMutex) => {
195                                return (WrapperKind::StdArcMutex, inner_inner);
196                            }
197                            ("Arc", WrapperKind::StdRwLock) => {
198                                return (WrapperKind::StdArcRwLock, inner_inner);
199                            }
200                            // parking_lot variants (default - when inner is Mutex/RwLock without std::sync prefix)
201                            ("Arc", WrapperKind::Mutex) => {
202                                return (WrapperKind::ArcMutex, inner_inner);
203                            }
204                            ("Arc", WrapperKind::RwLock) => {
205                                return (WrapperKind::ArcRwLock, inner_inner);
206                            }
207                            // tokio::sync variants (when inner is TokioMutex/TokioRwLock)
208                            ("Arc", WrapperKind::TokioMutex) => {
209                                return (WrapperKind::TokioArcMutex, inner_inner);
210                            }
211                            ("Arc", WrapperKind::TokioRwLock) => {
212                                return (WrapperKind::TokioArcRwLock, inner_inner);
213                            }
214                            _ => {
215                                // Handle single-level containers
216                                // For Mutex and RwLock:
217                                // - If path contains std::sync, it's std::sync (StdMutex/StdRwLock)
218                                // - Otherwise, default to parking_lot (Mutex/RwLock)
219                                return match ident_str.as_str() {
220                                    "Option" => (WrapperKind::Option, Some(inner.clone())),
221                                    "Box" => (WrapperKind::Box, Some(inner.clone())),
222                                    "Rc" => (WrapperKind::Rc, Some(inner.clone())),
223                                    "Arc" => (WrapperKind::Arc, Some(inner.clone())),
224                                    "Vec" => (WrapperKind::Vec, Some(inner.clone())),
225                                    "HashSet" => (WrapperKind::HashSet, Some(inner.clone())),
226                                    "BTreeSet" => (WrapperKind::BTreeSet, Some(inner.clone())),
227                                    "VecDeque" => (WrapperKind::VecDeque, Some(inner.clone())),
228                                    "LinkedList" => (WrapperKind::LinkedList, Some(inner.clone())),
229                                    "BinaryHeap" => (WrapperKind::BinaryHeap, Some(inner.clone())),
230                                    "Result" => (WrapperKind::Result, Some(inner.clone())),
231                                    // For std::sync::Mutex and std::sync::RwLock, use Std variants
232                                    "Mutex" if is_std_sync => {
233                                        (WrapperKind::StdMutex, Some(inner.clone()))
234                                    }
235                                    "RwLock" if is_std_sync => {
236                                        (WrapperKind::StdRwLock, Some(inner.clone()))
237                                    }
238                                    // For tokio::sync::Mutex and tokio::sync::RwLock, use Tokio variants
239                                    "Mutex" if is_tokio_sync => {
240                                        (WrapperKind::TokioMutex, Some(inner.clone()))
241                                    }
242                                    "RwLock" if is_tokio_sync => {
243                                        (WrapperKind::TokioRwLock, Some(inner.clone()))
244                                    }
245                                    // Default: parking_lot (no std::sync or tokio::sync prefix)
246                                    "Mutex" => (WrapperKind::Mutex, Some(inner.clone())),
247                                    "RwLock" => (WrapperKind::RwLock, Some(inner.clone())),
248                                    "Weak" => (WrapperKind::Weak, Some(inner.clone())),
249                                    "Tagged" => (WrapperKind::Tagged, Some(inner.clone())),
250                                    _ => (WrapperKind::None, None),
251                                };
252                            }
253                        }
254                    }
255                }
256            }
257        }
258    }
259    (WrapperKind::None, None)
260}
261
262/// For HashMap<K,V> or BTreeMap<K,V>, returns Some((key_ty, value_ty)).
263fn extract_map_key_value(ty: &Type) -> Option<(Type, Type)> {
264    use syn::{GenericArgument, PathArguments};
265
266    if let Type::Path(tp) = ty {
267        if let Some(seg) = tp.path.segments.last() {
268            let ident_str = seg.ident.to_string();
269            if ident_str == "HashMap" || ident_str == "BTreeMap" {
270                if let PathArguments::AngleBracketed(ab) = &seg.arguments {
271                    let args: Vec<_> = ab.args.iter().collect();
272                    if let (Some(key_arg), Some(value_arg)) = (args.get(0), args.get(1)) {
273                        if let (GenericArgument::Type(key_ty), GenericArgument::Type(value_ty)) =
274                            (key_arg, value_arg)
275                        {
276                            return Some((key_ty.clone(), value_ty.clone()));
277                        }
278                    }
279                }
280            }
281        }
282    }
283    None
284}
285
286fn to_snake_case(name: &str) -> String {
287    let mut out = String::new();
288    for (i, c) in name.chars().enumerate() {
289        if c.is_uppercase() {
290            if i != 0 {
291                out.push('_');
292            }
293            out.push(c.to_ascii_lowercase());
294        } else {
295            out.push(c);
296        }
297    }
298    out
299}
300
301/// Derive macro for generating simple keypath methods.
302/// 
303/// Generates one method per field: `StructName::field_name()` that returns a `Kp`.
304/// Intelligently handles wrapper types (Option, Vec, Box, Arc, etc.) to generate appropriate keypaths.
305/// 
306/// # Example
307/// 
308/// ```ignore
309/// #[derive(Kp)]
310/// struct Person {
311///     name: String,
312///     age: i32,
313///     email: Option<String>,
314///     addresses: Vec<String>,
315/// }
316/// 
317/// // Generates:
318/// // impl Person {
319/// //     pub fn name() -> Kp<...> { ... }
320/// //     pub fn age() -> Kp<...> { ... }
321/// //     pub fn email() -> Kp<...> { ... } // unwraps Option
322/// //     pub fn addresses() -> Kp<...> { ... } // accesses first element
323/// // }
324/// ```
325#[proc_macro_derive(Kp)]
326pub fn derive_keypaths(input: TokenStream) -> TokenStream {
327    let input = parse_macro_input!(input as DeriveInput);
328    let name = &input.ident;
329    let input_span = input.span();
330
331    let methods = match input.data {
332        Data::Struct(data_struct) => match data_struct.fields {
333            Fields::Named(fields_named) => {
334                let mut tokens = proc_macro2::TokenStream::new();
335
336                // Generate identity methods for the struct
337                tokens.extend(quote! {
338                    /// Returns a generic identity keypath for this type
339                    pub fn identity_typed<Root, MutRoot>() -> rust_key_paths::Kp<
340                        #name,
341                        #name,
342                        Root,
343                        Root,
344                        MutRoot,
345                        MutRoot,
346                        fn(Root) -> Option<Root>,
347                        fn(MutRoot) -> Option<MutRoot>,
348                    >
349                    where
350                        Root: std::borrow::Borrow<#name>,
351                        MutRoot: std::borrow::BorrowMut<#name>,
352                    {
353                        rust_key_paths::Kp::new(
354                            |r: Root| Some(r),
355                            |r: MutRoot| Some(r)
356                        )
357                    }
358
359                    /// Returns a simple identity keypath for this type
360                    pub fn identity() -> rust_key_paths::KpType<'static, #name, #name> {
361                        rust_key_paths::Kp::new(
362                            |r: &#name| Some(r),
363                            |r: &mut #name| Some(r)
364                        )
365                    }
366                });
367                
368                for field in fields_named.named.iter() {
369                    let field_ident = field.ident.as_ref().unwrap();
370                    let ty = &field.ty;
371                    // Centralized keypath method names – change here to adjust for all types
372                    let kp_fn = format_ident!("{}", field_ident);
373                    let kp_at_fn = format_ident!("{}_at", field_ident);
374
375                    let (kind, inner_ty) = extract_wrapper_inner_type(ty);
376
377                    match (kind, inner_ty.clone()) {
378                        (WrapperKind::Option, Some(inner_ty)) => {
379                            // For Option<T>, unwrap and access inner type
380                            tokens.extend(quote! {
381                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
382                                    rust_key_paths::Kp::new(
383                                        |root: &#name| root.#field_ident.as_ref(),
384                                        |root: &mut #name| root.#field_ident.as_mut(),
385                                    )
386                                }
387                            });
388                        }
389                        (WrapperKind::Vec, Some(inner_ty)) => {
390                            tokens.extend(quote! {
391                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
392                                    rust_key_paths::Kp::new(
393                                        |root: &#name| Some(&root.#field_ident),
394                                        |root: &mut #name| Some(&mut root.#field_ident),
395                                    )
396                                }
397                                pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
398                                    rust_key_paths::Kp::new(
399                                        Box::new(move |root: &#name| root.#field_ident.get(index)),
400                                        Box::new(move |root: &mut #name| root.#field_ident.get_mut(index)),
401                                    )
402                                }
403                            });
404                        }
405                        (WrapperKind::HashMap, Some(inner_ty)) => {
406                            if let Some((key_ty, _)) = extract_map_key_value(ty) {
407                                tokens.extend(quote! {
408                                    pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
409                                        rust_key_paths::Kp::new(
410                                            |root: &#name| Some(&root.#field_ident),
411                                            |root: &mut #name| Some(&mut root.#field_ident),
412                                        )
413                                    }
414                                    pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
415                                    where
416                                        #key_ty: Clone + std::hash::Hash + Eq + 'static,
417                                    {
418                                        let key2 = key.clone();
419                                        rust_key_paths::Kp::new(
420                                            Box::new(move |root: &#name| root.#field_ident.get(&key)),
421                                            Box::new(move |root: &mut #name| root.#field_ident.get_mut(&key2)),
422                                        )
423                                    }
424                                });
425                            } else {
426                                tokens.extend(quote! {
427                                    pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
428                                        rust_key_paths::Kp::new(
429                                            |root: &#name| Some(&root.#field_ident),
430                                            |root: &mut #name| Some(&mut root.#field_ident),
431                                        )
432                                    }
433                                });
434                            }
435                        }
436                        (WrapperKind::BTreeMap, Some(inner_ty)) => {
437                            if let Some((key_ty, _)) = extract_map_key_value(ty) {
438                                tokens.extend(quote! {
439                                    pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
440                                        rust_key_paths::Kp::new(
441                                            |root: &#name| Some(&root.#field_ident),
442                                            |root: &mut #name| Some(&mut root.#field_ident),
443                                        )
444                                    }
445                                    pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
446                                    where
447                                        #key_ty: Clone + Ord + 'static,
448                                    {
449                                        let key2 = key.clone();
450                                        rust_key_paths::Kp::new(
451                                            Box::new(move |root: &#name| root.#field_ident.get(&key)),
452                                            Box::new(move |root: &mut #name| root.#field_ident.get_mut(&key2)),
453                                        )
454                                    }
455                                });
456                            } else {
457                                tokens.extend(quote! {
458                                    pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
459                                        rust_key_paths::Kp::new(
460                                            |root: &#name| Some(&root.#field_ident),
461                                            |root: &mut #name| Some(&mut root.#field_ident),
462                                        )
463                                    }
464                                });
465                            }
466                        }
467                        (WrapperKind::Box, Some(inner_ty)) => {
468                            // For Box<T>, deref to inner type (returns &T / &mut T, not &Box<T>)
469                            // Matches reference: WritableKeyPath::new(|s: &mut #name| &mut *s.#field_ident)
470                            tokens.extend(quote! {
471                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_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                            });
478                        }
479                        (WrapperKind::Rc, Some(inner_ty)) => {
480                            // For Rc<T>, deref to inner type (returns &T; get_mut when uniquely owned)
481                            tokens.extend(quote! {
482                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
483                                    rust_key_paths::Kp::new(
484                                        |root: &#name| Some(root.#field_ident.as_ref()),
485                                        |root: &mut #name| std::rc::Rc::get_mut(&mut root.#field_ident),
486                                    )
487                                }
488                            });
489                        }
490                        (WrapperKind::Arc, Some(inner_ty)) => {
491                            // For Arc<T>, deref to inner type (returns &T; get_mut when uniquely owned)
492                            tokens.extend(quote! {
493                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
494                                    rust_key_paths::Kp::new(
495                                        |root: &#name| Some(root.#field_ident.as_ref()),
496                                        |root: &mut #name| std::sync::Arc::get_mut(&mut root.#field_ident),
497                                    )
498                                }
499                            });
500                        }
501                        (WrapperKind::HashSet, Some(_inner_ty)) => {
502                            tokens.extend(quote! {
503                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
504                                    rust_key_paths::Kp::new(
505                                        |root: &#name| Some(&root.#field_ident),
506                                        |root: &mut #name| Some(&mut root.#field_ident),
507                                    )
508                                }
509                            });
510                        }
511                        (WrapperKind::BTreeSet, Some(_inner_ty)) => {
512                            tokens.extend(quote! {
513                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
514                                    rust_key_paths::Kp::new(
515                                        |root: &#name| Some(&root.#field_ident),
516                                        |root: &mut #name| Some(&mut root.#field_ident),
517                                    )
518                                }
519                            });
520                        }
521                        (WrapperKind::VecDeque, Some(inner_ty)) => {
522                            tokens.extend(quote! {
523                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
524                                    rust_key_paths::Kp::new(
525                                        |root: &#name| Some(&root.#field_ident),
526                                        |root: &mut #name| Some(&mut root.#field_ident),
527                                    )
528                                }
529                                pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
530                                    rust_key_paths::Kp::new(
531                                        Box::new(move |root: &#name| root.#field_ident.get(index)),
532                                        Box::new(move |root: &mut #name| root.#field_ident.get_mut(index)),
533                                    )
534                                }
535                            });
536                        }
537                        (WrapperKind::LinkedList, Some(_inner_ty)) => {
538                            tokens.extend(quote! {
539                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
540                                    rust_key_paths::Kp::new(
541                                        |root: &#name| Some(&root.#field_ident),
542                                        |root: &mut #name| Some(&mut root.#field_ident),
543                                    )
544                                }
545                            });
546                        }
547                        (WrapperKind::BinaryHeap, Some(_inner_ty)) => {
548                            tokens.extend(quote! {
549                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
550                                    rust_key_paths::Kp::new(
551                                        |root: &#name| Some(&root.#field_ident),
552                                        |root: &mut #name| Some(&mut root.#field_ident),
553                                    )
554                                }
555                            });
556                        }
557                        (WrapperKind::Result, Some(inner_ty)) => {
558                            // For Result<T, E>, access Ok value
559                            tokens.extend(quote! {
560                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
561                                    rust_key_paths::Kp::new(
562                                        |root: &#name| root.#field_ident.as_ref().ok(),
563                                        |root: &mut #name| root.#field_ident.as_mut().ok(),
564                                    )
565                                }
566                            });
567                        }
568                        (WrapperKind::StdArcMutex, Some(inner_ty)) => {
569                            // For Arc<std::sync::Mutex<T>>
570                            let kp_lock_fn = format_ident!("{}_lock", field_ident);
571                            tokens.extend(quote! {
572                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
573                                    rust_key_paths::Kp::new(
574                                        |root: &#name| Some(&root.#field_ident),
575                                        |root: &mut #name| Some(&mut root.#field_ident),
576                                    )
577                                }
578                                pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcMutexFor<#name, #ty, #inner_ty> {
579                                    rust_key_paths::lock::LockKp::new(
580                                        rust_key_paths::Kp::new(
581                                            |root: &#name| Some(&root.#field_ident),
582                                            |root: &mut #name| Some(&mut root.#field_ident),
583                                        ),
584                                        rust_key_paths::lock::ArcMutexAccess::new(),
585                                        rust_key_paths::Kp::new(
586                                            |v: &#inner_ty| Some(v),
587                                            |v: &mut #inner_ty| Some(v),
588                                        ),
589                                    )
590                                }
591                            });
592                        }
593                        (WrapperKind::StdArcRwLock, Some(inner_ty)) => {
594                            // For Arc<std::sync::RwLock<T>>
595                            let kp_lock_fn = format_ident!("{}_lock", field_ident);
596                            tokens.extend(quote! {
597                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
598                                    rust_key_paths::Kp::new(
599                                        |root: &#name| Some(&root.#field_ident),
600                                        |root: &mut #name| Some(&mut root.#field_ident),
601                                    )
602                                }
603                                pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcRwLockFor<#name, #ty, #inner_ty> {
604                                    rust_key_paths::lock::LockKp::new(
605                                        rust_key_paths::Kp::new(
606                                            |root: &#name| Some(&root.#field_ident),
607                                            |root: &mut #name| Some(&mut root.#field_ident),
608                                        ),
609                                        rust_key_paths::lock::ArcRwLockAccess::new(),
610                                        rust_key_paths::Kp::new(
611                                            |v: &#inner_ty| Some(v),
612                                            |v: &mut #inner_ty| Some(v),
613                                        ),
614                                    )
615                                }
616                            });
617                        }
618                        (WrapperKind::Mutex, Some(_inner_ty))
619                        | (WrapperKind::StdMutex, Some(_inner_ty)) => {
620                            // For Mutex<T>, return keypath to container
621                            tokens.extend(quote! {
622                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
623                                    rust_key_paths::Kp::new(
624                                        |root: &#name| Some(&root.#field_ident),
625                                        |root: &mut #name| Some(&mut root.#field_ident),
626                                    )
627                                }
628                            });
629                        }
630                        (WrapperKind::RwLock, Some(_inner_ty))
631                        | (WrapperKind::StdRwLock, Some(_inner_ty)) => {
632                            // For RwLock<T>, return keypath to container
633                            tokens.extend(quote! {
634                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
635                                    rust_key_paths::Kp::new(
636                                        |root: &#name| Some(&root.#field_ident),
637                                        |root: &mut #name| Some(&mut root.#field_ident),
638                                    )
639                                }
640                            });
641                        }
642                        (WrapperKind::TokioArcMutex, Some(inner_ty)) => {
643                            let kp_async_fn = format_ident!("{}_async", field_ident);
644                            tokens.extend(quote! {
645                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
646                                    rust_key_paths::Kp::new(
647                                        |root: &#name| Some(&root.#field_ident),
648                                        |root: &mut #name| Some(&mut root.#field_ident),
649                                    )
650                                }
651                                pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, #ty, #inner_ty> {
652                                    rust_key_paths::async_lock::AsyncLockKp::new(
653                                        rust_key_paths::Kp::new(
654                                            |root: &#name| Some(&root.#field_ident),
655                                            |root: &mut #name| Some(&mut root.#field_ident),
656                                        ),
657                                        rust_key_paths::async_lock::TokioMutexAccess::new(),
658                                        rust_key_paths::Kp::new(
659                                            |v: &#inner_ty| Some(v),
660                                            |v: &mut #inner_ty| Some(v),
661                                        ),
662                                    )
663                                }
664                            });
665                        }
666                        (WrapperKind::TokioArcRwLock, Some(inner_ty)) => {
667                            let kp_async_fn = format_ident!("{}_async", field_ident);
668                            tokens.extend(quote! {
669                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
670                                    rust_key_paths::Kp::new(
671                                        |root: &#name| Some(&root.#field_ident),
672                                        |root: &mut #name| Some(&mut root.#field_ident),
673                                    )
674                                }
675                                pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, #ty, #inner_ty> {
676                                    rust_key_paths::async_lock::AsyncLockKp::new(
677                                        rust_key_paths::Kp::new(
678                                            |root: &#name| Some(&root.#field_ident),
679                                            |root: &mut #name| Some(&mut root.#field_ident),
680                                        ),
681                                        rust_key_paths::async_lock::TokioRwLockAccess::new(),
682                                        rust_key_paths::Kp::new(
683                                            |v: &#inner_ty| Some(v),
684                                            |v: &mut #inner_ty| Some(v),
685                                        ),
686                                    )
687                                }
688                            });
689                        }
690                        (WrapperKind::OptionTokioArcMutex, Some(inner_ty)) => {
691                            let kp_async_fn = format_ident!("{}_async", field_ident);
692                            tokens.extend(quote! {
693                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
694                                    rust_key_paths::Kp::new(
695                                        |root: &#name| Some(&root.#field_ident),
696                                        |root: &mut #name| Some(&mut root.#field_ident),
697                                    )
698                                }
699                                pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, std::sync::Arc<tokio::sync::Mutex<#inner_ty>>, #inner_ty> {
700                                    rust_key_paths::async_lock::AsyncLockKp::new(
701                                        rust_key_paths::Kp::new(
702                                            |root: &#name| root.#field_ident.as_ref(),
703                                            |root: &mut #name| root.#field_ident.as_mut(),
704                                        ),
705                                        rust_key_paths::async_lock::TokioMutexAccess::new(),
706                                        rust_key_paths::Kp::new(
707                                            |v: &#inner_ty| Some(v),
708                                            |v: &mut #inner_ty| Some(v),
709                                        ),
710                                    )
711                                }
712                            });
713                        }
714                        (WrapperKind::OptionTokioArcRwLock, Some(inner_ty)) => {
715                            let kp_async_fn = format_ident!("{}_async", field_ident);
716                            tokens.extend(quote! {
717                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
718                                    rust_key_paths::Kp::new(
719                                        |root: &#name| Some(&root.#field_ident),
720                                        |root: &mut #name| Some(&mut root.#field_ident),
721                                    )
722                                }
723                                pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, std::sync::Arc<tokio::sync::RwLock<#inner_ty>>, #inner_ty> {
724                                    rust_key_paths::async_lock::AsyncLockKp::new(
725                                        rust_key_paths::Kp::new(
726                                            |root: &#name| root.#field_ident.as_ref(),
727                                            |root: &mut #name| root.#field_ident.as_mut(),
728                                        ),
729                                        rust_key_paths::async_lock::TokioRwLockAccess::new(),
730                                        rust_key_paths::Kp::new(
731                                            |v: &#inner_ty| Some(v),
732                                            |v: &mut #inner_ty| Some(v),
733                                        ),
734                                    )
735                                }
736                            });
737                        }
738                        (WrapperKind::OptionStdArcMutex, Some(inner_ty))
739                        | (WrapperKind::OptionArcMutex, Some(inner_ty)) => {
740                            let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
741                            let kp_lock_fn = format_ident!("{}_lock", field_ident);
742                            tokens.extend(quote! {
743                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
744                                    rust_key_paths::Kp::new(
745                                        |root: &#name| Some(&root.#field_ident),
746                                        |root: &mut #name| Some(&mut root.#field_ident),
747                                    )
748                                }
749                                pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::Mutex<#inner_ty>>> {
750                                    rust_key_paths::Kp::new(
751                                        |root: &#name| root.#field_ident.as_ref(),
752                                        |root: &mut #name| root.#field_ident.as_mut(),
753                                    )
754                                }
755                                pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcMutexFor<#name, std::sync::Arc<std::sync::Mutex<#inner_ty>>, #inner_ty> {
756                                    rust_key_paths::lock::LockKp::new(
757                                        rust_key_paths::Kp::new(
758                                            |root: &#name| root.#field_ident.as_ref(),
759                                            |root: &mut #name| root.#field_ident.as_mut(),
760                                        ),
761                                        rust_key_paths::lock::ArcMutexAccess::new(),
762                                        rust_key_paths::Kp::new(
763                                            |v: &#inner_ty| Some(v),
764                                            |v: &mut #inner_ty| Some(v),
765                                        ),
766                                    )
767                                }
768                            });
769                        }
770                        (WrapperKind::OptionStdArcRwLock, Some(inner_ty))
771                        | (WrapperKind::OptionArcRwLock, Some(inner_ty)) => {
772                            let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
773                            let kp_lock_fn = format_ident!("{}_lock", field_ident);
774                            tokens.extend(quote! {
775                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
776                                    rust_key_paths::Kp::new(
777                                        |root: &#name| Some(&root.#field_ident),
778                                        |root: &mut #name| Some(&mut root.#field_ident),
779                                    )
780                                }
781                                pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::RwLock<#inner_ty>>> {
782                                    rust_key_paths::Kp::new(
783                                        |root: &#name| root.#field_ident.as_ref(),
784                                        |root: &mut #name| root.#field_ident.as_mut(),
785                                    )
786                                }
787                                pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcRwLockFor<#name, std::sync::Arc<std::sync::RwLock<#inner_ty>>, #inner_ty> {
788                                    rust_key_paths::lock::LockKp::new(
789                                        rust_key_paths::Kp::new(
790                                            |root: &#name| root.#field_ident.as_ref(),
791                                            |root: &mut #name| root.#field_ident.as_mut(),
792                                        ),
793                                        rust_key_paths::lock::ArcRwLockAccess::new(),
794                                        rust_key_paths::Kp::new(
795                                            |v: &#inner_ty| Some(v),
796                                            |v: &mut #inner_ty| Some(v),
797                                        ),
798                                    )
799                                }
800                            });
801                        }
802                        (WrapperKind::OptionStdMutex, Some(inner_ty))
803                        | (WrapperKind::OptionMutex, Some(inner_ty)) => {
804                            let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
805                            tokens.extend(quote! {
806                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
807                                    rust_key_paths::Kp::new(
808                                        |root: &#name| Some(&root.#field_ident),
809                                        |root: &mut #name| Some(&mut root.#field_ident),
810                                    )
811                                }
812                                pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Mutex<#inner_ty>> {
813                                    rust_key_paths::Kp::new(
814                                        |root: &#name| root.#field_ident.as_ref(),
815                                        |root: &mut #name| root.#field_ident.as_mut(),
816                                    )
817                                }
818                            });
819                        }
820                        (WrapperKind::OptionStdRwLock, Some(inner_ty))
821                        | (WrapperKind::OptionRwLock, Some(inner_ty)) => {
822                            let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
823                            tokens.extend(quote! {
824                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
825                                    rust_key_paths::Kp::new(
826                                        |root: &#name| Some(&root.#field_ident),
827                                        |root: &mut #name| Some(&mut root.#field_ident),
828                                    )
829                                }
830                                pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::RwLock<#inner_ty>> {
831                                    rust_key_paths::Kp::new(
832                                        |root: &#name| root.#field_ident.as_ref(),
833                                        |root: &mut #name| root.#field_ident.as_mut(),
834                                    )
835                                }
836                            });
837                        }
838                        (WrapperKind::Weak, Some(_inner_ty)) => {
839                            // For Weak<T>, return keypath to container
840                            tokens.extend(quote! {
841                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
842                                    rust_key_paths::Kp::new(
843                                        |root: &#name| Some(&root.#field_ident),
844                                        |_root: &mut #name| None, // Weak doesn't support mutable access
845                                    )
846                                }
847                            });
848                        }
849                        (WrapperKind::None, None) => {
850                            // For basic types, direct access
851                            tokens.extend(quote! {
852                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
853                                    rust_key_paths::Kp::new(
854                                        |root: &#name| Some(&root.#field_ident),
855                                        |root: &mut #name| Some(&mut root.#field_ident),
856                                    )
857                                }
858                            });
859                        }
860                        _ => {
861                            // For unknown/complex nested types, return keypath to field itself
862                            tokens.extend(quote! {
863                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
864                            rust_key_paths::Kp::new(
865                                |root: &#name| Some(&root.#field_ident),
866                                |root: &mut #name| Some(&mut root.#field_ident),
867                            )
868                        }
869                            });
870                        }
871                    }
872                }
873                
874                tokens
875            }
876            Fields::Unnamed(unnamed) => {
877                let mut tokens = proc_macro2::TokenStream::new();
878
879                // Generate identity methods for the tuple struct
880                tokens.extend(quote! {
881                    /// Returns a generic identity keypath for this type
882                    pub fn identity_typed<Root, MutRoot>() -> rust_key_paths::Kp<
883                        #name,
884                        #name,
885                        Root,
886                        Root,
887                        MutRoot,
888                        MutRoot,
889                        fn(Root) -> Option<Root>,
890                        fn(MutRoot) -> Option<MutRoot>,
891                    >
892                    where
893                        Root: std::borrow::Borrow<#name>,
894                        MutRoot: std::borrow::BorrowMut<#name>,
895                    {
896                        rust_key_paths::Kp::new(
897                            |r: Root| Some(r),
898                            |r: MutRoot| Some(r)
899                        )
900                    }
901
902                    /// Returns a simple identity keypath for this type
903                    pub fn identity() -> rust_key_paths::KpType<'static, #name, #name> {
904                        rust_key_paths::Kp::new(
905                            |r: &#name| Some(r),
906                            |r: &mut #name| Some(r)
907                        )
908                    }
909                });
910
911                for (idx, field) in unnamed.unnamed.iter().enumerate() {
912                    let idx_lit = syn::Index::from(idx);
913                    let ty = &field.ty;
914                    // Centralized keypath method names for tuple fields – change here to adjust for all types
915                    let kp_fn = format_ident!("f{}", idx);
916                    let kp_at_fn = format_ident!("f{}_at", idx);
917
918                    let (kind, inner_ty) = extract_wrapper_inner_type(ty);
919
920                    match (kind, inner_ty.clone()) {
921                        (WrapperKind::Option, Some(inner_ty)) => {
922                            tokens.extend(quote! {
923                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
924                                    rust_key_paths::Kp::new(
925                                        |root: &#name| root.#idx_lit.as_ref(),
926                                        |root: &mut #name| root.#idx_lit.as_mut(),
927                                    )
928                                }
929                            });
930                        }
931                        (WrapperKind::Vec, Some(inner_ty)) => {
932                            tokens.extend(quote! {
933                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
934                                    rust_key_paths::Kp::new(
935                                        |root: &#name| Some(&root.#idx_lit),
936                                        |root: &mut #name| Some(&mut root.#idx_lit),
937                                    )
938                                }
939                                pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
940                                    rust_key_paths::Kp::new(
941                                        Box::new(move |root: &#name| root.#idx_lit.get(index)),
942                                        Box::new(move |root: &mut #name| root.#idx_lit.get_mut(index)),
943                                    )
944                                }
945                            });
946                        }
947                        (WrapperKind::HashMap, Some(inner_ty)) => {
948                            if let Some((key_ty, _)) = extract_map_key_value(ty) {
949                                tokens.extend(quote! {
950                                    pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
951                                        rust_key_paths::Kp::new(
952                                            |root: &#name| Some(&root.#idx_lit),
953                                            |root: &mut #name| Some(&mut root.#idx_lit),
954                                        )
955                                    }
956                                    pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
957                                    where
958                                        #key_ty: Clone + std::hash::Hash + Eq + 'static,
959                                    {
960                                        let key2 = key.clone();
961                                        rust_key_paths::Kp::new(
962                                            Box::new(move |root: &#name| root.#idx_lit.get(&key)),
963                                            Box::new(move |root: &mut #name| root.#idx_lit.get_mut(&key2)),
964                                        )
965                                    }
966                                });
967                            } else {
968                                tokens.extend(quote! {
969                                    pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
970                                        rust_key_paths::Kp::new(
971                                            |root: &#name| Some(&root.#idx_lit),
972                                            |root: &mut #name| Some(&mut root.#idx_lit),
973                                        )
974                                    }
975                                });
976                            }
977                        }
978                        (WrapperKind::BTreeMap, Some(inner_ty)) => {
979                            if let Some((key_ty, _)) = extract_map_key_value(ty) {
980                                tokens.extend(quote! {
981                                    pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
982                                        rust_key_paths::Kp::new(
983                                            |root: &#name| Some(&root.#idx_lit),
984                                            |root: &mut #name| Some(&mut root.#idx_lit),
985                                        )
986                                    }
987                                    pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
988                                    where
989                                        #key_ty: Clone + Ord + 'static,
990                                    {
991                                        let key2 = key.clone();
992                                        rust_key_paths::Kp::new(
993                                            Box::new(move |root: &#name| root.#idx_lit.get(&key)),
994                                            Box::new(move |root: &mut #name| root.#idx_lit.get_mut(&key2)),
995                                        )
996                                    }
997                                });
998                            } else {
999                                tokens.extend(quote! {
1000                                    pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1001                                        rust_key_paths::Kp::new(
1002                                            |root: &#name| Some(&root.#idx_lit),
1003                                            |root: &mut #name| Some(&mut root.#idx_lit),
1004                                        )
1005                                    }
1006                                });
1007                            }
1008                        }
1009                        (WrapperKind::Box, Some(inner_ty)) => {
1010                            // Box: deref to inner (returns &T / &mut T)
1011                            tokens.extend(quote! {
1012                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1013                                    rust_key_paths::Kp::new(
1014                                        |root: &#name| Some(&*root.#idx_lit),
1015                                        |root: &mut #name| Some(&mut *root.#idx_lit),
1016                                    )
1017                                }
1018                            });
1019                        }
1020                        (WrapperKind::Rc, Some(inner_ty)) => {
1021                            tokens.extend(quote! {
1022                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1023                                    rust_key_paths::Kp::new(
1024                                        |root: &#name| Some(root.#idx_lit.as_ref()),
1025                                        |root: &mut #name| std::rc::Rc::get_mut(&mut root.#idx_lit),
1026                                    )
1027                                }
1028                            });
1029                        }
1030                        (WrapperKind::Arc, Some(inner_ty)) => {
1031                            tokens.extend(quote! {
1032                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1033                                    rust_key_paths::Kp::new(
1034                                        |root: &#name| Some(root.#idx_lit.as_ref()),
1035                                        |root: &mut #name| std::sync::Arc::get_mut(&mut root.#idx_lit),
1036                                    )
1037                                }
1038                            });
1039                        }
1040                        (WrapperKind::HashSet, Some(_inner_ty)) => {
1041                            tokens.extend(quote! {
1042                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1043                                    rust_key_paths::Kp::new(
1044                                        |root: &#name| Some(&root.#idx_lit),
1045                                        |root: &mut #name| Some(&mut root.#idx_lit),
1046                                    )
1047                                }
1048                            });
1049                        }
1050                        (WrapperKind::BTreeSet, Some(_inner_ty)) => {
1051                            tokens.extend(quote! {
1052                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1053                                    rust_key_paths::Kp::new(
1054                                        |root: &#name| Some(&root.#idx_lit),
1055                                        |root: &mut #name| Some(&mut root.#idx_lit),
1056                                    )
1057                                }
1058                            });
1059                        }
1060                        (WrapperKind::VecDeque, Some(inner_ty)) => {
1061                            tokens.extend(quote! {
1062                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1063                                    rust_key_paths::Kp::new(
1064                                        |root: &#name| Some(&root.#idx_lit),
1065                                        |root: &mut #name| Some(&mut root.#idx_lit),
1066                                    )
1067                                }
1068                                pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
1069                                    rust_key_paths::Kp::new(
1070                                        Box::new(move |root: &#name| root.#idx_lit.get(index)),
1071                                        Box::new(move |root: &mut #name| root.#idx_lit.get_mut(index)),
1072                                    )
1073                                }
1074                            });
1075                        }
1076                        (WrapperKind::LinkedList, Some(_inner_ty)) => {
1077                            tokens.extend(quote! {
1078                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1079                                    rust_key_paths::Kp::new(
1080                                        |root: &#name| Some(&root.#idx_lit),
1081                                        |root: &mut #name| Some(&mut root.#idx_lit),
1082                                    )
1083                                }
1084                            });
1085                        }
1086                        (WrapperKind::BinaryHeap, Some(_inner_ty)) => {
1087                            tokens.extend(quote! {
1088                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1089                                    rust_key_paths::Kp::new(
1090                                        |root: &#name| Some(&root.#idx_lit),
1091                                        |root: &mut #name| Some(&mut root.#idx_lit),
1092                                    )
1093                                }
1094                            });
1095                        }
1096                        (WrapperKind::Result, Some(inner_ty)) => {
1097                            tokens.extend(quote! {
1098                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1099                                    rust_key_paths::Kp::new(
1100                                        |root: &#name| root.#idx_lit.as_ref().ok(),
1101                                        |root: &mut #name| root.#idx_lit.as_mut().ok(),
1102                                    )
1103                                }
1104                            });
1105                        }
1106                        (WrapperKind::Mutex, Some(_inner_ty))
1107                        | (WrapperKind::StdMutex, Some(_inner_ty)) => {
1108                            tokens.extend(quote! {
1109                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1110                                    rust_key_paths::Kp::new(
1111                                        |root: &#name| Some(&root.#idx_lit),
1112                                        |root: &mut #name| Some(&mut root.#idx_lit),
1113                                    )
1114                                }
1115                            });
1116                        }
1117                        (WrapperKind::RwLock, Some(_inner_ty))
1118                        | (WrapperKind::StdRwLock, Some(_inner_ty)) => {
1119                            tokens.extend(quote! {
1120                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1121                                    rust_key_paths::Kp::new(
1122                                        |root: &#name| Some(&root.#idx_lit),
1123                                        |root: &mut #name| Some(&mut root.#idx_lit),
1124                                    )
1125                                }
1126                            });
1127                        }
1128                        (WrapperKind::TokioArcMutex, Some(inner_ty)) => {
1129                            let kp_async_fn = format_ident!("f{}_async", idx);
1130                            tokens.extend(quote! {
1131                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1132                                    rust_key_paths::Kp::new(
1133                                        |root: &#name| Some(&root.#idx_lit),
1134                                        |root: &mut #name| Some(&mut root.#idx_lit),
1135                                    )
1136                                }
1137                                pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, #ty, #inner_ty> {
1138                                    rust_key_paths::async_lock::AsyncLockKp::new(
1139                                        rust_key_paths::Kp::new(
1140                                            |root: &#name| Some(&root.#idx_lit),
1141                                            |root: &mut #name| Some(&mut root.#idx_lit),
1142                                        ),
1143                                        rust_key_paths::async_lock::TokioMutexAccess::new(),
1144                                        rust_key_paths::Kp::new(
1145                                            |v: &#inner_ty| Some(v),
1146                                            |v: &mut #inner_ty| Some(v),
1147                                        ),
1148                                    )
1149                                }
1150                            });
1151                        }
1152                        (WrapperKind::TokioArcRwLock, Some(inner_ty)) => {
1153                            let kp_async_fn = format_ident!("f{}_async", idx);
1154                            tokens.extend(quote! {
1155                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1156                                    rust_key_paths::Kp::new(
1157                                        |root: &#name| Some(&root.#idx_lit),
1158                                        |root: &mut #name| Some(&mut root.#idx_lit),
1159                                    )
1160                                }
1161                                pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, #ty, #inner_ty> {
1162                                    rust_key_paths::async_lock::AsyncLockKp::new(
1163                                        rust_key_paths::Kp::new(
1164                                            |root: &#name| Some(&root.#idx_lit),
1165                                            |root: &mut #name| Some(&mut root.#idx_lit),
1166                                        ),
1167                                        rust_key_paths::async_lock::TokioRwLockAccess::new(),
1168                                        rust_key_paths::Kp::new(
1169                                            |v: &#inner_ty| Some(v),
1170                                            |v: &mut #inner_ty| Some(v),
1171                                        ),
1172                                    )
1173                                }
1174                            });
1175                        }
1176                        (WrapperKind::OptionTokioArcMutex, Some(inner_ty)) => {
1177                            let kp_async_fn = format_ident!("f{}_async", idx);
1178                            tokens.extend(quote! {
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                                pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, std::sync::Arc<tokio::sync::Mutex<#inner_ty>>, #inner_ty> {
1186                                    rust_key_paths::async_lock::AsyncLockKp::new(
1187                                        rust_key_paths::Kp::new(
1188                                            |root: &#name| root.#idx_lit.as_ref(),
1189                                            |root: &mut #name| root.#idx_lit.as_mut(),
1190                                        ),
1191                                        rust_key_paths::async_lock::TokioMutexAccess::new(),
1192                                        rust_key_paths::Kp::new(
1193                                            |v: &#inner_ty| Some(v),
1194                                            |v: &mut #inner_ty| Some(v),
1195                                        ),
1196                                    )
1197                                }
1198                            });
1199                        }
1200                        (WrapperKind::OptionTokioArcRwLock, Some(inner_ty)) => {
1201                            let kp_async_fn = format_ident!("f{}_async", idx);
1202                            tokens.extend(quote! {
1203                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1204                                    rust_key_paths::Kp::new(
1205                                        |root: &#name| Some(&root.#idx_lit),
1206                                        |root: &mut #name| Some(&mut root.#idx_lit),
1207                                    )
1208                                }
1209                                pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, std::sync::Arc<tokio::sync::RwLock<#inner_ty>>, #inner_ty> {
1210                                    rust_key_paths::async_lock::AsyncLockKp::new(
1211                                        rust_key_paths::Kp::new(
1212                                            |root: &#name| root.#idx_lit.as_ref(),
1213                                            |root: &mut #name| root.#idx_lit.as_mut(),
1214                                        ),
1215                                        rust_key_paths::async_lock::TokioRwLockAccess::new(),
1216                                        rust_key_paths::Kp::new(
1217                                            |v: &#inner_ty| Some(v),
1218                                            |v: &mut #inner_ty| Some(v),
1219                                        ),
1220                                    )
1221                                }
1222                            });
1223                        }
1224                        (WrapperKind::OptionStdArcMutex, Some(inner_ty))
1225                        | (WrapperKind::OptionArcMutex, Some(inner_ty)) => {
1226                            let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1227                            tokens.extend(quote! {
1228                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1229                                    rust_key_paths::Kp::new(
1230                                        |root: &#name| Some(&root.#idx_lit),
1231                                        |root: &mut #name| Some(&mut root.#idx_lit),
1232                                    )
1233                                }
1234                                pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::Mutex<#inner_ty>>> {
1235                                    rust_key_paths::Kp::new(
1236                                        |root: &#name| root.#idx_lit.as_ref(),
1237                                        |root: &mut #name| root.#idx_lit.as_mut(),
1238                                    )
1239                                }
1240                            });
1241                        }
1242                        (WrapperKind::OptionStdArcRwLock, Some(inner_ty))
1243                        | (WrapperKind::OptionArcRwLock, Some(inner_ty)) => {
1244                            let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1245                            tokens.extend(quote! {
1246                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1247                                    rust_key_paths::Kp::new(
1248                                        |root: &#name| Some(&root.#idx_lit),
1249                                        |root: &mut #name| Some(&mut root.#idx_lit),
1250                                    )
1251                                }
1252                                pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::RwLock<#inner_ty>>> {
1253                                    rust_key_paths::Kp::new(
1254                                        |root: &#name| root.#idx_lit.as_ref(),
1255                                        |root: &mut #name| root.#idx_lit.as_mut(),
1256                                    )
1257                                }
1258                            });
1259                        }
1260                        (WrapperKind::OptionStdMutex, Some(inner_ty))
1261                        | (WrapperKind::OptionMutex, Some(inner_ty)) => {
1262                            let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1263                            tokens.extend(quote! {
1264                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1265                                    rust_key_paths::Kp::new(
1266                                        |root: &#name| Some(&root.#idx_lit),
1267                                        |root: &mut #name| Some(&mut root.#idx_lit),
1268                                    )
1269                                }
1270                                pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Mutex<#inner_ty>> {
1271                                    rust_key_paths::Kp::new(
1272                                        |root: &#name| root.#idx_lit.as_ref(),
1273                                        |root: &mut #name| root.#idx_lit.as_mut(),
1274                                    )
1275                                }
1276                            });
1277                        }
1278                        (WrapperKind::OptionStdRwLock, Some(inner_ty))
1279                        | (WrapperKind::OptionRwLock, Some(inner_ty)) => {
1280                            let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1281                            tokens.extend(quote! {
1282                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1283                                    rust_key_paths::Kp::new(
1284                                        |root: &#name| Some(&root.#idx_lit),
1285                                        |root: &mut #name| Some(&mut root.#idx_lit),
1286                                    )
1287                                }
1288                                pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::RwLock<#inner_ty>> {
1289                                    rust_key_paths::Kp::new(
1290                                        |root: &#name| root.#idx_lit.as_ref(),
1291                                        |root: &mut #name| root.#idx_lit.as_mut(),
1292                                    )
1293                                }
1294                            });
1295                        }
1296                        (WrapperKind::Weak, Some(_inner_ty)) => {
1297                            tokens.extend(quote! {
1298                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1299                                    rust_key_paths::Kp::new(
1300                                        |root: &#name| Some(&root.#idx_lit),
1301                                        |_root: &mut #name| None,
1302                                    )
1303                                }
1304                            });
1305                        }
1306                        (WrapperKind::None, None) => {
1307                            tokens.extend(quote! {
1308                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1309                                    rust_key_paths::Kp::new(
1310                                        |root: &#name| Some(&root.#idx_lit),
1311                                        |root: &mut #name| Some(&mut root.#idx_lit),
1312                                    )
1313                                }
1314                            });
1315                        }
1316                        _ => {
1317                            tokens.extend(quote! {
1318                                pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1319                                    rust_key_paths::Kp::new(
1320                                        |root: &#name| Some(&root.#idx_lit),
1321                                        |root: &mut #name| Some(&mut root.#idx_lit),
1322                                    )
1323                                }
1324                            });
1325                        }
1326                    }
1327                }
1328
1329                tokens
1330            }
1331            Fields::Unit => {
1332                return syn::Error::new(input_span, "Kp derive does not support unit structs")
1333                .to_compile_error()
1334                .into();
1335            }
1336        },
1337        Data::Enum(data_enum) => {
1338            let mut tokens = proc_macro2::TokenStream::new();
1339
1340            // Generate identity methods for the enum
1341            tokens.extend(quote! {
1342                /// Returns a generic identity keypath for this type
1343                pub fn identity_typed<Root, MutRoot>() -> rust_key_paths::Kp<
1344                    #name,
1345                    #name,
1346                    Root,
1347                    Root,
1348                    MutRoot,
1349                    MutRoot,
1350                    fn(Root) -> Option<Root>,
1351                    fn(MutRoot) -> Option<MutRoot>,
1352                >
1353                where
1354                    Root: std::borrow::Borrow<#name>,
1355                    MutRoot: std::borrow::BorrowMut<#name>,
1356                {
1357                    rust_key_paths::Kp::new(
1358                        |r: Root| Some(r),
1359                        |r: MutRoot| Some(r)
1360                    )
1361                }
1362
1363                /// Returns a simple identity keypath for this type
1364                pub fn identity() -> rust_key_paths::KpType<'static, #name, #name> {
1365                    rust_key_paths::Kp::new(
1366                        |r: &#name| Some(r),
1367                        |r: &mut #name| Some(r)
1368                    )
1369                }
1370            });
1371
1372            for variant in data_enum.variants.iter() {
1373                let v_ident = &variant.ident;
1374                let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
1375
1376                match &variant.fields {
1377                    Fields::Unit => {
1378                        // Unit variant - return keypath that checks if enum matches variant
1379                        tokens.extend(quote! {
1380                            pub fn #snake() -> rust_key_paths::KpType<'static, #name, ()> {
1381                                rust_key_paths::Kp::new(
1382                                    |root: &#name| match root {
1383                                        #name::#v_ident => {
1384                                            static UNIT: () = ();
1385                                            Some(&UNIT)
1386                                        },
1387                                        _ => None,
1388                                    },
1389                                    |_root: &mut #name| None, // Can't mutate unit variant
1390                                )
1391                            }
1392                        });
1393                    }
1394                    Fields::Unnamed(unnamed) => {
1395                        if unnamed.unnamed.len() == 1 {
1396                            // Single-field tuple variant
1397                            let field_ty = &unnamed.unnamed[0].ty;
1398                            let (kind, inner_ty) = extract_wrapper_inner_type(field_ty);
1399
1400                            match (kind, inner_ty.clone()) {
1401                                (WrapperKind::Option, Some(inner_ty)) => {
1402                                    tokens.extend(quote! {
1403                                        pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1404                                            rust_key_paths::Kp::new(
1405                                                |root: &#name| match root {
1406                                                    #name::#v_ident(inner) => inner.as_ref(),
1407                                                    _ => None,
1408                                                },
1409                                                |root: &mut #name| match root {
1410                                                    #name::#v_ident(inner) => inner.as_mut(),
1411                                                    _ => None,
1412                                                },
1413                                            )
1414                                        }
1415                                    });
1416                                }
1417                                (WrapperKind::Vec, Some(inner_ty)) => {
1418                                    tokens.extend(quote! {
1419                                        pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1420                                            rust_key_paths::Kp::new(
1421                                                |root: &#name| match root {
1422                                                    #name::#v_ident(inner) => inner.first(),
1423                                                    _ => None,
1424                                                },
1425                                                |root: &mut #name| match root {
1426                                                    #name::#v_ident(inner) => inner.first_mut(),
1427                                                    _ => None,
1428                                                },
1429                                            )
1430                                        }
1431                                    });
1432                                }
1433                                (WrapperKind::Box, Some(inner_ty)) => {
1434                                    // Box in enum: deref to inner (&T / &mut T)
1435                                    tokens.extend(quote! {
1436                                        pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1437                                            rust_key_paths::Kp::new(
1438                                                |root: &#name| match root {
1439                                                    #name::#v_ident(inner) => Some(&**inner),
1440                                                    _ => None,
1441                                                },
1442                                                |root: &mut #name| match root {
1443                                                    #name::#v_ident(inner) => Some(&mut **inner),
1444                                                    _ => None,
1445                                                },
1446                                            )
1447                                        }
1448                                    });
1449                                }
1450                                (WrapperKind::Rc, Some(inner_ty)) => {
1451                                    tokens.extend(quote! {
1452                                        pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1453                                            rust_key_paths::Kp::new(
1454                                                |root: &#name| match root {
1455                                                    #name::#v_ident(inner) => Some(inner.as_ref()),
1456                                                    _ => None,
1457                                                },
1458                                                |root: &mut #name| match root {
1459                                                    #name::#v_ident(inner) => std::rc::Rc::get_mut(inner),
1460                                                    _ => None,
1461                                                },
1462                                            )
1463                                        }
1464                                    });
1465                                }
1466                                (WrapperKind::Arc, Some(inner_ty)) => {
1467                                    tokens.extend(quote! {
1468                                        pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1469                                            rust_key_paths::Kp::new(
1470                                                |root: &#name| match root {
1471                                                    #name::#v_ident(inner) => Some(inner.as_ref()),
1472                                                    _ => None,
1473                                                },
1474                                                |root: &mut #name| match root {
1475                                                    #name::#v_ident(inner) => std::sync::Arc::get_mut(inner),
1476                                                    _ => None,
1477                                                },
1478                                            )
1479                                        }
1480                                    });
1481                                }
1482                                (WrapperKind::None, None) => {
1483                                    // Basic type
1484                                    tokens.extend(quote! {
1485                                        pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
1486                                            rust_key_paths::Kp::new(
1487                                                |root: &#name| match root {
1488                                                    #name::#v_ident(inner) => Some(inner),
1489                                                    _ => None,
1490                                                },
1491                                                |root: &mut #name| match root {
1492                                                    #name::#v_ident(inner) => Some(inner),
1493                                                    _ => None,
1494                                                },
1495                                            )
1496                                        }
1497                                    });
1498                                }
1499                                _ => {
1500                                    // Other wrapper types - return keypath to field
1501                                    tokens.extend(quote! {
1502                                        pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
1503                                            rust_key_paths::Kp::new(
1504                                                |root: &#name| match root {
1505                                                    #name::#v_ident(inner) => Some(inner),
1506                                                    _ => None,
1507                                                },
1508                                                |root: &mut #name| match root {
1509                                                    #name::#v_ident(inner) => Some(inner),
1510                                                    _ => None,
1511                                                },
1512                                            )
1513                                        }
1514                                    });
1515                                }
1516                            }
1517                        } else {
1518                            // Multi-field tuple variant - return keypath to variant itself
1519                            tokens.extend(quote! {
1520                                pub fn #snake() -> rust_key_paths::KpType<'static, #name, #name> {
1521                                    rust_key_paths::Kp::new(
1522                                        |root: &#name| match root {
1523                                            #name::#v_ident(..) => Some(root),
1524                                            _ => None,
1525                                        },
1526                                        |root: &mut #name| match root {
1527                                            #name::#v_ident(..) => Some(root),
1528                                            _ => None,
1529                                        },
1530                                    )
1531                                }
1532                            });
1533                        }
1534                    }
1535                    Fields::Named(_) => {
1536                        // Named field variant - return keypath to variant itself
1537                        tokens.extend(quote! {
1538                            pub fn #snake() -> rust_key_paths::KpType<'static, #name, #name> {
1539                                rust_key_paths::Kp::new(
1540                                    |root: &#name| match root {
1541                                        #name::#v_ident { .. } => Some(root),
1542                                        _ => None,
1543                                    },
1544                                    |root: &mut #name| match root {
1545                                        #name::#v_ident { .. } => Some(root),
1546                                        _ => None,
1547                                    },
1548                                )
1549                            }
1550                        });
1551                    }
1552                }
1553            }
1554
1555            tokens
1556        }
1557        Data::Union(_) => {
1558            return syn::Error::new(input_span, "Kp derive does not support unions")
1559            .to_compile_error()
1560            .into();
1561        }
1562    };
1563
1564    let expanded = quote! {
1565        impl #name {
1566            #methods
1567        }
1568    };
1569
1570    TokenStream::from(expanded)
1571}