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 OptionTagged,
70 // Clone-on-write (std::borrow::Cow)
71 Cow,
72 OptionCow,
73 // Reference types (&T, &str, &[T], etc.)
74 Reference,
75 OptionReference,
76 // Atomic types (std::sync::atomic::*)
77 Atomic,
78 OptionAtomic,
79 // Pin types
80 Pin,
81 PinBox,
82 /// Field marked with #[pin] - plain type (pin_project pattern)
83 PinnedField,
84 /// Field marked with #[pin] - Future type (pin_project pattern)
85 PinnedFuture,
86 /// Field marked with #[pin] - Box<dyn Future> (pin_project pattern)
87 PinnedBoxFuture,
88}
89
90/// Helper function to check if a type path includes std::sync module
91fn is_std_sync_type(path: &syn::Path) -> bool {
92 // Check for paths like std::sync::Mutex, std::sync::RwLock
93 let segments: Vec<_> = path.segments.iter().map(|s| s.ident.to_string()).collect();
94 segments.len() >= 2
95 && segments.contains(&"std".to_string())
96 && segments.contains(&"sync".to_string())
97}
98
99/// Helper function to check if a type path includes tokio::sync module
100fn is_tokio_sync_type(path: &syn::Path) -> bool {
101 // Check for paths like tokio::sync::Mutex, tokio::sync::RwLock
102 let segments: Vec<_> = path.segments.iter().map(|s| s.ident.to_string()).collect();
103 segments.len() >= 2
104 && segments.contains(&"tokio".to_string())
105 && segments.contains(&"sync".to_string())
106}
107
108/// Helper function to check if a type path includes std::sync::atomic module
109fn is_std_sync_atomic_type(path: &syn::Path) -> bool {
110 let segments: Vec<_> = path.segments.iter().map(|s| s.ident.to_string()).collect();
111 segments.contains(&"std".to_string())
112 && segments.contains(&"sync".to_string())
113 && segments.contains(&"atomic".to_string())
114}
115
116/// Atomic type idents (no type params): AtomicBool, AtomicI8, etc.
117const ATOMIC_TYPE_IDENTS: &[&str] = &[
118 "AtomicBool", "AtomicI8", "AtomicI16", "AtomicI32", "AtomicI64", "AtomicI128", "AtomicIsize",
119 "AtomicU8", "AtomicU16", "AtomicU32", "AtomicU64", "AtomicU128", "AtomicUsize",
120];
121
122fn extract_wrapper_inner_type(ty: &Type) -> (WrapperKind, Option<Type>) {
123 use syn::{GenericArgument, PathArguments};
124
125 // Handle reference types: &T, &'a str, &[T], etc.
126 if let Type::Reference(tr) = ty {
127 return (WrapperKind::Reference, Some((*tr.elem).clone()));
128 }
129
130 if let Type::Path(tp) = ty {
131 // Check if this is explicitly a std::sync type
132 let is_std_sync = is_std_sync_type(&tp.path);
133 // Check if this is explicitly a tokio::sync type
134 let is_tokio_sync = is_tokio_sync_type(&tp.path);
135
136 if let Some(seg) = tp.path.segments.last() {
137 let ident_str = seg.ident.to_string();
138
139 if let PathArguments::AngleBracketed(ab) = &seg.arguments {
140 let args: Vec<_> = ab.args.iter().collect();
141
142 // Handle map types (HashMap, BTreeMap) - they have K, V parameters
143 if ident_str == "HashMap" || ident_str == "BTreeMap" {
144 if let (Some(_key_arg), Some(value_arg)) = (args.get(0), args.get(1)) {
145 if let GenericArgument::Type(inner) = value_arg {
146 // Check for nested Option in map values
147 let (inner_kind, inner_inner) = extract_wrapper_inner_type(inner);
148 match (ident_str.as_str(), inner_kind) {
149 ("HashMap", WrapperKind::Option) => {
150 return (WrapperKind::HashMapOption, inner_inner);
151 }
152 _ => {
153 return match ident_str.as_str() {
154 "HashMap" => (WrapperKind::HashMap, Some(inner.clone())),
155 "BTreeMap" => (WrapperKind::BTreeMap, Some(inner.clone())),
156 _ => (WrapperKind::None, None),
157 };
158 }
159 }
160 }
161 }
162 }
163 // Handle Cow<'a, B> - has lifetime then type parameter
164 else if ident_str == "Cow" {
165 if let Some(inner) = args.iter().find_map(|arg| {
166 if let GenericArgument::Type(t) = arg {
167 Some(t.clone())
168 } else {
169 None
170 }
171 }) {
172 return (WrapperKind::Cow, Some(inner));
173 }
174 }
175 // Handle single-parameter container types
176 else if let Some(arg) = args.get(0) {
177 if let GenericArgument::Type(inner) = arg {
178 // Check for nested containers first
179 let (inner_kind, inner_inner) = extract_wrapper_inner_type(inner);
180
181 // Handle nested combinations
182 match (ident_str.as_str(), inner_kind) {
183 ("Option", WrapperKind::Box) => {
184 return (WrapperKind::OptionBox, inner_inner);
185 }
186 ("Option", WrapperKind::Rc) => {
187 return (WrapperKind::OptionRc, inner_inner);
188 }
189 ("Option", WrapperKind::Arc) => {
190 return (WrapperKind::OptionArc, inner_inner);
191 }
192 ("Option", WrapperKind::Vec) => {
193 return (WrapperKind::OptionVec, inner_inner);
194 }
195 ("Option", WrapperKind::HashMap) => {
196 return (WrapperKind::OptionHashMap, inner_inner);
197 }
198 ("Option", WrapperKind::StdArcMutex) => {
199 return (WrapperKind::OptionStdArcMutex, inner_inner);
200 }
201 ("Option", WrapperKind::StdArcRwLock) => {
202 return (WrapperKind::OptionStdArcRwLock, inner_inner);
203 }
204 ("Option", WrapperKind::ArcMutex) => {
205 return (WrapperKind::OptionArcMutex, inner_inner);
206 }
207 ("Option", WrapperKind::ArcRwLock) => {
208 return (WrapperKind::OptionArcRwLock, inner_inner);
209 }
210 ("Option", WrapperKind::StdMutex) => {
211 return (WrapperKind::OptionStdMutex, inner_inner);
212 }
213 ("Option", WrapperKind::StdRwLock) => {
214 return (WrapperKind::OptionStdRwLock, inner_inner);
215 }
216 ("Option", WrapperKind::Mutex) => {
217 return (WrapperKind::OptionMutex, inner_inner);
218 }
219 ("Option", WrapperKind::RwLock) => {
220 return (WrapperKind::OptionRwLock, inner_inner);
221 }
222 ("Option", WrapperKind::TokioArcMutex) => {
223 return (WrapperKind::OptionTokioArcMutex, inner_inner);
224 }
225 ("Option", WrapperKind::TokioArcRwLock) => {
226 return (WrapperKind::OptionTokioArcRwLock, inner_inner);
227 }
228 ("Option", WrapperKind::Cow) => {
229 return (WrapperKind::OptionCow, inner_inner);
230 }
231 ("Option", WrapperKind::Tagged) => {
232 return (WrapperKind::OptionTagged, inner_inner);
233 }
234 ("Option", WrapperKind::Reference) => {
235 return (WrapperKind::OptionReference, Some(inner.clone()));
236 }
237 ("Option", WrapperKind::Atomic) => {
238 return (WrapperKind::OptionAtomic, Some(inner.clone()));
239 }
240 ("Pin", WrapperKind::Box) => {
241 return (WrapperKind::PinBox, inner_inner);
242 }
243 ("Box", WrapperKind::Option) => {
244 return (WrapperKind::BoxOption, inner_inner);
245 }
246 ("Rc", WrapperKind::Option) => {
247 return (WrapperKind::RcOption, inner_inner);
248 }
249 ("Arc", WrapperKind::Option) => {
250 return (WrapperKind::ArcOption, inner_inner);
251 }
252 ("Vec", WrapperKind::Option) => {
253 return (WrapperKind::VecOption, inner_inner);
254 }
255 ("HashMap", WrapperKind::Option) => {
256 return (WrapperKind::HashMapOption, inner_inner);
257 }
258 // std::sync variants (when inner is StdMutex/StdRwLock)
259 ("Arc", WrapperKind::StdMutex) => {
260 return (WrapperKind::StdArcMutex, inner_inner);
261 }
262 ("Arc", WrapperKind::StdRwLock) => {
263 return (WrapperKind::StdArcRwLock, inner_inner);
264 }
265 // parking_lot variants (default - when inner is Mutex/RwLock without std::sync prefix)
266 ("Arc", WrapperKind::Mutex) => {
267 return (WrapperKind::ArcMutex, inner_inner);
268 }
269 ("Arc", WrapperKind::RwLock) => {
270 return (WrapperKind::ArcRwLock, inner_inner);
271 }
272 // tokio::sync variants (when inner is TokioMutex/TokioRwLock)
273 ("Arc", WrapperKind::TokioMutex) => {
274 return (WrapperKind::TokioArcMutex, inner_inner);
275 }
276 ("Arc", WrapperKind::TokioRwLock) => {
277 return (WrapperKind::TokioArcRwLock, inner_inner);
278 }
279 _ => {
280 // Handle single-level containers
281 // For Mutex and RwLock:
282 // - If path contains std::sync, it's std::sync (StdMutex/StdRwLock)
283 // - Otherwise, default to parking_lot (Mutex/RwLock)
284 return match ident_str.as_str() {
285 "Option" => (WrapperKind::Option, Some(inner.clone())),
286 "Box" => (WrapperKind::Box, Some(inner.clone())),
287 "Rc" => (WrapperKind::Rc, Some(inner.clone())),
288 "Arc" => (WrapperKind::Arc, Some(inner.clone())),
289 "Vec" => (WrapperKind::Vec, Some(inner.clone())),
290 "HashSet" => (WrapperKind::HashSet, Some(inner.clone())),
291 "BTreeSet" => (WrapperKind::BTreeSet, Some(inner.clone())),
292 "VecDeque" => (WrapperKind::VecDeque, Some(inner.clone())),
293 "LinkedList" => (WrapperKind::LinkedList, Some(inner.clone())),
294 "BinaryHeap" => (WrapperKind::BinaryHeap, Some(inner.clone())),
295 "Result" => (WrapperKind::Result, Some(inner.clone())),
296 // For std::sync::Mutex and std::sync::RwLock, use Std variants
297 "Mutex" if is_std_sync => {
298 (WrapperKind::StdMutex, Some(inner.clone()))
299 }
300 "RwLock" if is_std_sync => {
301 (WrapperKind::StdRwLock, Some(inner.clone()))
302 }
303 // For tokio::sync::Mutex and tokio::sync::RwLock, use Tokio variants
304 "Mutex" if is_tokio_sync => {
305 (WrapperKind::TokioMutex, Some(inner.clone()))
306 }
307 "RwLock" if is_tokio_sync => {
308 (WrapperKind::TokioRwLock, Some(inner.clone()))
309 }
310 // Default: parking_lot (no std::sync or tokio::sync prefix)
311 "Mutex" => (WrapperKind::Mutex, Some(inner.clone())),
312 "RwLock" => (WrapperKind::RwLock, Some(inner.clone())),
313 "Weak" => (WrapperKind::Weak, Some(inner.clone())),
314 "Tagged" => (WrapperKind::Tagged, Some(inner.clone())),
315 "Cow" => (WrapperKind::Cow, Some(inner.clone())),
316 "AtomicPtr" if is_std_sync_atomic_type(&tp.path) => (WrapperKind::Atomic, None),
317 "Pin" => (WrapperKind::Pin, Some(inner.clone())),
318 _ => (WrapperKind::None, None),
319 };
320 }
321 }
322 }
323 }
324 }
325 // Handle atomic types with no angle bracket args (AtomicBool, AtomicI32, etc.)
326 if matches!(seg.arguments, PathArguments::None)
327 && is_std_sync_atomic_type(&tp.path)
328 && ATOMIC_TYPE_IDENTS.contains(&ident_str.as_str())
329 {
330 return (WrapperKind::Atomic, None);
331 }
332 }
333 }
334 (WrapperKind::None, None)
335}
336
337/// Check if a field has the #[pin] attribute (pin_project pattern).
338fn field_has_pin_attr(field: &syn::Field) -> bool {
339 field.attrs.iter().any(|attr| {
340 attr.path().get_ident().map(|i| i == "pin").unwrap_or(false)
341 })
342}
343
344/// Check if a type is a Future (dyn Future, impl Future, or Box<dyn Future>).
345fn is_future_type(ty: &Type) -> bool {
346 use syn::{GenericArgument, PathArguments, TypeParamBound};
347
348 match ty {
349 Type::TraitObject(trait_obj) => trait_obj.bounds.iter().any(|b| {
350 if let TypeParamBound::Trait(t) = b {
351 t.path.segments.last()
352 .map(|s| s.ident == "Future")
353 .unwrap_or(false)
354 } else {
355 false
356 }
357 }),
358 Type::ImplTrait(impl_trait) => impl_trait.bounds.iter().any(|b| {
359 if let TypeParamBound::Trait(t) = b {
360 t.path.segments.last()
361 .map(|s| s.ident == "Future")
362 .unwrap_or(false)
363 } else {
364 false
365 }
366 }),
367 Type::Path(tp) => {
368 if let Some(seg) = tp.path.segments.last() {
369 if seg.ident == "Box" {
370 if let PathArguments::AngleBracketed(args) = &seg.arguments {
371 if let Some(GenericArgument::Type(inner)) = args.args.first() {
372 return is_future_type(inner);
373 }
374 }
375 }
376 }
377 false
378 }
379 _ => false,
380 }
381}
382
383/// Extract Output type from Future trait bound (dyn Future<Output = T>, impl Future<Output = T>, etc.).
384fn extract_future_output(ty: &Type) -> Option<Type> {
385 use syn::{GenericArgument, PathArguments, TypeParamBound};
386
387 let bounds = match ty {
388 Type::TraitObject(t) => &t.bounds,
389 Type::ImplTrait(t) => &t.bounds,
390 Type::Path(tp) => {
391 if let Some(seg) = tp.path.segments.last() {
392 if seg.ident == "Box" {
393 if let PathArguments::AngleBracketed(args) = &seg.arguments {
394 if let Some(GenericArgument::Type(inner)) = args.args.first() {
395 return extract_future_output(inner);
396 }
397 }
398 }
399 }
400 return None;
401 }
402 _ => return None,
403 };
404
405 for bound in bounds {
406 if let TypeParamBound::Trait(trait_bound) = bound {
407 if let Some(seg) = trait_bound.path.segments.last() {
408 if seg.ident == "Future" {
409 if let PathArguments::AngleBracketed(args) = &seg.arguments {
410 for arg in &args.args {
411 if let GenericArgument::AssocType(assoc) = arg {
412 if assoc.ident == "Output" {
413 return Some(assoc.ty.clone());
414 }
415 }
416 }
417 }
418 }
419 }
420 }
421 }
422 None
423}
424
425/// For HashMap<K,V> or BTreeMap<K,V>, returns Some((key_ty, value_ty)).
426fn extract_map_key_value(ty: &Type) -> Option<(Type, Type)> {
427 use syn::{GenericArgument, PathArguments};
428
429 if let Type::Path(tp) = ty {
430 if let Some(seg) = tp.path.segments.last() {
431 let ident_str = seg.ident.to_string();
432 if ident_str == "HashMap" || ident_str == "BTreeMap" {
433 if let PathArguments::AngleBracketed(ab) = &seg.arguments {
434 let args: Vec<_> = ab.args.iter().collect();
435 if let (Some(key_arg), Some(value_arg)) = (args.get(0), args.get(1)) {
436 if let (GenericArgument::Type(key_ty), GenericArgument::Type(value_ty)) =
437 (key_arg, value_arg)
438 {
439 return Some((key_ty.clone(), value_ty.clone()));
440 }
441 }
442 }
443 }
444 }
445 }
446 None
447}
448
449fn to_snake_case(name: &str) -> String {
450 let mut out = String::new();
451 for (i, c) in name.chars().enumerate() {
452 if c.is_uppercase() {
453 if i != 0 {
454 out.push('_');
455 }
456 out.push(c.to_ascii_lowercase());
457 } else {
458 out.push(c);
459 }
460 }
461 out
462}
463
464/// Derive macro for generating simple keypath methods.
465///
466/// Generates one method per field: `StructName::field_name()` that returns a `Kp`.
467/// Intelligently handles wrapper types (Option, Vec, Box, Arc, etc.) to generate appropriate keypaths.
468///
469/// # Example
470///
471/// ```ignore
472/// #[derive(Kp)]
473/// struct Person {
474/// name: String,
475/// age: i32,
476/// email: Option<String>,
477/// addresses: Vec<String>,
478/// }
479///
480/// // Generates:
481/// // impl Person {
482/// // pub fn name() -> Kp<...> { ... }
483/// // pub fn age() -> Kp<...> { ... }
484/// // pub fn email() -> Kp<...> { ... } // unwraps Option
485/// // pub fn addresses() -> Kp<...> { ... } // accesses first element
486/// // }
487/// ```
488#[proc_macro_derive(Kp)]
489pub fn derive_keypaths(input: TokenStream) -> TokenStream {
490 let input = parse_macro_input!(input as DeriveInput);
491 let name = &input.ident;
492 let input_span = input.span();
493
494 let methods = match input.data {
495 Data::Struct(data_struct) => match data_struct.fields {
496 Fields::Named(fields_named) => {
497 let mut tokens = proc_macro2::TokenStream::new();
498
499 // Generate identity methods for the struct
500 tokens.extend(quote! {
501 /// Returns a generic identity keypath for this type
502 #[inline(always)]
503 pub fn identity_typed<Root, MutRoot>() -> rust_key_paths::Kp<
504 #name,
505 #name,
506 Root,
507 Root,
508 MutRoot,
509 MutRoot,
510 fn(Root) -> Option<Root>,
511 fn(MutRoot) -> Option<MutRoot>,
512 >
513 where
514 Root: std::borrow::Borrow<#name>,
515 MutRoot: std::borrow::BorrowMut<#name>,
516 {
517 rust_key_paths::Kp::new(
518 |r: Root| Some(r),
519 |r: MutRoot| Some(r)
520 )
521 }
522
523 /// Returns a simple identity keypath for this type
524 #[inline(always)]
525 pub fn identity() -> rust_key_paths::KpType<'static, #name, #name> {
526 rust_key_paths::Kp::new(
527 |r: &#name| Some(r),
528 |r: &mut #name| Some(r)
529 )
530 }
531 });
532
533 // When struct has #[pin] fields, generated code calls this.project() which must
534 // be provided by #[pin_project]. If missing, user gets: no method named `project`.
535
536 for field in fields_named.named.iter() {
537 let field_ident = field.ident.as_ref().unwrap();
538 let ty = &field.ty;
539 // Centralized keypath method names – change here to adjust for all types
540 let kp_fn = format_ident!("{}", field_ident);
541 let kp_at_fn = format_ident!("{}_at", field_ident);
542
543 let (kind, inner_ty) = extract_wrapper_inner_type(ty);
544
545 // Override kind when field has #[pin] (pin_project pattern)
546 let (kind, inner_ty) = if field_has_pin_attr(field) {
547 let pinned_kind = if let Some(output_ty) = extract_future_output(ty) {
548 if matches!(kind, WrapperKind::Box) {
549 (WrapperKind::PinnedBoxFuture, Some(output_ty))
550 } else {
551 (WrapperKind::PinnedFuture, Some(output_ty))
552 }
553 } else if is_future_type(ty) {
554 (WrapperKind::PinnedFuture, inner_ty.clone())
555 } else {
556 (WrapperKind::PinnedField, inner_ty.clone())
557 };
558 pinned_kind
559 } else {
560 (kind, inner_ty.clone())
561 };
562
563 match (kind, inner_ty) {
564 (WrapperKind::Option, Some(inner_ty)) => {
565 // For Option<T>, unwrap and access inner type
566 tokens.extend(quote! {
567 #[inline(always)]
568 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
569 rust_key_paths::Kp::new(
570 |root: &#name| root.#field_ident.as_ref(),
571 |root: &mut #name| root.#field_ident.as_mut(),
572 )
573 }
574 });
575 }
576 (WrapperKind::Vec, Some(inner_ty)) => {
577 tokens.extend(quote! {
578 #[inline(always)]
579 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
580 rust_key_paths::Kp::new(
581 |root: &#name| Some(&root.#field_ident),
582 |root: &mut #name| Some(&mut root.#field_ident),
583 )
584 }
585 #[inline(always)]
586 pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
587 rust_key_paths::Kp::new(
588 Box::new(move |root: &#name| root.#field_ident.get(index)),
589 Box::new(move |root: &mut #name| root.#field_ident.get_mut(index)),
590 )
591 }
592 });
593 }
594 (WrapperKind::HashMap, Some(inner_ty)) => {
595 if let Some((key_ty, _)) = extract_map_key_value(ty) {
596 tokens.extend(quote! {
597 #[inline(always)]
598 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
599 rust_key_paths::Kp::new(
600 |root: &#name| Some(&root.#field_ident),
601 |root: &mut #name| Some(&mut root.#field_ident),
602 )
603 }
604 #[inline(always)]
605 pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
606 where
607 #key_ty: Clone + std::hash::Hash + Eq + 'static,
608 {
609 let key2 = key.clone();
610 rust_key_paths::Kp::new(
611 Box::new(move |root: &#name| root.#field_ident.get(&key)),
612 Box::new(move |root: &mut #name| root.#field_ident.get_mut(&key2)),
613 )
614 }
615 });
616 } else {
617 tokens.extend(quote! {
618 #[inline(always)]
619 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
620 rust_key_paths::Kp::new(
621 |root: &#name| Some(&root.#field_ident),
622 |root: &mut #name| Some(&mut root.#field_ident),
623 )
624 }
625 });
626 }
627 }
628 (WrapperKind::BTreeMap, Some(inner_ty)) => {
629 if let Some((key_ty, _)) = extract_map_key_value(ty) {
630 tokens.extend(quote! {
631 #[inline(always)]
632 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
633 rust_key_paths::Kp::new(
634 |root: &#name| Some(&root.#field_ident),
635 |root: &mut #name| Some(&mut root.#field_ident),
636 )
637 }
638 #[inline(always)]
639 pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
640 where
641 #key_ty: Clone + Ord + 'static,
642 {
643 let key2 = key.clone();
644 rust_key_paths::Kp::new(
645 Box::new(move |root: &#name| root.#field_ident.get(&key)),
646 Box::new(move |root: &mut #name| root.#field_ident.get_mut(&key2)),
647 )
648 }
649 });
650 } else {
651 tokens.extend(quote! {
652 #[inline(always)]
653 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
654 rust_key_paths::Kp::new(
655 |root: &#name| Some(&root.#field_ident),
656 |root: &mut #name| Some(&mut root.#field_ident),
657 )
658 }
659 });
660 }
661 }
662 (WrapperKind::Box, Some(inner_ty)) => {
663 // For Box<T>, deref to inner type (returns &T / &mut T, not &Box<T>)
664 tokens.extend(quote! {
665 #[inline(always)]
666 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
667 rust_key_paths::Kp::new(
668 |root: &#name| Some(&*root.#field_ident),
669 |root: &mut #name| Some(&mut *root.#field_ident),
670 )
671 }
672 });
673 }
674 (WrapperKind::Pin, Some(inner_ty)) => {
675 let kp_inner_fn = format_ident!("{}_inner", field_ident);
676 tokens.extend(quote! {
677 #[inline(always)]
678 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
679 rust_key_paths::Kp::new(
680 |root: &#name| Some(&root.#field_ident),
681 |root: &mut #name| Some(&mut root.#field_ident),
682 )
683 }
684 #[inline(always)]
685 pub fn #kp_inner_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty>
686 where #inner_ty: std::marker::Unpin
687 {
688 rust_key_paths::Kp::new(
689 |root: &#name| Some(std::pin::Pin::as_ref(&root.#field_ident).get_ref()),
690 |root: &mut #name| Some(std::pin::Pin::as_mut(&mut root.#field_ident).get_mut()),
691 )
692 }
693 });
694 }
695 (WrapperKind::PinBox, Some(inner_ty)) => {
696 let kp_inner_fn = format_ident!("{}_inner", field_ident);
697 tokens.extend(quote! {
698 #[inline(always)]
699 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
700 rust_key_paths::Kp::new(
701 |root: &#name| Some(&root.#field_ident),
702 |root: &mut #name| Some(&mut root.#field_ident),
703 )
704 }
705 #[inline(always)]
706 pub fn #kp_inner_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty>
707 where #inner_ty: std::marker::Unpin
708 {
709 // Pin::as_ref on Pin<Box<T>> returns Pin<&T> (Box Deref target), so get_ref() already gives &T
710 rust_key_paths::Kp::new(
711 |root: &#name| Some(std::pin::Pin::as_ref(&root.#field_ident).get_ref()),
712 |root: &mut #name| Some(std::pin::Pin::as_mut(&mut root.#field_ident).get_mut()),
713 )
714 }
715 });
716 }
717 (WrapperKind::PinnedField, _) => {
718 let kp_pinned_fn = format_ident!("{}_pinned", field_ident);
719 tokens.extend(quote! {
720 #[inline(always)]
721 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
722 rust_key_paths::Kp::new(
723 |root: &#name| Some(&root.#field_ident),
724 |root: &mut #name| Some(&mut root.#field_ident),
725 )
726 }
727 /// Pinned projection for #[pin] field. Requires #[pin_project] on struct.
728 #[inline(always)]
729 pub fn #kp_pinned_fn(this: std::pin::Pin<&mut #name>) -> std::pin::Pin<&mut #ty> {
730 this.project().#field_ident
731 }
732 });
733 }
734 (WrapperKind::PinnedFuture, _) => {
735 let kp_pinned_fn = format_ident!("{}_pinned", field_ident);
736 let kp_await_fn = format_ident!("{}_await", field_ident);
737 tokens.extend(quote! {
738 #[inline(always)]
739 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
740 rust_key_paths::Kp::new(
741 |root: &#name| Some(&root.#field_ident),
742 |root: &mut #name| Some(&mut root.#field_ident),
743 )
744 }
745 /// Pinned projection for #[pin] Future field. Requires #[pin_project] on struct.
746 #[inline(always)]
747 pub fn #kp_pinned_fn(this: std::pin::Pin<&mut #name>) -> std::pin::Pin<&mut #ty> {
748 this.project().#field_ident
749 }
750 /// Poll the pinned future. Requires #[pin_project] on struct.
751 pub async fn #kp_await_fn(this: std::pin::Pin<&mut #name>) -> Option<<#ty as std::future::Future>::Output>
752 where #ty: std::future::Future
753 {
754 use std::future::Future;
755 Some(this.project().#field_ident.await)
756 }
757 });
758 }
759 (WrapperKind::PinnedBoxFuture, Some(output_ty)) => {
760 let kp_pinned_fn = format_ident!("{}_pinned", field_ident);
761 let kp_await_fn = format_ident!("{}_await", field_ident);
762 tokens.extend(quote! {
763 #[inline(always)]
764 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
765 rust_key_paths::Kp::new(
766 |root: &#name| Some(&root.#field_ident),
767 |root: &mut #name| Some(&mut root.#field_ident),
768 )
769 }
770 /// Pinned projection for #[pin] Box<dyn Future> field. Requires #[pin_project] on struct.
771 #[inline(always)]
772 pub fn #kp_pinned_fn(this: std::pin::Pin<&mut #name>) -> std::pin::Pin<&mut #ty> {
773 this.project().#field_ident
774 }
775 /// Poll the pinned boxed future. Requires #[pin_project] on struct.
776 pub async fn #kp_await_fn(this: std::pin::Pin<&mut #name>) -> Option<#output_ty> {
777 Some(this.project().#field_ident.await)
778 }
779 });
780 }
781 (WrapperKind::Rc, Some(inner_ty)) => {
782 // For Rc<T>, deref to inner type (returns &T; get_mut when uniquely owned)
783 tokens.extend(quote! {
784 #[inline(always)]
785 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
786 rust_key_paths::Kp::new(
787 |root: &#name| Some(root.#field_ident.as_ref()),
788 |root: &mut #name| std::rc::Rc::get_mut(&mut root.#field_ident),
789 )
790 }
791 });
792 }
793 (WrapperKind::Arc, Some(inner_ty)) => {
794 // For Arc<T>, deref to inner type (returns &T; get_mut when uniquely owned)
795 tokens.extend(quote! {
796 #[inline(always)]
797 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
798 rust_key_paths::Kp::new(
799 |root: &#name| Some(root.#field_ident.as_ref()),
800 |root: &mut #name| std::sync::Arc::get_mut(&mut root.#field_ident),
801 )
802 }
803 });
804 }
805 (WrapperKind::Cow, Some(inner_ty)) => {
806 // For Cow<'_, B>, deref to inner type (as_ref/to_mut)
807 tokens.extend(quote! {
808 #[inline(always)]
809 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
810 rust_key_paths::Kp::new(
811 |root: &#name| Some(root.#field_ident.as_ref()),
812 |root: &mut #name| Some(root.#field_ident.to_mut()),
813 )
814 }
815 });
816 }
817
818 (WrapperKind::OptionCow, Some(inner_ty)) => {
819 // For Option<Cow<'_, B>>
820 tokens.extend(quote! {
821 #[inline(always)]
822 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
823 rust_key_paths::Kp::new(
824 |root: &#name| root.#field_ident.as_ref().map(|c| c.as_ref()),
825 |root: &mut #name| root.#field_ident.as_mut().map(|c| c.to_mut()),
826 )
827 }
828 });
829 }
830 (WrapperKind::OptionTagged, Some(inner_ty)) => {
831 // For Option<Tagged<Tag, T>> - Tagged implements Deref/DerefMut
832 tokens.extend(quote! {
833 #[inline(always)]
834 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
835 rust_key_paths::Kp::new(
836 |root: &#name| root.#field_ident.as_ref().map(|t| std::ops::Deref::deref(t)),
837 |root: &mut #name| root.#field_ident.as_mut().map(|t| std::ops::DerefMut::deref_mut(t)),
838 )
839 }
840 });
841 }
842 (WrapperKind::OptionReference, Some(inner_ty)) => {
843 // For Option<&T>, Option<&str>, Option<&[T]> - read-only, setter returns None
844 tokens.extend(quote! {
845 #[inline(always)]
846 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
847 rust_key_paths::Kp::new(
848 |root: &#name| root.#field_ident.as_ref(),
849 |_root: &mut #name| None,
850 )
851 }
852 });
853 }
854 (WrapperKind::HashSet, Some(inner_ty)) => {
855 let kp_at_fn = format_ident!("{}_at", field_ident);
856
857 tokens.extend(quote! {
858 #[inline(always)]
859 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
860 rust_key_paths::Kp::new(
861 |root: &#name| Some(&root.#field_ident),
862 |root: &mut #name| Some(&mut root.#field_ident),
863 )
864 }
865
866 /// _at: check if element exists and get reference.
867 /// HashSet does not allow mutable element access (would break hash invariant).
868 #[inline(always)]
869 pub fn #kp_at_fn(key: #inner_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
870 where
871 #inner_ty: Clone + std::hash::Hash + Eq + 'static,
872 {
873 rust_key_paths::Kp::new(
874 Box::new(move |root: &#name| root.#field_ident.get(&key)),
875 Box::new(move |_root: &mut #name| None),
876 )
877 }
878 });
879 }
880 (WrapperKind::BTreeSet, Some(inner_ty)) => {
881 let kp_at_fn = format_ident!("{}_at", field_ident);
882
883 tokens.extend(quote! {
884 #[inline(always)]
885 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
886 rust_key_paths::Kp::new(
887 |root: &#name| Some(&root.#field_ident),
888 |root: &mut #name| Some(&mut root.#field_ident),
889 )
890 }
891
892 /// _at: check if element exists and get reference.
893 /// BTreeSet does not allow mutable element access (would break ordering invariant).
894 #[inline(always)]
895 pub fn #kp_at_fn(key: #inner_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
896 where
897 #inner_ty: Clone + Ord + 'static,
898 {
899 rust_key_paths::Kp::new(
900 Box::new(move |root: &#name| root.#field_ident.get(&key)),
901 Box::new(move |_root: &mut #name| None),
902 )
903 }
904 });
905 }
906 (WrapperKind::VecDeque, Some(inner_ty)) => {
907 tokens.extend(quote! {
908 #[inline(always)]
909 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
910 rust_key_paths::Kp::new(
911 |root: &#name| Some(&root.#field_ident),
912 |root: &mut #name| Some(&mut root.#field_ident),
913 )
914 }
915 #[inline(always)]
916 pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
917 rust_key_paths::Kp::new(
918 Box::new(move |root: &#name| root.#field_ident.get(index)),
919 Box::new(move |root: &mut #name| root.#field_ident.get_mut(index)),
920 )
921 }
922 });
923 }
924 (WrapperKind::LinkedList, Some(_inner_ty)) => {
925 tokens.extend(quote! {
926 #[inline(always)]
927 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
928 rust_key_paths::Kp::new(
929 |root: &#name| Some(&root.#field_ident),
930 |root: &mut #name| Some(&mut root.#field_ident),
931 )
932 }
933 });
934 }
935 (WrapperKind::BinaryHeap, Some(_inner_ty)) => {
936 tokens.extend(quote! {
937 #[inline(always)]
938 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
939 rust_key_paths::Kp::new(
940 |root: &#name| Some(&root.#field_ident),
941 |root: &mut #name| Some(&mut root.#field_ident),
942 )
943 }
944 });
945 }
946 (WrapperKind::Result, Some(inner_ty)) => {
947 // For Result<T, E>, access Ok value
948 tokens.extend(quote! {
949 #[inline(always)]
950 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
951 rust_key_paths::Kp::new(
952 |root: &#name| root.#field_ident.as_ref().ok(),
953 |root: &mut #name| root.#field_ident.as_mut().ok(),
954 )
955 }
956 });
957 }
958 (WrapperKind::StdArcMutex, Some(inner_ty)) => {
959 // For Arc<std::sync::Mutex<T>>
960 let kp_lock_fn = format_ident!("{}_lock", field_ident);
961 tokens.extend(quote! {
962 #[inline(always)]
963 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
964 rust_key_paths::Kp::new(
965 |root: &#name| Some(&root.#field_ident),
966 |root: &mut #name| Some(&mut root.#field_ident),
967 )
968 }
969 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcMutexFor<#name, #ty, #inner_ty> {
970 rust_key_paths::lock::LockKp::new(
971 rust_key_paths::Kp::new(
972 |root: &#name| Some(&root.#field_ident),
973 |root: &mut #name| Some(&mut root.#field_ident),
974 ),
975 rust_key_paths::lock::ArcMutexAccess::new(),
976 rust_key_paths::Kp::new(
977 |v: &#inner_ty| Some(v),
978 |v: &mut #inner_ty| Some(v),
979 ),
980 )
981 }
982 });
983 }
984 (WrapperKind::StdArcRwLock, Some(inner_ty)) => {
985 // For Arc<std::sync::RwLock<T>>
986 let kp_lock_fn = format_ident!("{}_lock", field_ident);
987 tokens.extend(quote! {
988 #[inline(always)]
989 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
990 rust_key_paths::Kp::new(
991 |root: &#name| Some(&root.#field_ident),
992 |root: &mut #name| Some(&mut root.#field_ident),
993 )
994 }
995 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcRwLockFor<#name, #ty, #inner_ty> {
996 rust_key_paths::lock::LockKp::new(
997 rust_key_paths::Kp::new(
998 |root: &#name| Some(&root.#field_ident),
999 |root: &mut #name| Some(&mut root.#field_ident),
1000 ),
1001 rust_key_paths::lock::ArcRwLockAccess::new(),
1002 rust_key_paths::Kp::new(
1003 |v: &#inner_ty| Some(v),
1004 |v: &mut #inner_ty| Some(v),
1005 ),
1006 )
1007 }
1008 });
1009 }
1010 (WrapperKind::ArcRwLock, Some(inner_ty)) => {
1011 // For Arc<parking_lot::RwLock<T>> (requires rust-key-paths "parking_lot" feature)
1012 let kp_lock_fn = format_ident!("{}_lock", field_ident);
1013 tokens.extend(quote! {
1014 #[inline(always)]
1015 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1016 rust_key_paths::Kp::new(
1017 |root: &#name| Some(&root.#field_ident),
1018 |root: &mut #name| Some(&mut root.#field_ident),
1019 )
1020 }
1021 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpParkingLotRwLockFor<#name, #ty, #inner_ty> {
1022 rust_key_paths::lock::LockKp::new(
1023 rust_key_paths::Kp::new(
1024 |root: &#name| Some(&root.#field_ident),
1025 |root: &mut #name| Some(&mut root.#field_ident),
1026 ),
1027 rust_key_paths::lock::ParkingLotRwLockAccess::new(),
1028 rust_key_paths::Kp::new(
1029 |v: &#inner_ty| Some(v),
1030 |v: &mut #inner_ty| Some(v),
1031 ),
1032 )
1033 }
1034 });
1035 }
1036 (WrapperKind::ArcMutex, Some(inner_ty)) => {
1037 // For Arc<parking_lot::Mutex<T>> (requires rust-key-paths "parking_lot" feature)
1038 let kp_lock_fn = format_ident!("{}_lock", field_ident);
1039 tokens.extend(quote! {
1040 #[inline(always)]
1041 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1042 rust_key_paths::Kp::new(
1043 |root: &#name| Some(&root.#field_ident),
1044 |root: &mut #name| Some(&mut root.#field_ident),
1045 )
1046 }
1047 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpParkingLotMutexFor<#name, #ty, #inner_ty> {
1048 rust_key_paths::lock::LockKp::new(
1049 rust_key_paths::Kp::new(
1050 |root: &#name| Some(&root.#field_ident),
1051 |root: &mut #name| Some(&mut root.#field_ident),
1052 ),
1053 rust_key_paths::lock::ParkingLotMutexAccess::new(),
1054 rust_key_paths::Kp::new(
1055 |v: &#inner_ty| Some(v),
1056 |v: &mut #inner_ty| Some(v),
1057 ),
1058 )
1059 }
1060 });
1061 }
1062 (WrapperKind::Mutex, Some(_inner_ty))
1063 | (WrapperKind::StdMutex, Some(_inner_ty)) => {
1064 // For Mutex<T>, return keypath to container
1065 tokens.extend(quote! {
1066 #[inline(always)]
1067 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1068 rust_key_paths::Kp::new(
1069 |root: &#name| Some(&root.#field_ident),
1070 |root: &mut #name| Some(&mut root.#field_ident),
1071 )
1072 }
1073 });
1074 }
1075 (WrapperKind::RwLock, Some(_inner_ty))
1076 | (WrapperKind::StdRwLock, Some(_inner_ty)) => {
1077 // For RwLock<T>, return keypath to container
1078 tokens.extend(quote! {
1079 #[inline(always)]
1080 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1081 rust_key_paths::Kp::new(
1082 |root: &#name| Some(&root.#field_ident),
1083 |root: &mut #name| Some(&mut root.#field_ident),
1084 )
1085 }
1086 });
1087 }
1088 (WrapperKind::TokioArcMutex, Some(inner_ty)) => {
1089 let kp_async_fn = format_ident!("{}_async", field_ident);
1090 tokens.extend(quote! {
1091 #[inline(always)]
1092 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1093 rust_key_paths::Kp::new(
1094 |root: &#name| Some(&root.#field_ident),
1095 |root: &mut #name| Some(&mut root.#field_ident),
1096 )
1097 }
1098 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, #ty, #inner_ty> {
1099 rust_key_paths::async_lock::AsyncLockKp::new(
1100 rust_key_paths::Kp::new(
1101 |root: &#name| Some(&root.#field_ident),
1102 |root: &mut #name| Some(&mut root.#field_ident),
1103 ),
1104 rust_key_paths::async_lock::TokioMutexAccess::new(),
1105 rust_key_paths::Kp::new(
1106 |v: &#inner_ty| Some(v),
1107 |v: &mut #inner_ty| Some(v),
1108 ),
1109 )
1110 }
1111 });
1112 }
1113 (WrapperKind::TokioArcRwLock, Some(inner_ty)) => {
1114 let kp_async_fn = format_ident!("{}_async", field_ident);
1115 tokens.extend(quote! {
1116 #[inline(always)]
1117 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1118 rust_key_paths::Kp::new(
1119 |root: &#name| Some(&root.#field_ident),
1120 |root: &mut #name| Some(&mut root.#field_ident),
1121 )
1122 }
1123 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, #ty, #inner_ty> {
1124 rust_key_paths::async_lock::AsyncLockKp::new(
1125 rust_key_paths::Kp::new(
1126 |root: &#name| Some(&root.#field_ident),
1127 |root: &mut #name| Some(&mut root.#field_ident),
1128 ),
1129 rust_key_paths::async_lock::TokioRwLockAccess::new(),
1130 rust_key_paths::Kp::new(
1131 |v: &#inner_ty| Some(v),
1132 |v: &mut #inner_ty| Some(v),
1133 ),
1134 )
1135 }
1136 });
1137 }
1138 (WrapperKind::OptionTokioArcMutex, Some(inner_ty)) => {
1139 let kp_async_fn = format_ident!("{}_async", field_ident);
1140 tokens.extend(quote! {
1141 #[inline(always)]
1142 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1143 rust_key_paths::Kp::new(
1144 |root: &#name| Some(&root.#field_ident),
1145 |root: &mut #name| Some(&mut root.#field_ident),
1146 )
1147 }
1148 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, std::sync::Arc<tokio::sync::Mutex<#inner_ty>>, #inner_ty> {
1149 rust_key_paths::async_lock::AsyncLockKp::new(
1150 rust_key_paths::Kp::new(
1151 |root: &#name| root.#field_ident.as_ref(),
1152 |root: &mut #name| root.#field_ident.as_mut(),
1153 ),
1154 rust_key_paths::async_lock::TokioMutexAccess::new(),
1155 rust_key_paths::Kp::new(
1156 |v: &#inner_ty| Some(v),
1157 |v: &mut #inner_ty| Some(v),
1158 ),
1159 )
1160 }
1161 });
1162 }
1163 (WrapperKind::OptionTokioArcRwLock, Some(inner_ty)) => {
1164 let kp_async_fn = format_ident!("{}_async", field_ident);
1165 tokens.extend(quote! {
1166 #[inline(always)]
1167 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1168 rust_key_paths::Kp::new(
1169 |root: &#name| Some(&root.#field_ident),
1170 |root: &mut #name| Some(&mut root.#field_ident),
1171 )
1172 }
1173 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, std::sync::Arc<tokio::sync::RwLock<#inner_ty>>, #inner_ty> {
1174 rust_key_paths::async_lock::AsyncLockKp::new(
1175 rust_key_paths::Kp::new(
1176 |root: &#name| root.#field_ident.as_ref(),
1177 |root: &mut #name| root.#field_ident.as_mut(),
1178 ),
1179 rust_key_paths::async_lock::TokioRwLockAccess::new(),
1180 rust_key_paths::Kp::new(
1181 |v: &#inner_ty| Some(v),
1182 |v: &mut #inner_ty| Some(v),
1183 ),
1184 )
1185 }
1186 });
1187 }
1188 (WrapperKind::OptionStdArcMutex, Some(inner_ty)) => {
1189 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
1190 let kp_lock_fn = format_ident!("{}_lock", field_ident);
1191 tokens.extend(quote! {
1192 #[inline(always)]
1193 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1194 rust_key_paths::Kp::new(
1195 |root: &#name| Some(&root.#field_ident),
1196 |root: &mut #name| Some(&mut root.#field_ident),
1197 )
1198 }
1199 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::Mutex<#inner_ty>>> {
1200 rust_key_paths::Kp::new(
1201 |root: &#name| root.#field_ident.as_ref(),
1202 |root: &mut #name| root.#field_ident.as_mut(),
1203 )
1204 }
1205 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcMutexFor<#name, std::sync::Arc<std::sync::Mutex<#inner_ty>>, #inner_ty> {
1206 rust_key_paths::lock::LockKp::new(
1207 rust_key_paths::Kp::new(
1208 |root: &#name| root.#field_ident.as_ref(),
1209 |root: &mut #name| root.#field_ident.as_mut(),
1210 ),
1211 rust_key_paths::lock::ArcMutexAccess::new(),
1212 rust_key_paths::Kp::new(
1213 |v: &#inner_ty| Some(v),
1214 |v: &mut #inner_ty| Some(v),
1215 ),
1216 )
1217 }
1218 });
1219 }
1220 (WrapperKind::OptionArcMutex, Some(inner_ty)) => {
1221 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
1222 let kp_lock_fn = format_ident!("{}_lock", field_ident);
1223 tokens.extend(quote! {
1224 #[inline(always)]
1225 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1226 rust_key_paths::Kp::new(
1227 |root: &#name| Some(&root.#field_ident),
1228 |root: &mut #name| Some(&mut root.#field_ident),
1229 )
1230 }
1231 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<parking_lot::Mutex<#inner_ty>>> {
1232 rust_key_paths::Kp::new(
1233 |root: &#name| root.#field_ident.as_ref(),
1234 |root: &mut #name| root.#field_ident.as_mut(),
1235 )
1236 }
1237 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpParkingLotMutexFor<#name, std::sync::Arc<parking_lot::Mutex<#inner_ty>>, #inner_ty> {
1238 rust_key_paths::lock::LockKp::new(
1239 rust_key_paths::Kp::new(
1240 |root: &#name| root.#field_ident.as_ref(),
1241 |root: &mut #name| root.#field_ident.as_mut(),
1242 ),
1243 rust_key_paths::lock::ParkingLotMutexAccess::new(),
1244 rust_key_paths::Kp::new(
1245 |v: &#inner_ty| Some(v),
1246 |v: &mut #inner_ty| Some(v),
1247 ),
1248 )
1249 }
1250 });
1251 }
1252 (WrapperKind::OptionStdArcRwLock, Some(inner_ty)) => {
1253 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
1254 let kp_lock_fn = format_ident!("{}_lock", field_ident);
1255 tokens.extend(quote! {
1256 #[inline(always)]
1257 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1258 rust_key_paths::Kp::new(
1259 |root: &#name| Some(&root.#field_ident),
1260 |root: &mut #name| Some(&mut root.#field_ident),
1261 )
1262 }
1263 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::RwLock<#inner_ty>>> {
1264 rust_key_paths::Kp::new(
1265 |root: &#name| root.#field_ident.as_ref(),
1266 |root: &mut #name| root.#field_ident.as_mut(),
1267 )
1268 }
1269 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpArcRwLockFor<#name, std::sync::Arc<std::sync::RwLock<#inner_ty>>, #inner_ty> {
1270 rust_key_paths::lock::LockKp::new(
1271 rust_key_paths::Kp::new(
1272 |root: &#name| root.#field_ident.as_ref(),
1273 |root: &mut #name| root.#field_ident.as_mut(),
1274 ),
1275 rust_key_paths::lock::ArcRwLockAccess::new(),
1276 rust_key_paths::Kp::new(
1277 |v: &#inner_ty| Some(v),
1278 |v: &mut #inner_ty| Some(v),
1279 ),
1280 )
1281 }
1282 });
1283 }
1284 (WrapperKind::OptionArcRwLock, Some(inner_ty)) => {
1285 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
1286 let kp_lock_fn = format_ident!("{}_lock", field_ident);
1287 tokens.extend(quote! {
1288 #[inline(always)]
1289 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1290 rust_key_paths::Kp::new(
1291 |root: &#name| Some(&root.#field_ident),
1292 |root: &mut #name| Some(&mut root.#field_ident),
1293 )
1294 }
1295 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<parking_lot::RwLock<#inner_ty>>> {
1296 rust_key_paths::Kp::new(
1297 |root: &#name| root.#field_ident.as_ref(),
1298 |root: &mut #name| root.#field_ident.as_mut(),
1299 )
1300 }
1301 pub fn #kp_lock_fn() -> rust_key_paths::lock::LockKpParkingLotRwLockFor<#name, std::sync::Arc<parking_lot::RwLock<#inner_ty>>, #inner_ty> {
1302 rust_key_paths::lock::LockKp::new(
1303 rust_key_paths::Kp::new(
1304 |root: &#name| root.#field_ident.as_ref(),
1305 |root: &mut #name| root.#field_ident.as_mut(),
1306 ),
1307 rust_key_paths::lock::ParkingLotRwLockAccess::new(),
1308 rust_key_paths::Kp::new(
1309 |v: &#inner_ty| Some(v),
1310 |v: &mut #inner_ty| Some(v),
1311 ),
1312 )
1313 }
1314 });
1315 }
1316 (WrapperKind::OptionStdMutex, Some(inner_ty))
1317 | (WrapperKind::OptionMutex, Some(inner_ty)) => {
1318 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
1319 tokens.extend(quote! {
1320 #[inline(always)]
1321 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1322 rust_key_paths::Kp::new(
1323 |root: &#name| Some(&root.#field_ident),
1324 |root: &mut #name| Some(&mut root.#field_ident),
1325 )
1326 }
1327 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Mutex<#inner_ty>> {
1328 rust_key_paths::Kp::new(
1329 |root: &#name| root.#field_ident.as_ref(),
1330 |root: &mut #name| root.#field_ident.as_mut(),
1331 )
1332 }
1333 });
1334 }
1335 (WrapperKind::OptionStdRwLock, Some(inner_ty))
1336 | (WrapperKind::OptionRwLock, Some(inner_ty)) => {
1337 let kp_unlocked_fn = format_ident!("{}_unlocked", field_ident);
1338 tokens.extend(quote! {
1339 #[inline(always)]
1340 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1341 rust_key_paths::Kp::new(
1342 |root: &#name| Some(&root.#field_ident),
1343 |root: &mut #name| Some(&mut root.#field_ident),
1344 )
1345 }
1346 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::RwLock<#inner_ty>> {
1347 rust_key_paths::Kp::new(
1348 |root: &#name| root.#field_ident.as_ref(),
1349 |root: &mut #name| root.#field_ident.as_mut(),
1350 )
1351 }
1352 });
1353 }
1354 (WrapperKind::Weak, Some(_inner_ty)) => {
1355 // For Weak<T>, return keypath to container
1356 tokens.extend(quote! {
1357 #[inline(always)]
1358 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1359 rust_key_paths::Kp::new(
1360 |root: &#name| Some(&root.#field_ident),
1361 |_root: &mut #name| None, // Weak doesn't support mutable access
1362 )
1363 }
1364 });
1365 }
1366 (WrapperKind::Atomic, None | Some(_)) => {
1367 // For atomic types: return keypath to the atomic (user calls .load()/.store())
1368 tokens.extend(quote! {
1369 #[inline(always)]
1370 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1371 rust_key_paths::Kp::new(
1372 |root: &#name| Some(&root.#field_ident),
1373 |root: &mut #name| Some(&mut root.#field_ident),
1374 )
1375 }
1376 });
1377 }
1378 (WrapperKind::OptionAtomic, Some(inner_ty)) => {
1379 tokens.extend(quote! {
1380 #[inline(always)]
1381 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1382 rust_key_paths::Kp::new(
1383 |root: &#name| root.#field_ident.as_ref(),
1384 |root: &mut #name| root.#field_ident.as_mut(),
1385 )
1386 }
1387 });
1388 }
1389 (WrapperKind::Reference, Some(_inner_ty)) => {
1390 // For reference types (&T, &str, &[T]): read-only, setter returns None
1391 tokens.extend(quote! {
1392 #[inline(always)]
1393 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1394 rust_key_paths::Kp::new(
1395 |root: &#name| Some(&root.#field_ident),
1396 |_root: &mut #name| None, // references: read-only
1397 )
1398 }
1399 });
1400 }
1401 (WrapperKind::None, None) => {
1402 // For basic types, direct access
1403 tokens.extend(quote! {
1404 #[inline(always)]
1405 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1406 rust_key_paths::Kp::new(
1407 |root: &#name| Some(&root.#field_ident),
1408 |root: &mut #name| Some(&mut root.#field_ident),
1409 )
1410 }
1411 });
1412 }
1413 _ => {
1414 // For unknown/complex nested types, return keypath to field itself
1415 tokens.extend(quote! {
1416 #[inline(always)]
1417 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1418 rust_key_paths::Kp::new(
1419 |root: &#name| Some(&root.#field_ident),
1420 |root: &mut #name| Some(&mut root.#field_ident),
1421 )
1422 }
1423 });
1424 }
1425 }
1426 }
1427
1428 tokens
1429 }
1430 Fields::Unnamed(unnamed) => {
1431 let mut tokens = proc_macro2::TokenStream::new();
1432
1433 // Generate identity methods for the tuple struct
1434 tokens.extend(quote! {
1435 /// Returns a generic identity keypath for this type
1436 #[inline(always)]
1437 pub fn identity_typed<Root, MutRoot>() -> rust_key_paths::Kp<
1438 #name,
1439 #name,
1440 Root,
1441 Root,
1442 MutRoot,
1443 MutRoot,
1444 fn(Root) -> Option<Root>,
1445 fn(MutRoot) -> Option<MutRoot>,
1446 >
1447 where
1448 Root: std::borrow::Borrow<#name>,
1449 MutRoot: std::borrow::BorrowMut<#name>,
1450 {
1451 rust_key_paths::Kp::new(
1452 |r: Root| Some(r),
1453 |r: MutRoot| Some(r)
1454 )
1455 }
1456
1457 /// Returns a simple identity keypath for this type
1458 #[inline(always)]
1459 pub fn identity() -> rust_key_paths::KpType<'static, #name, #name> {
1460 rust_key_paths::Kp::new(
1461 |r: &#name| Some(r),
1462 |r: &mut #name| Some(r)
1463 )
1464 }
1465 });
1466
1467 for (idx, field) in unnamed.unnamed.iter().enumerate() {
1468 let idx_lit = syn::Index::from(idx);
1469 let ty = &field.ty;
1470 // Centralized keypath method names for tuple fields – change here to adjust for all types
1471 let kp_fn = format_ident!("f{}", idx);
1472 let kp_at_fn = format_ident!("f{}_at", idx);
1473
1474 let (kind, inner_ty) = extract_wrapper_inner_type(ty);
1475
1476 match (kind, inner_ty.clone()) {
1477 (WrapperKind::Option, Some(inner_ty)) => {
1478 tokens.extend(quote! {
1479 #[inline(always)]
1480 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1481 rust_key_paths::Kp::new(
1482 |root: &#name| root.#idx_lit.as_ref(),
1483 |root: &mut #name| root.#idx_lit.as_mut(),
1484 )
1485 }
1486 });
1487 }
1488 (WrapperKind::Vec, Some(inner_ty)) => {
1489 tokens.extend(quote! {
1490 #[inline(always)]
1491 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1492 rust_key_paths::Kp::new(
1493 |root: &#name| Some(&root.#idx_lit),
1494 |root: &mut #name| Some(&mut root.#idx_lit),
1495 )
1496 }
1497 #[inline(always)]
1498 pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
1499 rust_key_paths::Kp::new(
1500 Box::new(move |root: &#name| root.#idx_lit.get(index)),
1501 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(index)),
1502 )
1503 }
1504 });
1505 }
1506 (WrapperKind::HashMap, Some(inner_ty)) => {
1507 if let Some((key_ty, _)) = extract_map_key_value(ty) {
1508 tokens.extend(quote! {
1509 #[inline(always)]
1510 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1511 rust_key_paths::Kp::new(
1512 |root: &#name| Some(&root.#idx_lit),
1513 |root: &mut #name| Some(&mut root.#idx_lit),
1514 )
1515 }
1516 #[inline(always)]
1517 pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
1518 where
1519 #key_ty: Clone + std::hash::Hash + Eq + 'static,
1520 {
1521 let key2 = key.clone();
1522 rust_key_paths::Kp::new(
1523 Box::new(move |root: &#name| root.#idx_lit.get(&key)),
1524 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(&key2)),
1525 )
1526 }
1527 });
1528 } else {
1529 tokens.extend(quote! {
1530 #[inline(always)]
1531 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1532 rust_key_paths::Kp::new(
1533 |root: &#name| Some(&root.#idx_lit),
1534 |root: &mut #name| Some(&mut root.#idx_lit),
1535 )
1536 }
1537 });
1538 }
1539 }
1540 (WrapperKind::BTreeMap, Some(inner_ty)) => {
1541 if let Some((key_ty, _)) = extract_map_key_value(ty) {
1542 tokens.extend(quote! {
1543 #[inline(always)]
1544 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1545 rust_key_paths::Kp::new(
1546 |root: &#name| Some(&root.#idx_lit),
1547 |root: &mut #name| Some(&mut root.#idx_lit),
1548 )
1549 }
1550 #[inline(always)]
1551 pub fn #kp_at_fn(key: #key_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
1552 where
1553 #key_ty: Clone + Ord + 'static,
1554 {
1555 let key2 = key.clone();
1556 rust_key_paths::Kp::new(
1557 Box::new(move |root: &#name| root.#idx_lit.get(&key)),
1558 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(&key2)),
1559 )
1560 }
1561 });
1562 } else {
1563 tokens.extend(quote! {
1564 #[inline(always)]
1565 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1566 rust_key_paths::Kp::new(
1567 |root: &#name| Some(&root.#idx_lit),
1568 |root: &mut #name| Some(&mut root.#idx_lit),
1569 )
1570 }
1571 });
1572 }
1573 }
1574 (WrapperKind::Box, Some(inner_ty)) => {
1575 // Box: deref to inner (returns &T / &mut T)
1576 tokens.extend(quote! {
1577 #[inline(always)]
1578 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1579 rust_key_paths::Kp::new(
1580 |root: &#name| Some(&*root.#idx_lit),
1581 |root: &mut #name| Some(&mut *root.#idx_lit),
1582 )
1583 }
1584 });
1585 }
1586 (WrapperKind::Pin, Some(inner_ty)) => {
1587 let kp_inner_fn = format_ident!("{}_inner", kp_fn);
1588 tokens.extend(quote! {
1589 #[inline(always)]
1590 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1591 rust_key_paths::Kp::new(
1592 |root: &#name| Some(&root.#idx_lit),
1593 |root: &mut #name| Some(&mut root.#idx_lit),
1594 )
1595 }
1596 #[inline(always)]
1597 pub fn #kp_inner_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty>
1598 where #inner_ty: std::marker::Unpin
1599 {
1600 rust_key_paths::Kp::new(
1601 |root: &#name| Some(std::pin::Pin::as_ref(&root.#idx_lit).get_ref()),
1602 |root: &mut #name| Some(std::pin::Pin::as_mut(&mut root.#idx_lit).get_mut()),
1603 )
1604 }
1605 });
1606 }
1607 (WrapperKind::PinBox, Some(inner_ty)) => {
1608 let kp_inner_fn = format_ident!("{}_inner", kp_fn);
1609 tokens.extend(quote! {
1610 #[inline(always)]
1611 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1612 rust_key_paths::Kp::new(
1613 |root: &#name| Some(&root.#idx_lit),
1614 |root: &mut #name| Some(&mut root.#idx_lit),
1615 )
1616 }
1617 #[inline(always)]
1618 pub fn #kp_inner_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty>
1619 where #inner_ty: std::marker::Unpin
1620 {
1621 rust_key_paths::Kp::new(
1622 |root: &#name| Some(std::pin::Pin::as_ref(&root.#idx_lit).get_ref()),
1623 |root: &mut #name| Some(std::pin::Pin::as_mut(&mut root.#idx_lit).get_mut()),
1624 )
1625 }
1626 });
1627 }
1628 (WrapperKind::Rc, Some(inner_ty)) => {
1629 tokens.extend(quote! {
1630 #[inline(always)]
1631 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1632 rust_key_paths::Kp::new(
1633 |root: &#name| Some(root.#idx_lit.as_ref()),
1634 |root: &mut #name| std::rc::Rc::get_mut(&mut root.#idx_lit),
1635 )
1636 }
1637 });
1638 }
1639 (WrapperKind::Arc, Some(inner_ty)) => {
1640 tokens.extend(quote! {
1641 #[inline(always)]
1642 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1643 rust_key_paths::Kp::new(
1644 |root: &#name| Some(root.#idx_lit.as_ref()),
1645 |root: &mut #name| std::sync::Arc::get_mut(&mut root.#idx_lit),
1646 )
1647 }
1648 });
1649 }
1650
1651 (WrapperKind::Cow, Some(inner_ty)) => {
1652 tokens.extend(quote! {
1653 #[inline(always)]
1654 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1655 rust_key_paths::Kp::new(
1656 |root: &#name| Some(root.#idx_lit.as_ref()),
1657 |root: &mut #name| Some(root.#idx_lit.to_mut()),
1658 )
1659 }
1660 });
1661 }
1662
1663 (WrapperKind::OptionCow, Some(inner_ty)) => {
1664 tokens.extend(quote! {
1665 #[inline(always)]
1666 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1667 rust_key_paths::Kp::new(
1668 |root: &#name| root.#idx_lit.as_ref().map(|c| c.as_ref()),
1669 |root: &mut #name| root.#idx_lit.as_mut().map(|c| c.to_mut()),
1670 )
1671 }
1672 });
1673 }
1674 (WrapperKind::OptionTagged, Some(inner_ty)) => {
1675 tokens.extend(quote! {
1676 #[inline(always)]
1677 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1678 rust_key_paths::Kp::new(
1679 |root: &#name| root.#idx_lit.as_ref().map(|t| std::ops::Deref::deref(t)),
1680 |root: &mut #name| root.#idx_lit.as_mut().map(|t| std::ops::DerefMut::deref_mut(t)),
1681 )
1682 }
1683 });
1684 }
1685 (WrapperKind::OptionReference, Some(inner_ty)) => {
1686 tokens.extend(quote! {
1687 #[inline(always)]
1688 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1689 rust_key_paths::Kp::new(
1690 |root: &#name| root.#idx_lit.as_ref(),
1691 |_root: &mut #name| None,
1692 )
1693 }
1694 });
1695 }
1696 (WrapperKind::HashSet, Some(inner_ty)) => {
1697 let kp_at_fn = format_ident!("f{}_at", idx);
1698
1699 tokens.extend(quote! {
1700 #[inline(always)]
1701 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1702 rust_key_paths::Kp::new(
1703 |root: &#name| Some(&root.#idx_lit),
1704 |root: &mut #name| Some(&mut root.#idx_lit),
1705 )
1706 }
1707
1708 /// _at: check if element exists and get reference.
1709 /// HashSet does not allow mutable element access (would break hash invariant).
1710 #[inline(always)]
1711 pub fn #kp_at_fn(key: #inner_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
1712 where
1713 #inner_ty: Clone + std::hash::Hash + Eq + 'static,
1714 {
1715 rust_key_paths::Kp::new(
1716 Box::new(move |root: &#name| root.#idx_lit.get(&key)),
1717 Box::new(move |_root: &mut #name| None),
1718 )
1719 }
1720 });
1721 }
1722 (WrapperKind::BTreeSet, Some(inner_ty)) => {
1723 let kp_at_fn = format_ident!("f{}_at", idx);
1724
1725 tokens.extend(quote! {
1726 #[inline(always)]
1727 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1728 rust_key_paths::Kp::new(
1729 |root: &#name| Some(&root.#idx_lit),
1730 |root: &mut #name| Some(&mut root.#idx_lit),
1731 )
1732 }
1733
1734 /// _at: check if element exists and get reference.
1735 /// BTreeSet does not allow mutable element access (would break ordering invariant).
1736 #[inline(always)]
1737 pub fn #kp_at_fn(key: #inner_ty) -> rust_key_paths::KpDynamic<#name, #inner_ty>
1738 where
1739 #inner_ty: Clone + Ord + 'static,
1740 {
1741 rust_key_paths::Kp::new(
1742 Box::new(move |root: &#name| root.#idx_lit.get(&key)),
1743 Box::new(move |_root: &mut #name| None),
1744 )
1745 }
1746 });
1747 }
1748 (WrapperKind::VecDeque, Some(inner_ty)) => {
1749 tokens.extend(quote! {
1750 #[inline(always)]
1751 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1752 rust_key_paths::Kp::new(
1753 |root: &#name| Some(&root.#idx_lit),
1754 |root: &mut #name| Some(&mut root.#idx_lit),
1755 )
1756 }
1757 #[inline(always)]
1758 pub fn #kp_at_fn(index: usize) -> rust_key_paths::KpDynamic<#name, #inner_ty> {
1759 rust_key_paths::Kp::new(
1760 Box::new(move |root: &#name| root.#idx_lit.get(index)),
1761 Box::new(move |root: &mut #name| root.#idx_lit.get_mut(index)),
1762 )
1763 }
1764 });
1765 }
1766 (WrapperKind::LinkedList, Some(_inner_ty)) => {
1767 tokens.extend(quote! {
1768 #[inline(always)]
1769 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1770 rust_key_paths::Kp::new(
1771 |root: &#name| Some(&root.#idx_lit),
1772 |root: &mut #name| Some(&mut root.#idx_lit),
1773 )
1774 }
1775 });
1776 }
1777 (WrapperKind::BinaryHeap, Some(_inner_ty)) => {
1778 tokens.extend(quote! {
1779 #[inline(always)]
1780 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1781 rust_key_paths::Kp::new(
1782 |root: &#name| Some(&root.#idx_lit),
1783 |root: &mut #name| Some(&mut root.#idx_lit),
1784 )
1785 }
1786 });
1787 }
1788 (WrapperKind::Result, Some(inner_ty)) => {
1789 tokens.extend(quote! {
1790 #[inline(always)]
1791 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
1792 rust_key_paths::Kp::new(
1793 |root: &#name| root.#idx_lit.as_ref().ok(),
1794 |root: &mut #name| root.#idx_lit.as_mut().ok(),
1795 )
1796 }
1797 });
1798 }
1799 (WrapperKind::Mutex, Some(_inner_ty))
1800 | (WrapperKind::StdMutex, Some(_inner_ty)) => {
1801 tokens.extend(quote! {
1802 #[inline(always)]
1803 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1804 rust_key_paths::Kp::new(
1805 |root: &#name| Some(&root.#idx_lit),
1806 |root: &mut #name| Some(&mut root.#idx_lit),
1807 )
1808 }
1809 });
1810 }
1811 (WrapperKind::RwLock, Some(_inner_ty))
1812 | (WrapperKind::StdRwLock, Some(_inner_ty)) => {
1813 tokens.extend(quote! {
1814 #[inline(always)]
1815 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1816 rust_key_paths::Kp::new(
1817 |root: &#name| Some(&root.#idx_lit),
1818 |root: &mut #name| Some(&mut root.#idx_lit),
1819 )
1820 }
1821 });
1822 }
1823 (WrapperKind::TokioArcMutex, Some(inner_ty)) => {
1824 let kp_async_fn = format_ident!("f{}_async", idx);
1825 tokens.extend(quote! {
1826 #[inline(always)]
1827 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1828 rust_key_paths::Kp::new(
1829 |root: &#name| Some(&root.#idx_lit),
1830 |root: &mut #name| Some(&mut root.#idx_lit),
1831 )
1832 }
1833 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, #ty, #inner_ty> {
1834 rust_key_paths::async_lock::AsyncLockKp::new(
1835 rust_key_paths::Kp::new(
1836 |root: &#name| Some(&root.#idx_lit),
1837 |root: &mut #name| Some(&mut root.#idx_lit),
1838 ),
1839 rust_key_paths::async_lock::TokioMutexAccess::new(),
1840 rust_key_paths::Kp::new(
1841 |v: &#inner_ty| Some(v),
1842 |v: &mut #inner_ty| Some(v),
1843 ),
1844 )
1845 }
1846 });
1847 }
1848 (WrapperKind::TokioArcRwLock, Some(inner_ty)) => {
1849 let kp_async_fn = format_ident!("f{}_async", idx);
1850 tokens.extend(quote! {
1851 #[inline(always)]
1852 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1853 rust_key_paths::Kp::new(
1854 |root: &#name| Some(&root.#idx_lit),
1855 |root: &mut #name| Some(&mut root.#idx_lit),
1856 )
1857 }
1858 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, #ty, #inner_ty> {
1859 rust_key_paths::async_lock::AsyncLockKp::new(
1860 rust_key_paths::Kp::new(
1861 |root: &#name| Some(&root.#idx_lit),
1862 |root: &mut #name| Some(&mut root.#idx_lit),
1863 ),
1864 rust_key_paths::async_lock::TokioRwLockAccess::new(),
1865 rust_key_paths::Kp::new(
1866 |v: &#inner_ty| Some(v),
1867 |v: &mut #inner_ty| Some(v),
1868 ),
1869 )
1870 }
1871 });
1872 }
1873 (WrapperKind::OptionTokioArcMutex, Some(inner_ty)) => {
1874 let kp_async_fn = format_ident!("f{}_async", idx);
1875 tokens.extend(quote! {
1876 #[inline(always)]
1877 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1878 rust_key_paths::Kp::new(
1879 |root: &#name| Some(&root.#idx_lit),
1880 |root: &mut #name| Some(&mut root.#idx_lit),
1881 )
1882 }
1883 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, std::sync::Arc<tokio::sync::Mutex<#inner_ty>>, #inner_ty> {
1884 rust_key_paths::async_lock::AsyncLockKp::new(
1885 rust_key_paths::Kp::new(
1886 |root: &#name| root.#idx_lit.as_ref(),
1887 |root: &mut #name| root.#idx_lit.as_mut(),
1888 ),
1889 rust_key_paths::async_lock::TokioMutexAccess::new(),
1890 rust_key_paths::Kp::new(
1891 |v: &#inner_ty| Some(v),
1892 |v: &mut #inner_ty| Some(v),
1893 ),
1894 )
1895 }
1896 });
1897 }
1898 (WrapperKind::OptionTokioArcRwLock, Some(inner_ty)) => {
1899 let kp_async_fn = format_ident!("f{}_async", idx);
1900 tokens.extend(quote! {
1901 #[inline(always)]
1902 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1903 rust_key_paths::Kp::new(
1904 |root: &#name| Some(&root.#idx_lit),
1905 |root: &mut #name| Some(&mut root.#idx_lit),
1906 )
1907 }
1908 pub fn #kp_async_fn() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, std::sync::Arc<tokio::sync::RwLock<#inner_ty>>, #inner_ty> {
1909 rust_key_paths::async_lock::AsyncLockKp::new(
1910 rust_key_paths::Kp::new(
1911 |root: &#name| root.#idx_lit.as_ref(),
1912 |root: &mut #name| root.#idx_lit.as_mut(),
1913 ),
1914 rust_key_paths::async_lock::TokioRwLockAccess::new(),
1915 rust_key_paths::Kp::new(
1916 |v: &#inner_ty| Some(v),
1917 |v: &mut #inner_ty| Some(v),
1918 ),
1919 )
1920 }
1921 });
1922 }
1923 (WrapperKind::OptionStdArcMutex, Some(inner_ty)) => {
1924 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1925 tokens.extend(quote! {
1926 #[inline(always)]
1927 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1928 rust_key_paths::Kp::new(
1929 |root: &#name| Some(&root.#idx_lit),
1930 |root: &mut #name| Some(&mut root.#idx_lit),
1931 )
1932 }
1933 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::Mutex<#inner_ty>>> {
1934 rust_key_paths::Kp::new(
1935 |root: &#name| root.#idx_lit.as_ref(),
1936 |root: &mut #name| root.#idx_lit.as_mut(),
1937 )
1938 }
1939 });
1940 }
1941 (WrapperKind::OptionArcMutex, Some(inner_ty)) => {
1942 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1943 tokens.extend(quote! {
1944 #[inline(always)]
1945 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1946 rust_key_paths::Kp::new(
1947 |root: &#name| Some(&root.#idx_lit),
1948 |root: &mut #name| Some(&mut root.#idx_lit),
1949 )
1950 }
1951 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<parking_lot::Mutex<#inner_ty>>> {
1952 rust_key_paths::Kp::new(
1953 |root: &#name| root.#idx_lit.as_ref(),
1954 |root: &mut #name| root.#idx_lit.as_mut(),
1955 )
1956 }
1957 });
1958 }
1959 (WrapperKind::OptionStdArcRwLock, Some(inner_ty)) => {
1960 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1961 tokens.extend(quote! {
1962 #[inline(always)]
1963 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1964 rust_key_paths::Kp::new(
1965 |root: &#name| Some(&root.#idx_lit),
1966 |root: &mut #name| Some(&mut root.#idx_lit),
1967 )
1968 }
1969 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::RwLock<#inner_ty>>> {
1970 rust_key_paths::Kp::new(
1971 |root: &#name| root.#idx_lit.as_ref(),
1972 |root: &mut #name| root.#idx_lit.as_mut(),
1973 )
1974 }
1975 });
1976 }
1977 (WrapperKind::OptionArcRwLock, Some(inner_ty)) => {
1978 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1979 tokens.extend(quote! {
1980 #[inline(always)]
1981 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
1982 rust_key_paths::Kp::new(
1983 |root: &#name| Some(&root.#idx_lit),
1984 |root: &mut #name| Some(&mut root.#idx_lit),
1985 )
1986 }
1987 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<parking_lot::RwLock<#inner_ty>>> {
1988 rust_key_paths::Kp::new(
1989 |root: &#name| root.#idx_lit.as_ref(),
1990 |root: &mut #name| root.#idx_lit.as_mut(),
1991 )
1992 }
1993 });
1994 }
1995 (WrapperKind::OptionStdMutex, Some(inner_ty))
1996 | (WrapperKind::OptionMutex, Some(inner_ty)) => {
1997 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
1998 tokens.extend(quote! {
1999 #[inline(always)]
2000 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
2001 rust_key_paths::Kp::new(
2002 |root: &#name| Some(&root.#idx_lit),
2003 |root: &mut #name| Some(&mut root.#idx_lit),
2004 )
2005 }
2006 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::Mutex<#inner_ty>> {
2007 rust_key_paths::Kp::new(
2008 |root: &#name| root.#idx_lit.as_ref(),
2009 |root: &mut #name| root.#idx_lit.as_mut(),
2010 )
2011 }
2012 });
2013 }
2014 (WrapperKind::OptionStdRwLock, Some(inner_ty))
2015 | (WrapperKind::OptionRwLock, Some(inner_ty)) => {
2016 let kp_unlocked_fn = format_ident!("f{}_unlocked", idx);
2017 tokens.extend(quote! {
2018 #[inline(always)]
2019 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
2020 rust_key_paths::Kp::new(
2021 |root: &#name| Some(&root.#idx_lit),
2022 |root: &mut #name| Some(&mut root.#idx_lit),
2023 )
2024 }
2025 pub fn #kp_unlocked_fn() -> rust_key_paths::KpType<'static, #name, std::sync::RwLock<#inner_ty>> {
2026 rust_key_paths::Kp::new(
2027 |root: &#name| root.#idx_lit.as_ref(),
2028 |root: &mut #name| root.#idx_lit.as_mut(),
2029 )
2030 }
2031 });
2032 }
2033 (WrapperKind::Weak, Some(_inner_ty)) => {
2034 tokens.extend(quote! {
2035 #[inline(always)]
2036 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
2037 rust_key_paths::Kp::new(
2038 |root: &#name| Some(&root.#idx_lit),
2039 |_root: &mut #name| None,
2040 )
2041 }
2042 });
2043 }
2044 (WrapperKind::Atomic, None | Some(_)) => {
2045 tokens.extend(quote! {
2046 #[inline(always)]
2047 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
2048 rust_key_paths::Kp::new(
2049 |root: &#name| Some(&root.#idx_lit),
2050 |root: &mut #name| Some(&mut root.#idx_lit),
2051 )
2052 }
2053 });
2054 }
2055 (WrapperKind::OptionAtomic, Some(inner_ty)) => {
2056 tokens.extend(quote! {
2057 #[inline(always)]
2058 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2059 rust_key_paths::Kp::new(
2060 |root: &#name| root.#idx_lit.as_ref(),
2061 |root: &mut #name| root.#idx_lit.as_mut(),
2062 )
2063 }
2064 });
2065 }
2066 (WrapperKind::Reference, Some(_inner_ty)) => {
2067 tokens.extend(quote! {
2068 #[inline(always)]
2069 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
2070 rust_key_paths::Kp::new(
2071 |root: &#name| Some(&root.#idx_lit),
2072 |_root: &mut #name| None,
2073 )
2074 }
2075 });
2076 }
2077 (WrapperKind::None, None) => {
2078 tokens.extend(quote! {
2079 #[inline(always)]
2080 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
2081 rust_key_paths::Kp::new(
2082 |root: &#name| Some(&root.#idx_lit),
2083 |root: &mut #name| Some(&mut root.#idx_lit),
2084 )
2085 }
2086 });
2087 }
2088 _ => {
2089 tokens.extend(quote! {
2090 #[inline(always)]
2091 pub fn #kp_fn() -> rust_key_paths::KpType<'static, #name, #ty> {
2092 rust_key_paths::Kp::new(
2093 |root: &#name| Some(&root.#idx_lit),
2094 |root: &mut #name| Some(&mut root.#idx_lit),
2095 )
2096 }
2097 });
2098 }
2099 }
2100 }
2101
2102 tokens
2103 }
2104 Fields::Unit => {
2105 return syn::Error::new(input_span, "Kp derive does not support unit structs")
2106 .to_compile_error()
2107 .into();
2108 }
2109 },
2110 Data::Enum(data_enum) => {
2111 let mut tokens = proc_macro2::TokenStream::new();
2112
2113 // Generate identity methods for the enum
2114 tokens.extend(quote! {
2115 /// Returns a generic identity keypath for this type
2116 #[inline(always)]
2117 pub fn identity_typed<Root, MutRoot>() -> rust_key_paths::Kp<
2118 #name,
2119 #name,
2120 Root,
2121 Root,
2122 MutRoot,
2123 MutRoot,
2124 fn(Root) -> Option<Root>,
2125 fn(MutRoot) -> Option<MutRoot>,
2126 >
2127 where
2128 Root: std::borrow::Borrow<#name>,
2129 MutRoot: std::borrow::BorrowMut<#name>,
2130 {
2131 rust_key_paths::Kp::new(
2132 |r: Root| Some(r),
2133 |r: MutRoot| Some(r)
2134 )
2135 }
2136
2137 /// Returns a simple identity keypath for this type
2138 #[inline(always)]
2139 pub fn identity() -> rust_key_paths::KpType<'static, #name, #name> {
2140 rust_key_paths::Kp::new(
2141 |r: &#name| Some(r),
2142 |r: &mut #name| Some(r)
2143 )
2144 }
2145 });
2146
2147 for variant in data_enum.variants.iter() {
2148 let v_ident = &variant.ident;
2149 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
2150
2151 match &variant.fields {
2152 Fields::Unit => {
2153 // Unit variant - return keypath that checks if enum matches variant
2154 tokens.extend(quote! {
2155 #[inline(always)]
2156 pub fn #snake() -> rust_key_paths::KpType<'static, #name, ()> {
2157 rust_key_paths::Kp::new(
2158 |root: &#name| match root {
2159 #name::#v_ident => {
2160 static UNIT: () = ();
2161 Some(&UNIT)
2162 },
2163 _ => None,
2164 },
2165 |_root: &mut #name| None, // Can't mutate unit variant
2166 )
2167 }
2168 });
2169 }
2170 Fields::Unnamed(unnamed) => {
2171 if unnamed.unnamed.len() == 1 {
2172 // Single-field tuple variant
2173 let field_ty = &unnamed.unnamed[0].ty;
2174 let (kind, inner_ty) = extract_wrapper_inner_type(field_ty);
2175
2176 match (kind, inner_ty.clone()) {
2177 (WrapperKind::Option, Some(inner_ty)) => {
2178 tokens.extend(quote! {
2179 #[inline(always)]
2180 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2181 rust_key_paths::Kp::new(
2182 |root: &#name| match root {
2183 #name::#v_ident(inner) => inner.as_ref(),
2184 _ => None,
2185 },
2186 |root: &mut #name| match root {
2187 #name::#v_ident(inner) => inner.as_mut(),
2188 _ => None,
2189 },
2190 )
2191 }
2192 });
2193 }
2194 (WrapperKind::Vec, Some(inner_ty)) => {
2195 tokens.extend(quote! {
2196 #[inline(always)]
2197 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2198 rust_key_paths::Kp::new(
2199 |root: &#name| match root {
2200 #name::#v_ident(inner) => inner.first(),
2201 _ => None,
2202 },
2203 |root: &mut #name| match root {
2204 #name::#v_ident(inner) => inner.first_mut(),
2205 _ => None,
2206 },
2207 )
2208 }
2209 });
2210 }
2211 (WrapperKind::Box, Some(inner_ty)) => {
2212 // Box in enum: deref to inner (&T / &mut T)
2213 tokens.extend(quote! {
2214 #[inline(always)]
2215 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2216 rust_key_paths::Kp::new(
2217 |root: &#name| match root {
2218 #name::#v_ident(inner) => Some(&**inner),
2219 _ => None,
2220 },
2221 |root: &mut #name| match root {
2222 #name::#v_ident(inner) => Some(&mut **inner),
2223 _ => None,
2224 },
2225 )
2226 }
2227 });
2228 }
2229 (WrapperKind::Pin, Some(inner_ty)) => {
2230 let snake_inner = format_ident!("{}_inner", snake);
2231 tokens.extend(quote! {
2232 #[inline(always)]
2233 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2234 rust_key_paths::Kp::new(
2235 |root: &#name| match root {
2236 #name::#v_ident(inner) => Some(inner),
2237 _ => None,
2238 },
2239 |root: &mut #name| match root {
2240 #name::#v_ident(inner) => Some(inner),
2241 _ => None,
2242 },
2243 )
2244 }
2245 #[inline(always)]
2246 pub fn #snake_inner() -> rust_key_paths::KpType<'static, #name, #inner_ty>
2247 where #inner_ty: std::marker::Unpin
2248 {
2249 rust_key_paths::Kp::new(
2250 |root: &#name| match root {
2251 #name::#v_ident(inner) => Some(std::pin::Pin::as_ref(inner).get_ref()),
2252 _ => None,
2253 },
2254 |root: &mut #name| match root {
2255 #name::#v_ident(inner) => Some(std::pin::Pin::as_mut(inner).get_mut()),
2256 _ => None,
2257 },
2258 )
2259 }
2260 });
2261 }
2262 (WrapperKind::PinBox, Some(inner_ty)) => {
2263 let snake_inner = format_ident!("{}_inner", snake);
2264 tokens.extend(quote! {
2265 #[inline(always)]
2266 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2267 rust_key_paths::Kp::new(
2268 |root: &#name| match root {
2269 #name::#v_ident(inner) => Some(inner),
2270 _ => None,
2271 },
2272 |root: &mut #name| match root {
2273 #name::#v_ident(inner) => Some(inner),
2274 _ => None,
2275 },
2276 )
2277 }
2278 #[inline(always)]
2279 pub fn #snake_inner() -> rust_key_paths::KpType<'static, #name, #inner_ty>
2280 where #inner_ty: std::marker::Unpin
2281 {
2282 rust_key_paths::Kp::new(
2283 |root: &#name| match root {
2284 #name::#v_ident(inner) => Some(std::pin::Pin::as_ref(inner).get_ref()),
2285 _ => None,
2286 },
2287 |root: &mut #name| match root {
2288 #name::#v_ident(inner) => Some(std::pin::Pin::as_mut(inner).get_mut()),
2289 _ => None,
2290 },
2291 )
2292 }
2293 });
2294 }
2295 (WrapperKind::Rc, Some(inner_ty)) => {
2296 tokens.extend(quote! {
2297 #[inline(always)]
2298 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2299 rust_key_paths::Kp::new(
2300 |root: &#name| match root {
2301 #name::#v_ident(inner) => Some(inner.as_ref()),
2302 _ => None,
2303 },
2304 |root: &mut #name| match root {
2305 #name::#v_ident(inner) => std::rc::Rc::get_mut(inner),
2306 _ => None,
2307 },
2308 )
2309 }
2310 });
2311 }
2312 (WrapperKind::Arc, Some(inner_ty)) => {
2313 tokens.extend(quote! {
2314 #[inline(always)]
2315 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2316 rust_key_paths::Kp::new(
2317 |root: &#name| match root {
2318 #name::#v_ident(inner) => Some(inner.as_ref()),
2319 _ => None,
2320 },
2321 |root: &mut #name| match root {
2322 #name::#v_ident(inner) => std::sync::Arc::get_mut(inner),
2323 _ => None,
2324 },
2325 )
2326 }
2327 });
2328 }
2329 (WrapperKind::StdArcRwLock, Some(inner_ty)) => {
2330 let snake_lock = format_ident!("{}_lock", snake);
2331 tokens.extend(quote! {
2332 #[inline(always)]
2333 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2334 rust_key_paths::Kp::new(
2335 |root: &#name| match root {
2336 #name::#v_ident(inner) => Some(inner),
2337 _ => None,
2338 },
2339 |root: &mut #name| match root {
2340 #name::#v_ident(inner) => Some(inner),
2341 _ => None,
2342 },
2343 )
2344 }
2345 pub fn #snake_lock() -> rust_key_paths::lock::LockKpArcRwLockFor<#name, #field_ty, #inner_ty> {
2346 rust_key_paths::lock::LockKp::new(
2347 rust_key_paths::Kp::new(
2348 |root: &#name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2349 |root: &mut #name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2350 ),
2351 rust_key_paths::lock::ArcRwLockAccess::new(),
2352 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2353 )
2354 }
2355 });
2356 }
2357 (WrapperKind::StdArcMutex, Some(inner_ty)) => {
2358 let snake_lock = format_ident!("{}_lock", snake);
2359 tokens.extend(quote! {
2360 #[inline(always)]
2361 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2362 rust_key_paths::Kp::new(
2363 |root: &#name| match root {
2364 #name::#v_ident(inner) => Some(inner),
2365 _ => None,
2366 },
2367 |root: &mut #name| match root {
2368 #name::#v_ident(inner) => Some(inner),
2369 _ => None,
2370 },
2371 )
2372 }
2373 pub fn #snake_lock() -> rust_key_paths::lock::LockKpArcMutexFor<#name, #field_ty, #inner_ty> {
2374 rust_key_paths::lock::LockKp::new(
2375 rust_key_paths::Kp::new(
2376 |root: &#name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2377 |root: &mut #name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2378 ),
2379 rust_key_paths::lock::ArcMutexAccess::new(),
2380 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2381 )
2382 }
2383 });
2384 }
2385 (WrapperKind::ArcRwLock, Some(inner_ty)) => {
2386 let snake_lock = format_ident!("{}_lock", snake);
2387 tokens.extend(quote! {
2388 #[inline(always)]
2389 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2390 rust_key_paths::Kp::new(
2391 |root: &#name| match root {
2392 #name::#v_ident(inner) => Some(inner),
2393 _ => None,
2394 },
2395 |root: &mut #name| match root {
2396 #name::#v_ident(inner) => Some(inner),
2397 _ => None,
2398 },
2399 )
2400 }
2401 pub fn #snake_lock() -> rust_key_paths::lock::LockKpParkingLotRwLockFor<#name, #field_ty, #inner_ty> {
2402 rust_key_paths::lock::LockKp::new(
2403 rust_key_paths::Kp::new(
2404 |root: &#name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2405 |root: &mut #name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2406 ),
2407 rust_key_paths::lock::ParkingLotRwLockAccess::new(),
2408 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2409 )
2410 }
2411 });
2412 }
2413 (WrapperKind::ArcMutex, Some(inner_ty)) => {
2414 let snake_lock = format_ident!("{}_lock", snake);
2415 tokens.extend(quote! {
2416 #[inline(always)]
2417 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2418 rust_key_paths::Kp::new(
2419 |root: &#name| match root {
2420 #name::#v_ident(inner) => Some(inner),
2421 _ => None,
2422 },
2423 |root: &mut #name| match root {
2424 #name::#v_ident(inner) => Some(inner),
2425 _ => None,
2426 },
2427 )
2428 }
2429 pub fn #snake_lock() -> rust_key_paths::lock::LockKpParkingLotMutexFor<#name, #field_ty, #inner_ty> {
2430 rust_key_paths::lock::LockKp::new(
2431 rust_key_paths::Kp::new(
2432 |root: &#name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2433 |root: &mut #name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2434 ),
2435 rust_key_paths::lock::ParkingLotMutexAccess::new(),
2436 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2437 )
2438 }
2439 });
2440 }
2441 (WrapperKind::TokioArcMutex, Some(inner_ty)) => {
2442 let snake_async = format_ident!("{}_async", snake);
2443 tokens.extend(quote! {
2444 #[inline(always)]
2445 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2446 rust_key_paths::Kp::new(
2447 |root: &#name| match root {
2448 #name::#v_ident(inner) => Some(inner),
2449 _ => None,
2450 },
2451 |root: &mut #name| match root {
2452 #name::#v_ident(inner) => Some(inner),
2453 _ => None,
2454 },
2455 )
2456 }
2457 pub fn #snake_async() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, #field_ty, #inner_ty> {
2458 rust_key_paths::async_lock::AsyncLockKp::new(
2459 rust_key_paths::Kp::new(
2460 |root: &#name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2461 |root: &mut #name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2462 ),
2463 rust_key_paths::async_lock::TokioMutexAccess::new(),
2464 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2465 )
2466 }
2467 });
2468 }
2469 (WrapperKind::TokioArcRwLock, Some(inner_ty)) => {
2470 let snake_async = format_ident!("{}_async", snake);
2471 tokens.extend(quote! {
2472 #[inline(always)]
2473 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2474 rust_key_paths::Kp::new(
2475 |root: &#name| match root {
2476 #name::#v_ident(inner) => Some(inner),
2477 _ => None,
2478 },
2479 |root: &mut #name| match root {
2480 #name::#v_ident(inner) => Some(inner),
2481 _ => None,
2482 },
2483 )
2484 }
2485 pub fn #snake_async() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, #field_ty, #inner_ty> {
2486 rust_key_paths::async_lock::AsyncLockKp::new(
2487 rust_key_paths::Kp::new(
2488 |root: &#name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2489 |root: &mut #name| match root { #name::#v_ident(inner) => Some(inner), _ => None },
2490 ),
2491 rust_key_paths::async_lock::TokioRwLockAccess::new(),
2492 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2493 )
2494 }
2495 });
2496 }
2497 (WrapperKind::OptionTokioArcMutex, Some(inner_ty)) => {
2498 let snake_async = format_ident!("{}_async", snake);
2499 tokens.extend(quote! {
2500 #[inline(always)]
2501 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2502 rust_key_paths::Kp::new(
2503 |root: &#name| match root {
2504 #name::#v_ident(inner) => Some(inner),
2505 _ => None,
2506 },
2507 |root: &mut #name| match root {
2508 #name::#v_ident(inner) => Some(inner),
2509 _ => None,
2510 },
2511 )
2512 }
2513 pub fn #snake_async() -> rust_key_paths::async_lock::AsyncLockKpMutexFor<#name, std::sync::Arc<tokio::sync::Mutex<#inner_ty>>, #inner_ty> {
2514 rust_key_paths::async_lock::AsyncLockKp::new(
2515 rust_key_paths::Kp::new(
2516 |root: &#name| match root { #name::#v_ident(inner) => inner.as_ref(), _ => None },
2517 |root: &mut #name| match root { #name::#v_ident(inner) => inner.as_mut(), _ => None },
2518 ),
2519 rust_key_paths::async_lock::TokioMutexAccess::new(),
2520 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2521 )
2522 }
2523 });
2524 }
2525 (WrapperKind::OptionTokioArcRwLock, Some(inner_ty)) => {
2526 let snake_async = format_ident!("{}_async", snake);
2527 tokens.extend(quote! {
2528 #[inline(always)]
2529 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2530 rust_key_paths::Kp::new(
2531 |root: &#name| match root {
2532 #name::#v_ident(inner) => Some(inner),
2533 _ => None,
2534 },
2535 |root: &mut #name| match root {
2536 #name::#v_ident(inner) => Some(inner),
2537 _ => None,
2538 },
2539 )
2540 }
2541 pub fn #snake_async() -> rust_key_paths::async_lock::AsyncLockKpRwLockFor<#name, std::sync::Arc<tokio::sync::RwLock<#inner_ty>>, #inner_ty> {
2542 rust_key_paths::async_lock::AsyncLockKp::new(
2543 rust_key_paths::Kp::new(
2544 |root: &#name| match root { #name::#v_ident(inner) => inner.as_ref(), _ => None },
2545 |root: &mut #name| match root { #name::#v_ident(inner) => inner.as_mut(), _ => None },
2546 ),
2547 rust_key_paths::async_lock::TokioRwLockAccess::new(),
2548 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2549 )
2550 }
2551 });
2552 }
2553 (WrapperKind::OptionStdArcMutex, Some(inner_ty)) => {
2554 let snake_unlocked = format_ident!("{}_unlocked", snake);
2555 let snake_lock = format_ident!("{}_lock", snake);
2556 tokens.extend(quote! {
2557 #[inline(always)]
2558 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2559 rust_key_paths::Kp::new(
2560 |root: &#name| match root {
2561 #name::#v_ident(inner) => Some(inner),
2562 _ => None,
2563 },
2564 |root: &mut #name| match root {
2565 #name::#v_ident(inner) => Some(inner),
2566 _ => None,
2567 },
2568 )
2569 }
2570 pub fn #snake_unlocked() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::Mutex<#inner_ty>>> {
2571 rust_key_paths::Kp::new(
2572 |root: &#name| match root { #name::#v_ident(inner) => inner.as_ref(), _ => None },
2573 |root: &mut #name| match root { #name::#v_ident(inner) => inner.as_mut(), _ => None },
2574 )
2575 }
2576 pub fn #snake_lock() -> rust_key_paths::lock::LockKpArcMutexFor<#name, std::sync::Arc<std::sync::Mutex<#inner_ty>>, #inner_ty> {
2577 rust_key_paths::lock::LockKp::new(
2578 rust_key_paths::Kp::new(
2579 |root: &#name| match root { #name::#v_ident(inner) => inner.as_ref(), _ => None },
2580 |root: &mut #name| match root { #name::#v_ident(inner) => inner.as_mut(), _ => None },
2581 ),
2582 rust_key_paths::lock::ArcMutexAccess::new(),
2583 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2584 )
2585 }
2586 });
2587 }
2588 (WrapperKind::OptionArcMutex, Some(inner_ty)) => {
2589 let snake_unlocked = format_ident!("{}_unlocked", snake);
2590 let snake_lock = format_ident!("{}_lock", snake);
2591 tokens.extend(quote! {
2592 #[inline(always)]
2593 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2594 rust_key_paths::Kp::new(
2595 |root: &#name| match root {
2596 #name::#v_ident(inner) => Some(inner),
2597 _ => None,
2598 },
2599 |root: &mut #name| match root {
2600 #name::#v_ident(inner) => Some(inner),
2601 _ => None,
2602 },
2603 )
2604 }
2605 pub fn #snake_unlocked() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<parking_lot::Mutex<#inner_ty>>> {
2606 rust_key_paths::Kp::new(
2607 |root: &#name| match root { #name::#v_ident(inner) => inner.as_ref(), _ => None },
2608 |root: &mut #name| match root { #name::#v_ident(inner) => inner.as_mut(), _ => None },
2609 )
2610 }
2611 pub fn #snake_lock() -> rust_key_paths::lock::LockKpParkingLotMutexFor<#name, std::sync::Arc<parking_lot::Mutex<#inner_ty>>, #inner_ty> {
2612 rust_key_paths::lock::LockKp::new(
2613 rust_key_paths::Kp::new(
2614 |root: &#name| match root { #name::#v_ident(inner) => inner.as_ref(), _ => None },
2615 |root: &mut #name| match root { #name::#v_ident(inner) => inner.as_mut(), _ => None },
2616 ),
2617 rust_key_paths::lock::ParkingLotMutexAccess::new(),
2618 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2619 )
2620 }
2621 });
2622 }
2623 (WrapperKind::OptionStdArcRwLock, Some(inner_ty)) => {
2624 let snake_unlocked = format_ident!("{}_unlocked", snake);
2625 let snake_lock = format_ident!("{}_lock", snake);
2626 tokens.extend(quote! {
2627 #[inline(always)]
2628 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2629 rust_key_paths::Kp::new(
2630 |root: &#name| match root {
2631 #name::#v_ident(inner) => Some(inner),
2632 _ => None,
2633 },
2634 |root: &mut #name| match root {
2635 #name::#v_ident(inner) => Some(inner),
2636 _ => None,
2637 },
2638 )
2639 }
2640 pub fn #snake_unlocked() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<std::sync::RwLock<#inner_ty>>> {
2641 rust_key_paths::Kp::new(
2642 |root: &#name| match root { #name::#v_ident(inner) => inner.as_ref(), _ => None },
2643 |root: &mut #name| match root { #name::#v_ident(inner) => inner.as_mut(), _ => None },
2644 )
2645 }
2646 pub fn #snake_lock() -> rust_key_paths::lock::LockKpArcRwLockFor<#name, std::sync::Arc<std::sync::RwLock<#inner_ty>>, #inner_ty> {
2647 rust_key_paths::lock::LockKp::new(
2648 rust_key_paths::Kp::new(
2649 |root: &#name| match root { #name::#v_ident(inner) => inner.as_ref(), _ => None },
2650 |root: &mut #name| match root { #name::#v_ident(inner) => inner.as_mut(), _ => None },
2651 ),
2652 rust_key_paths::lock::ArcRwLockAccess::new(),
2653 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2654 )
2655 }
2656 });
2657 }
2658 (WrapperKind::OptionArcRwLock, Some(inner_ty)) => {
2659 let snake_unlocked = format_ident!("{}_unlocked", snake);
2660 let snake_lock = format_ident!("{}_lock", snake);
2661 tokens.extend(quote! {
2662 #[inline(always)]
2663 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2664 rust_key_paths::Kp::new(
2665 |root: &#name| match root {
2666 #name::#v_ident(inner) => Some(inner),
2667 _ => None,
2668 },
2669 |root: &mut #name| match root {
2670 #name::#v_ident(inner) => Some(inner),
2671 _ => None,
2672 },
2673 )
2674 }
2675 pub fn #snake_unlocked() -> rust_key_paths::KpType<'static, #name, std::sync::Arc<parking_lot::RwLock<#inner_ty>>> {
2676 rust_key_paths::Kp::new(
2677 |root: &#name| match root { #name::#v_ident(inner) => inner.as_ref(), _ => None },
2678 |root: &mut #name| match root { #name::#v_ident(inner) => inner.as_mut(), _ => None },
2679 )
2680 }
2681 pub fn #snake_lock() -> rust_key_paths::lock::LockKpParkingLotRwLockFor<#name, std::sync::Arc<parking_lot::RwLock<#inner_ty>>, #inner_ty> {
2682 rust_key_paths::lock::LockKp::new(
2683 rust_key_paths::Kp::new(
2684 |root: &#name| match root { #name::#v_ident(inner) => inner.as_ref(), _ => None },
2685 |root: &mut #name| match root { #name::#v_ident(inner) => inner.as_mut(), _ => None },
2686 ),
2687 rust_key_paths::lock::ParkingLotRwLockAccess::new(),
2688 rust_key_paths::Kp::new(|v: &#inner_ty| Some(v), |v: &mut #inner_ty| Some(v)),
2689 )
2690 }
2691 });
2692 }
2693 (WrapperKind::StdMutex, Some(_inner_ty))
2694 | (WrapperKind::Mutex, Some(_inner_ty))
2695 | (WrapperKind::StdRwLock, Some(_inner_ty))
2696 | (WrapperKind::RwLock, Some(_inner_ty)) => {
2697 tokens.extend(quote! {
2698 #[inline(always)]
2699 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2700 rust_key_paths::Kp::new(
2701 |root: &#name| match root {
2702 #name::#v_ident(inner) => Some(inner),
2703 _ => None,
2704 },
2705 |root: &mut #name| match root {
2706 #name::#v_ident(inner) => Some(inner),
2707 _ => None,
2708 },
2709 )
2710 }
2711 });
2712 }
2713 (WrapperKind::Tagged, Some(inner_ty)) => {
2714 tokens.extend(quote! {
2715 #[inline(always)]
2716 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2717 rust_key_paths::Kp::new(
2718 |root: &#name| match root {
2719 #name::#v_ident(inner) => Some(std::ops::Deref::deref(inner)),
2720 _ => None,
2721 },
2722 |root: &mut #name| match root {
2723 #name::#v_ident(inner) => Some(std::ops::DerefMut::deref_mut(inner)),
2724 _ => None,
2725 },
2726 )
2727 }
2728 });
2729 }
2730 (WrapperKind::Atomic, None | Some(_)) => {
2731 tokens.extend(quote! {
2732 #[inline(always)]
2733 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2734 rust_key_paths::Kp::new(
2735 |root: &#name| match root {
2736 #name::#v_ident(inner) => Some(inner),
2737 _ => None,
2738 },
2739 |root: &mut #name| match root {
2740 #name::#v_ident(inner) => Some(inner),
2741 _ => None,
2742 },
2743 )
2744 }
2745 });
2746 }
2747 (WrapperKind::OptionAtomic, Some(inner_ty)) => {
2748 tokens.extend(quote! {
2749 #[inline(always)]
2750 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2751 rust_key_paths::Kp::new(
2752 |root: &#name| match root { #name::#v_ident(inner) => inner.as_ref(), _ => None },
2753 |root: &mut #name| match root { #name::#v_ident(inner) => inner.as_mut(), _ => None },
2754 )
2755 }
2756 });
2757 }
2758 (WrapperKind::Reference, Some(_inner_ty)) => {
2759 tokens.extend(quote! {
2760 #[inline(always)]
2761 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2762 rust_key_paths::Kp::new(
2763 |root: &#name| match root {
2764 #name::#v_ident(inner) => Some(inner),
2765 _ => None,
2766 },
2767 |_root: &mut #name| None,
2768 )
2769 }
2770 });
2771 }
2772 (WrapperKind::Weak, Some(_inner_ty)) => {
2773 tokens.extend(quote! {
2774 #[inline(always)]
2775 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2776 rust_key_paths::Kp::new(
2777 |root: &#name| match root {
2778 #name::#v_ident(inner) => Some(inner),
2779 _ => None,
2780 },
2781 |_root: &mut #name| None,
2782 )
2783 }
2784 });
2785 }
2786 (WrapperKind::Cow, Some(inner_ty)) => {
2787 tokens.extend(quote! {
2788 #[inline(always)]
2789 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2790 rust_key_paths::Kp::new(
2791 |root: &#name| match root {
2792 #name::#v_ident(inner) => Some(inner.as_ref()),
2793 _ => None,
2794 },
2795 |root: &mut #name| match root {
2796 #name::#v_ident(inner) => Some(inner.to_mut()),
2797 _ => None,
2798 },
2799 )
2800 }
2801 });
2802 }
2803 (WrapperKind::OptionCow, Some(inner_ty)) => {
2804 tokens.extend(quote! {
2805 #[inline(always)]
2806 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2807 rust_key_paths::Kp::new(
2808 |root: &#name| match root {
2809 #name::#v_ident(inner) => inner.as_ref().map(|c| c.as_ref()),
2810 _ => None,
2811 },
2812 |root: &mut #name| match root {
2813 #name::#v_ident(inner) => inner.as_mut().map(|c| c.to_mut()),
2814 _ => None,
2815 },
2816 )
2817 }
2818 });
2819 }
2820 (WrapperKind::OptionTagged, Some(inner_ty)) => {
2821 tokens.extend(quote! {
2822 #[inline(always)]
2823 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2824 rust_key_paths::Kp::new(
2825 |root: &#name| match root {
2826 #name::#v_ident(inner) => inner.as_ref().map(|t| std::ops::Deref::deref(t)),
2827 _ => None,
2828 },
2829 |root: &mut #name| match root {
2830 #name::#v_ident(inner) => inner.as_mut().map(|t| std::ops::DerefMut::deref_mut(t)),
2831 _ => None,
2832 },
2833 )
2834 }
2835 });
2836 }
2837 (WrapperKind::OptionReference, Some(inner_ty)) => {
2838 tokens.extend(quote! {
2839 #[inline(always)]
2840 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #inner_ty> {
2841 rust_key_paths::Kp::new(
2842 |root: &#name| match root {
2843 #name::#v_ident(inner) => inner.as_ref(),
2844 _ => None,
2845 },
2846 |_root: &mut #name| None,
2847 )
2848 }
2849 });
2850 }
2851 (WrapperKind::None, None) => {
2852 // Basic type
2853 tokens.extend(quote! {
2854 #[inline(always)]
2855 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2856 rust_key_paths::Kp::new(
2857 |root: &#name| match root {
2858 #name::#v_ident(inner) => Some(inner),
2859 _ => None,
2860 },
2861 |root: &mut #name| match root {
2862 #name::#v_ident(inner) => Some(inner),
2863 _ => None,
2864 },
2865 )
2866 }
2867 });
2868 }
2869 _ => {
2870 // Other wrapper types - return keypath to field
2871 tokens.extend(quote! {
2872 #[inline(always)]
2873 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #field_ty> {
2874 rust_key_paths::Kp::new(
2875 |root: &#name| match root {
2876 #name::#v_ident(inner) => Some(inner),
2877 _ => None,
2878 },
2879 |root: &mut #name| match root {
2880 #name::#v_ident(inner) => Some(inner),
2881 _ => None,
2882 },
2883 )
2884 }
2885 });
2886 }
2887 }
2888 } else {
2889 // Multi-field tuple variant - return keypath to variant itself
2890 tokens.extend(quote! {
2891 #[inline(always)]
2892 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #name> {
2893 rust_key_paths::Kp::new(
2894 |root: &#name| match root {
2895 #name::#v_ident(..) => Some(root),
2896 _ => None,
2897 },
2898 |root: &mut #name| match root {
2899 #name::#v_ident(..) => Some(root),
2900 _ => None,
2901 },
2902 )
2903 }
2904 });
2905 }
2906 }
2907 Fields::Named(_) => {
2908 // Named field variant - return keypath to variant itself
2909 tokens.extend(quote! {
2910 pub fn #snake() -> rust_key_paths::KpType<'static, #name, #name> {
2911 rust_key_paths::Kp::new(
2912 |root: &#name| match root {
2913 #name::#v_ident { .. } => Some(root),
2914 _ => None,
2915 },
2916 |root: &mut #name| match root {
2917 #name::#v_ident { .. } => Some(root),
2918 _ => None,
2919 },
2920 )
2921 }
2922 });
2923 }
2924 }
2925 }
2926
2927 tokens
2928 }
2929 Data::Union(_) => {
2930 return syn::Error::new(input_span, "Kp derive does not support unions")
2931 .to_compile_error()
2932 .into();
2933 }
2934 };
2935
2936 let expanded = quote! {
2937 impl #name {
2938 #methods
2939 }
2940 };
2941
2942 TokenStream::from(expanded)
2943}
2944
2945/// Derive macro that generates `partial_kps() -> Vec<PKp<Self>>` returning all field/variant keypaths.
2946/// **Requires `#[derive(Kp)]`** so the keypath accessor methods exist.
2947///
2948/// For structs: returns keypaths for each field. For enums: returns keypaths for each variant
2949/// (using the same methods Kp generates, e.g. `some_variant()`).
2950///
2951/// # Example
2952/// ```
2953/// use key_paths_derive::{Kp, Pkp};
2954/// use rust_key_paths::PKp;
2955///
2956/// #[derive(Kp, Pkp)]
2957/// struct Person {
2958/// name: String,
2959/// age: i32,
2960/// }
2961///
2962/// let kps = Person::partial_kps();
2963/// assert_eq!(kps.len(), 2);
2964/// ```
2965#[proc_macro_derive(Pkp)]
2966pub fn derive_partial_keypaths(input: TokenStream) -> TokenStream {
2967 let input = parse_macro_input!(input as DeriveInput);
2968 let name = &input.ident;
2969
2970 let kp_calls = match &input.data {
2971 Data::Struct(data_struct) => match &data_struct.fields {
2972 Fields::Named(fields_named) => {
2973 let calls: Vec<_> = fields_named
2974 .named
2975 .iter()
2976 .filter_map(|f| f.ident.as_ref())
2977 .map(|field_ident| {
2978 quote! { rust_key_paths::PKp::new(Self::#field_ident()) }
2979 })
2980 .collect();
2981 quote! { #(#calls),* }
2982 }
2983 Fields::Unnamed(unnamed) => {
2984 let calls: Vec<_> = (0..unnamed.unnamed.len())
2985 .map(|idx| {
2986 let kp_fn = format_ident!("f{}", idx);
2987 quote! { rust_key_paths::PKp::new(Self::#kp_fn()) }
2988 })
2989 .collect();
2990 quote! { #(#calls),* }
2991 }
2992 Fields::Unit => quote! {},
2993 },
2994 Data::Enum(data_enum) => {
2995 let calls: Vec<_> = data_enum
2996 .variants
2997 .iter()
2998 .map(|variant| {
2999 let v_ident = &variant.ident;
3000 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
3001 quote! { rust_key_paths::PKp::new(Self::#snake()) }
3002 })
3003 .collect();
3004 quote! { #(#calls),* }
3005 }
3006 Data::Union(_) => {
3007 return syn::Error::new(
3008 input.ident.span(),
3009 "Pkp derive does not support unions",
3010 )
3011 .to_compile_error()
3012 .into();
3013 }
3014 };
3015
3016 let expanded = quote! {
3017 impl #name {
3018 /// Returns a vec of all field keypaths as partial keypaths (type-erased).
3019 #[inline(always)]
3020 pub fn partial_kps() -> Vec<rust_key_paths::PKp<#name>> {
3021 vec![#kp_calls]
3022 }
3023 }
3024 };
3025
3026 TokenStream::from(expanded)
3027}
3028
3029/// Derive macro that generates `any_kps() -> Vec<AKp>` returning all field/variant keypaths as any keypaths.
3030/// **Requires `#[derive(Kp)]`** so the keypath accessor methods exist.
3031/// AKp type-erases both Root and Value, enabling heterogeneous collections of keypaths.
3032///
3033/// For structs: returns keypaths for each field. For enums: returns keypaths for each variant
3034/// (using the same methods Kp generates, e.g. `some_variant()`).
3035///
3036/// # Example
3037/// ```
3038/// use key_paths_derive::{Kp, Akp};
3039/// use rust_key_paths::AKp;
3040///
3041/// #[derive(Kp, Akp)]
3042/// struct Person {
3043/// name: String,
3044/// age: i32,
3045/// }
3046///
3047/// let kps = Person::any_kps();
3048/// assert_eq!(kps.len(), 2);
3049/// let person = Person { name: "Alice".into(), age: 30 };
3050/// let name: Option<&String> = kps[0].get(&person as &dyn std::any::Any).and_then(|v| v.downcast_ref());
3051/// assert_eq!(name, Some(&"Alice".to_string()));
3052/// ```
3053#[proc_macro_derive(Akp)]
3054pub fn derive_any_keypaths(input: TokenStream) -> TokenStream {
3055 let input = parse_macro_input!(input as DeriveInput);
3056 let name = &input.ident;
3057
3058 let kp_calls = match &input.data {
3059 Data::Struct(data_struct) => match &data_struct.fields {
3060 Fields::Named(fields_named) => {
3061 let calls: Vec<_> = fields_named
3062 .named
3063 .iter()
3064 .filter_map(|f| f.ident.as_ref())
3065 .map(|field_ident| {
3066 quote! { rust_key_paths::AKp::new(Self::#field_ident()) }
3067 })
3068 .collect();
3069 quote! { #(#calls),* }
3070 }
3071 Fields::Unnamed(unnamed) => {
3072 let calls: Vec<_> = (0..unnamed.unnamed.len())
3073 .map(|idx| {
3074 let kp_fn = format_ident!("f{}", idx);
3075 quote! { rust_key_paths::AKp::new(Self::#kp_fn()) }
3076 })
3077 .collect();
3078 quote! { #(#calls),* }
3079 }
3080 Fields::Unit => quote! {},
3081 },
3082 Data::Enum(data_enum) => {
3083 let calls: Vec<_> = data_enum
3084 .variants
3085 .iter()
3086 .map(|variant| {
3087 let v_ident = &variant.ident;
3088 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
3089 quote! { rust_key_paths::AKp::new(Self::#snake()) }
3090 })
3091 .collect();
3092 quote! { #(#calls),* }
3093 }
3094 Data::Union(_) => {
3095 return syn::Error::new(
3096 input.ident.span(),
3097 "Akp derive does not support unions",
3098 )
3099 .to_compile_error()
3100 .into();
3101 }
3102 };
3103
3104 let expanded = quote! {
3105 impl #name {
3106 /// Returns a vec of all field keypaths as any keypaths (fully type-erased).
3107 #[inline(always)]
3108 pub fn any_kps() -> Vec<rust_key_paths::AKp> {
3109 vec![#kp_calls]
3110 }
3111 }
3112 };
3113
3114 TokenStream::from(expanded)
3115}