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