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}