key_paths_derive/lib.rs
1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{Data, DeriveInput, Fields, Type, parse_macro_input};
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 // Nested container support
21 OptionBox,
22 OptionRc,
23 OptionArc,
24 BoxOption,
25 RcOption,
26 ArcOption,
27 VecOption,
28 OptionVec,
29 HashMapOption,
30 OptionHashMap,
31}
32
33#[proc_macro_derive(Keypaths)]
34pub fn derive_keypaths(input: TokenStream) -> TokenStream {
35 let input = parse_macro_input!(input as DeriveInput);
36 let name = input.ident;
37
38 let methods = match input.data {
39 Data::Struct(data_struct) => match data_struct.fields {
40 Fields::Named(fields_named) => {
41 let mut tokens = proc_macro2::TokenStream::new();
42 for field in fields_named.named.iter() {
43 let field_ident = field.ident.as_ref().unwrap();
44 let ty = &field.ty;
45
46 let r_fn = format_ident!("{}_r", field_ident);
47 let w_fn = format_ident!("{}_w", field_ident);
48 let fr_fn = format_ident!("{}_fr", field_ident);
49 let fw_fn = format_ident!("{}_fw", field_ident);
50 let fr_at_fn = format_ident!("{}_fr_at", field_ident);
51 let fw_at_fn = format_ident!("{}_fw_at", field_ident);
52
53 let (kind, inner_ty) = extract_wrapper_inner_type(ty);
54
55 match (kind, inner_ty) {
56 (WrapperKind::Option, Some(inner_ty)) => {
57 tokens.extend(quote! {
58 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
59 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
60 }
61 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
62 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
63 }
64 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
65 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.as_ref())
66 }
67 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
68 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#field_ident.as_mut())
69 }
70 });
71 }
72 (WrapperKind::Vec, Some(inner_ty)) => {
73 tokens.extend(quote! {
74 pub fn #fr_at_fn(index: usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
75 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.get(index))
76 }
77 pub fn #fw_at_fn(index: usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
78 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.get_mut(index))
79 }
80 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
81 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
82 }
83 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
84 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
85 }
86 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
87 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.first())
88 }
89 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
90 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#field_ident.first_mut())
91 }
92 });
93 }
94 (WrapperKind::HashMap, Some(inner_ty)) => {
95 tokens.extend(quote! {
96 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
97 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
98 }
99 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
100 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
101 }
102 pub fn #fr_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
103 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.get(&key))
104 }
105 pub fn #fw_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
106 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.get_mut(&key))
107 }
108 pub fn #fr_at_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
109 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.get(&key))
110 }
111 pub fn #fw_at_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
112 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.get_mut(&key))
113 }
114 });
115 }
116 (WrapperKind::Box, Some(inner_ty)) => {
117 tokens.extend(quote! {
118 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
119 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#field_ident)
120 }
121 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
122 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut *s.#field_ident)
123 }
124 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
125 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#field_ident))
126 }
127 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
128 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut *s.#field_ident))
129 }
130 });
131 }
132 (WrapperKind::Rc, Some(inner_ty)) | (WrapperKind::Arc, Some(inner_ty)) => {
133 tokens.extend(quote! {
134 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
135 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#field_ident)
136 }
137 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
138 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#field_ident))
139 }
140 });
141 }
142 (WrapperKind::BTreeMap, Some(inner_ty)) => {
143 tokens.extend(quote! {
144 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
145 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
146 }
147 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
148 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
149 }
150 // Note: Key-based access methods for BTreeMap require the exact key type
151 // For now, we'll skip generating these methods to avoid generic constraint issues
152 });
153 }
154 (WrapperKind::HashSet, Some(inner_ty)) => {
155 tokens.extend(quote! {
156 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
157 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
158 }
159 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
160 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
161 }
162 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
163 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.iter().next())
164 }
165 });
166 }
167 (WrapperKind::BTreeSet, Some(inner_ty)) => {
168 tokens.extend(quote! {
169 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
170 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
171 }
172 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
173 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
174 }
175 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
176 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.iter().next())
177 }
178 });
179 }
180 (WrapperKind::VecDeque, Some(inner_ty)) => {
181 tokens.extend(quote! {
182 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
183 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
184 }
185 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
186 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
187 }
188 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
189 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.front())
190 }
191 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
192 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#field_ident.front_mut())
193 }
194 pub fn #fr_at_fn(index: usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
195 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.get(index))
196 }
197 pub fn #fw_at_fn(index: usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
198 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.get_mut(index))
199 }
200 });
201 }
202 (WrapperKind::LinkedList, Some(inner_ty)) => {
203 tokens.extend(quote! {
204 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
205 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
206 }
207 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
208 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
209 }
210 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
211 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.front())
212 }
213 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
214 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#field_ident.front_mut())
215 }
216 });
217 }
218 (WrapperKind::BinaryHeap, Some(inner_ty)) => {
219 tokens.extend(quote! {
220 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
221 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
222 }
223 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
224 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
225 }
226 // Note: BinaryHeap peek() returns &T, but we need &inner_ty
227 // For now, we'll skip failable methods for BinaryHeap to avoid type issues
228 });
229 }
230 // Nested container combinations - COMMENTED OUT FOR NOW
231 // TODO: Fix type mismatch issues in nested combinations
232 /*
233 (WrapperKind::OptionBox, Some(inner_ty)) => {
234 tokens.extend(quote! {
235 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
236 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
237 }
238 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
239 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
240 }
241 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
242 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.as_ref().map(|b| &**b))
243 }
244 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
245 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#field_ident.as_mut().map(|b| &mut **b))
246 }
247 });
248 }
249 (WrapperKind::OptionRc, Some(inner_ty)) => {
250 tokens.extend(quote! {
251 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
252 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
253 }
254 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
255 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.as_ref().map(|r| &**r))
256 }
257 });
258 }
259 (WrapperKind::OptionArc, Some(inner_ty)) => {
260 tokens.extend(quote! {
261 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
262 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
263 }
264 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
265 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.as_ref().map(|a| &**a))
266 }
267 });
268 }
269 (WrapperKind::BoxOption, Some(inner_ty)) => {
270 tokens.extend(quote! {
271 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
272 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#field_ident)
273 }
274 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
275 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut *s.#field_ident)
276 }
277 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
278 key_paths_core::KeyPaths::failable_readable(|s: &#name| (*s.#field_ident).as_ref())
279 }
280 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
281 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| (*s.#field_ident).as_mut())
282 }
283 });
284 }
285 (WrapperKind::RcOption, Some(inner_ty)) => {
286 tokens.extend(quote! {
287 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
288 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#field_ident)
289 }
290 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
291 key_paths_core::KeyPaths::failable_readable(|s: &#name| (*s.#field_ident).as_ref())
292 }
293 });
294 }
295 (WrapperKind::ArcOption, Some(inner_ty)) => {
296 tokens.extend(quote! {
297 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
298 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#field_ident)
299 }
300 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
301 key_paths_core::KeyPaths::failable_readable(|s: &#name| (*s.#field_ident).as_ref())
302 }
303 });
304 }
305 (WrapperKind::VecOption, Some(inner_ty)) => {
306 tokens.extend(quote! {
307 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
308 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
309 }
310 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
311 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
312 }
313 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
314 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.first().and_then(|opt| opt.as_ref()))
315 }
316 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
317 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#field_ident.first_mut().and_then(|opt| opt.as_mut()))
318 }
319 pub fn #fr_at_fn(index: usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
320 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.get(index).and_then(|opt| opt.as_ref()))
321 }
322 pub fn #fw_at_fn(index: usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
323 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.get_mut(index).and_then(|opt| opt.as_mut()))
324 }
325 });
326 }
327 (WrapperKind::OptionVec, Some(inner_ty)) => {
328 tokens.extend(quote! {
329 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
330 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
331 }
332 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
333 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
334 }
335 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
336 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.as_ref().and_then(|v| v.first()))
337 }
338 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
339 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#field_ident.as_mut().and_then(|v| v.first_mut()))
340 }
341 pub fn #fr_at_fn(index: usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
342 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.as_ref().and_then(|v| v.get(index)))
343 }
344 pub fn #fw_at_fn(index: usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
345 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.as_mut().and_then(|v| v.get_mut(index)))
346 }
347 });
348 }
349 (WrapperKind::HashMapOption, Some(inner_ty)) => {
350 tokens.extend(quote! {
351 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
352 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
353 }
354 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
355 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
356 }
357 pub fn #fr_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
358 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.get(&key).and_then(|opt| opt.as_ref()))
359 }
360 pub fn #fw_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
361 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.get_mut(&key).and_then(|opt| opt.as_mut()))
362 }
363 pub fn #fr_at_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
364 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.get(&key).and_then(|opt| opt.as_ref()))
365 }
366 pub fn #fw_at_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
367 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.get_mut(&key).and_then(|opt| opt.as_mut()))
368 }
369 });
370 }
371 (WrapperKind::OptionHashMap, Some(inner_ty)) => {
372 tokens.extend(quote! {
373 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
374 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
375 }
376 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
377 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
378 }
379 pub fn #fr_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
380 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.as_ref().and_then(|m| m.get(&key)))
381 }
382 pub fn #fw_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
383 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.as_mut().and_then(|m| m.get_mut(&key)))
384 }
385 pub fn #fr_at_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
386 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.as_ref().and_then(|m| m.get(&key)))
387 }
388 pub fn #fw_at_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
389 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.as_mut().and_then(|m| m.get_mut(&key)))
390 }
391 });
392 }
393 */
394 (WrapperKind::None, None) => {
395 tokens.extend(quote! {
396 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
397 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
398 }
399 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
400 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
401 }
402 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #ty> {
403 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&s.#field_ident))
404 }
405 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #ty> {
406 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut s.#field_ident))
407 }
408 });
409 }
410 _ => {
411 tokens.extend(quote! {
412 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
413 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
414 }
415 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
416 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
417 }
418 });
419 }
420 }
421 }
422 tokens
423 }
424 Fields::Unnamed(unnamed) => {
425 let mut tokens = proc_macro2::TokenStream::new();
426 for (idx, field) in unnamed.unnamed.iter().enumerate() {
427 let idx_lit = syn::Index::from(idx);
428 let ty = &field.ty;
429
430 let r_fn = format_ident!("f{}_r", idx);
431 let w_fn = format_ident!("f{}_w", idx);
432 let fr_fn = format_ident!("f{}_fr", idx);
433 let fw_fn = format_ident!("f{}_fw", idx);
434 let fr_at_fn = format_ident!("f{}_fr_at", idx);
435 let fw_at_fn = format_ident!("f{}_fw_at", idx);
436
437 let (kind, inner_ty) = extract_wrapper_inner_type(ty);
438
439 match (kind, inner_ty) {
440 (WrapperKind::Option, Some(inner_ty)) => {
441 tokens.extend(quote! {
442 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
443 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
444 }
445 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
446 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
447 }
448 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
449 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.as_ref())
450 }
451 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
452 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.as_mut())
453 }
454 });
455 }
456 (WrapperKind::Vec, Some(inner_ty)) => {
457 tokens.extend(quote! {
458 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
459 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
460 }
461 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
462 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
463 }
464 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
465 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.first())
466 }
467 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
468 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.first_mut())
469 }
470 pub fn #fr_at_fn(index: &'static usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
471 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.get(*index))
472 }
473 pub fn #fw_at_fn(index: &'static usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
474 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.get_mut(*index))
475 }
476 });
477 }
478 (WrapperKind::HashMap, Some(inner_ty)) => {
479 tokens.extend(quote! {
480 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
481 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
482 }
483 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
484 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
485 }
486 pub fn #fr_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
487 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#idx_lit.get(&key))
488 }
489 pub fn #fw_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
490 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#idx_lit.get_mut(&key))
491 }
492 pub fn #fr_at_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
493 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#idx_lit.get(&key))
494 }
495 pub fn #fw_at_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
496 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#idx_lit.get_mut(&key))
497 }
498 });
499 }
500 (WrapperKind::Box, Some(inner_ty)) => {
501 tokens.extend(quote! {
502 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
503 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#idx_lit)
504 }
505 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
506 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut *s.#idx_lit)
507 }
508 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
509 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#idx_lit))
510 }
511 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
512 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut *s.#idx_lit))
513 }
514 });
515 }
516 (WrapperKind::Rc, Some(inner_ty)) | (WrapperKind::Arc, Some(inner_ty)) => {
517 tokens.extend(quote! {
518 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
519 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#idx_lit)
520 }
521 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
522 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#idx_lit))
523 }
524 });
525 }
526 (WrapperKind::BTreeMap, Some(inner_ty)) => {
527 tokens.extend(quote! {
528 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
529 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
530 }
531 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
532 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
533 }
534 // Note: Key-based access methods for BTreeMap require the exact key type
535 // For now, we'll skip generating these methods to avoid generic constraint issues
536 });
537 }
538 (WrapperKind::HashSet, Some(inner_ty)) => {
539 tokens.extend(quote! {
540 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
541 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
542 }
543 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
544 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
545 }
546 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
547 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.iter().next())
548 }
549 });
550 }
551 (WrapperKind::BTreeSet, Some(inner_ty)) => {
552 tokens.extend(quote! {
553 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
554 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
555 }
556 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
557 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
558 }
559 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
560 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.iter().next())
561 }
562 });
563 }
564 (WrapperKind::VecDeque, Some(inner_ty)) => {
565 tokens.extend(quote! {
566 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
567 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
568 }
569 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
570 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
571 }
572 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
573 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.front())
574 }
575 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
576 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.front_mut())
577 }
578 });
579 }
580 (WrapperKind::LinkedList, Some(inner_ty)) => {
581 tokens.extend(quote! {
582 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
583 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
584 }
585 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
586 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
587 }
588 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
589 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.front())
590 }
591 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
592 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.front_mut())
593 }
594 });
595 }
596 (WrapperKind::BinaryHeap, Some(inner_ty)) => {
597 tokens.extend(quote! {
598 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
599 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
600 }
601 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
602 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
603 }
604 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
605 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.peek())
606 }
607 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
608 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.peek_mut().map(|v| &mut **v))
609 }
610 });
611 }
612 // Nested container combinations for tuple structs - COMMENTED OUT FOR NOW
613 /*
614 (WrapperKind::OptionBox, Some(inner_ty)) => {
615 tokens.extend(quote! {
616 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
617 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
618 }
619 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
620 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
621 }
622 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
623 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.as_ref().map(|b| &**b))
624 }
625 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
626 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.as_mut().map(|b| &mut **b))
627 }
628 });
629 }
630 (WrapperKind::OptionRc, Some(inner_ty)) => {
631 tokens.extend(quote! {
632 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
633 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
634 }
635 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
636 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.as_ref().map(|r| &**r))
637 }
638 });
639 }
640 (WrapperKind::OptionArc, Some(inner_ty)) => {
641 tokens.extend(quote! {
642 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
643 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
644 }
645 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
646 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.as_ref().map(|a| &**a))
647 }
648 });
649 }
650 (WrapperKind::BoxOption, Some(inner_ty)) => {
651 tokens.extend(quote! {
652 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
653 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#idx_lit)
654 }
655 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
656 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut *s.#idx_lit)
657 }
658 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
659 key_paths_core::KeyPaths::failable_readable(|s: &#name| (*s.#idx_lit).as_ref())
660 }
661 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
662 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| (*s.#idx_lit).as_mut())
663 }
664 });
665 }
666 (WrapperKind::RcOption, Some(inner_ty)) => {
667 tokens.extend(quote! {
668 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
669 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#idx_lit)
670 }
671 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
672 key_paths_core::KeyPaths::failable_readable(|s: &#name| (*s.#idx_lit).as_ref())
673 }
674 });
675 }
676 (WrapperKind::ArcOption, Some(inner_ty)) => {
677 tokens.extend(quote! {
678 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
679 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#idx_lit)
680 }
681 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
682 key_paths_core::KeyPaths::failable_readable(|s: &#name| (*s.#idx_lit).as_ref())
683 }
684 });
685 }
686 (WrapperKind::VecOption, Some(inner_ty)) => {
687 tokens.extend(quote! {
688 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
689 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
690 }
691 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
692 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
693 }
694 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
695 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.first().and_then(|opt| opt.as_ref()))
696 }
697 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
698 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.first_mut().and_then(|opt| opt.as_mut()))
699 }
700 });
701 }
702 (WrapperKind::OptionVec, Some(inner_ty)) => {
703 tokens.extend(quote! {
704 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
705 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
706 }
707 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
708 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
709 }
710 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
711 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.as_ref().and_then(|v| v.first()))
712 }
713 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
714 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.as_mut().and_then(|v| v.first_mut()))
715 }
716 });
717 }
718 (WrapperKind::HashMapOption, Some(inner_ty)) => {
719 tokens.extend(quote! {
720 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
721 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
722 }
723 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
724 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
725 }
726 pub fn #fr_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
727 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#idx_lit.get(&key).and_then(|opt| opt.as_ref()))
728 }
729 pub fn #fw_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
730 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#idx_lit.get_mut(&key).and_then(|opt| opt.as_mut()))
731 }
732 });
733 }
734 (WrapperKind::OptionHashMap, Some(inner_ty)) => {
735 tokens.extend(quote! {
736 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
737 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
738 }
739 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
740 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
741 }
742 pub fn #fr_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
743 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#idx_lit.as_ref().and_then(|m| m.get(&key)))
744 }
745 pub fn #fw_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
746 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#idx_lit.as_mut().and_then(|m| m.get_mut(&key)))
747 }
748 });
749 }
750 */
751 (WrapperKind::None, None) => {
752 tokens.extend(quote! {
753 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
754 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
755 }
756 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
757 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
758 }
759 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #ty> {
760 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&s.#idx_lit))
761 }
762 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #ty> {
763 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut s.#idx_lit))
764 }
765 });
766 }
767 _ => {
768 tokens.extend(quote! {
769 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
770 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
771 }
772 });
773 }
774 }
775 }
776 tokens
777 }
778 _ => quote! {
779 compile_error!("Keypaths derive supports only structs with named or unnamed fields");
780 },
781 },
782 Data::Enum(data_enum) => {
783 let mut tokens = proc_macro2::TokenStream::new();
784 for variant in data_enum.variants.iter() {
785 let v_ident = &variant.ident;
786 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
787 let r_fn = format_ident!("{}_case_r", snake);
788 let w_fn = format_ident!("{}_case_w", snake);
789 let fr_fn = format_ident!("{}_case_fr", snake);
790 let fw_fn = format_ident!("{}_case_fw", snake);
791 let fr_at_fn = format_ident!("{}_case_fr_at", snake);
792 let fw_at_fn = format_ident!("{}_case_fw_at", snake);
793
794 match &variant.fields {
795 Fields::Unit => {
796 tokens.extend(quote! {
797 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, ()> {
798 static UNIT: () = ();
799 key_paths_core::KeyPaths::readable_enum(
800 |_| #name::#v_ident,
801 |e: &#name| match e { #name::#v_ident => Some(&UNIT), _ => None }
802 )
803 }
804 });
805 }
806 Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => {
807 let field_ty = &unnamed.unnamed.first().unwrap().ty;
808 let (kind, inner_ty_opt) = extract_wrapper_inner_type(field_ty);
809
810 match (kind, inner_ty_opt) {
811 (WrapperKind::Option, Some(inner_ty)) => {
812 tokens.extend(quote! {
813 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
814 key_paths_core::KeyPaths::readable_enum(
815 #name::#v_ident,
816 |e: &#name| match e { #name::#v_ident(v) => v.as_ref(), _ => None }
817 )
818 }
819 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
820 key_paths_core::KeyPaths::writable_enum(
821 #name::#v_ident,
822 |e: &#name| match e { #name::#v_ident(v) => v.as_ref(), _ => None },
823 |e: &mut #name| match e { #name::#v_ident(v) => v.as_mut(), _ => None },
824 )
825 }
826 });
827 }
828 (WrapperKind::Vec, Some(inner_ty)) => {
829 tokens.extend(quote! {
830 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
831 key_paths_core::KeyPaths::readable_enum(
832 #name::#v_ident,
833 |e: &#name| match e { #name::#v_ident(v) => v.first(), _ => None }
834 )
835 }
836 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
837 key_paths_core::KeyPaths::writable_enum(
838 #name::#v_ident,
839 |e: &#name| match e { #name::#v_ident(v) => v.first(), _ => None },
840 |e: &mut #name| match e { #name::#v_ident(v) => v.first_mut(), _ => None },
841 )
842 }
843 pub fn #fr_at_fn(index: &'static usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
844 key_paths_core::KeyPaths::readable_enum(
845 #name::#v_ident,
846 |e: &#name| match e { #name::#v_ident(v) => v.get(*index), _ => None }
847 )
848 }
849 pub fn #fw_at_fn(index: &'static usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
850 key_paths_core::KeyPaths::writable_enum(
851 #name::#v_ident,
852 |e: &#name| match e { #name::#v_ident(v) => v.get(*index), _ => None },
853 |e: &mut #name| match e { #name::#v_ident(v) => v.get_mut(*index), _ => None },
854 )
855 }
856 });
857 }
858 (WrapperKind::HashMap, Some(inner_ty)) => {
859 tokens.extend(quote! {
860 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
861 key_paths_core::KeyPaths::readable_enum(
862 #name::#v_ident,
863 |e: &#name| match e { #name::#v_ident(v) => v.first().map(|(_, v)| v), _ => None }
864 )
865 }
866 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
867 key_paths_core::KeyPaths::writable_enum(
868 #name::#v_ident,
869 |e: &#name| match e { #name::#v_ident(v) => v.first().map(|(_, v)| v), _ => None },
870 |e: &mut #name| match e { #name::#v_ident(v) => v.first_mut().map(|(_, v)| v), _ => None },
871 )
872 }
873 pub fn #fr_at_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: &'static K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
874 key_paths_core::KeyPaths::readable_enum(
875 #name::#v_ident,
876 |e: &#name| match e { #name::#v_ident(v) => v.get(key), _ => None }
877 )
878 }
879 pub fn #fw_at_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: &'static K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
880 key_paths_core::KeyPaths::writable_enum(
881 #name::#v_ident,
882 |e: &#name| match e { #name::#v_ident(v) => v.get(key), _ => None },
883 |e: &mut #name| match e { #name::#v_ident(v) => v.get_mut(key), _ => None },
884 )
885 }
886 });
887 }
888 (WrapperKind::Box, Some(inner_ty)) => {
889 tokens.extend(quote! {
890 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
891 key_paths_core::KeyPaths::readable_enum(
892 #name::#v_ident,
893 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None }
894 )
895 }
896 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
897 key_paths_core::KeyPaths::writable_enum(
898 #name::#v_ident,
899 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None },
900 |e: &mut #name| match e { #name::#v_ident(v) => Some(&mut *v), _ => None },
901 )
902 }
903 });
904 }
905 (WrapperKind::Rc, Some(inner_ty))
906 | (WrapperKind::Arc, Some(inner_ty)) => {
907 tokens.extend(quote! {
908 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
909 key_paths_core::KeyPaths::readable_enum(
910 #name::#v_ident,
911 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None }
912 )
913 }
914 });
915 }
916 (WrapperKind::BTreeMap, Some(inner_ty)) => {
917 tokens.extend(quote! {
918 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
919 key_paths_core::KeyPaths::readable_enum(
920 #name::#v_ident,
921 |e: &#name| match e { #name::#v_ident(v) => v.first().map(|(_, v)| v), _ => None }
922 )
923 }
924 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
925 key_paths_core::KeyPaths::writable_enum(
926 #name::#v_ident,
927 |e: &#name| match e { #name::#v_ident(v) => v.first().map(|(_, v)| v), _ => None },
928 |e: &mut #name| match e { #name::#v_ident(v) => v.first_mut().map(|(_, v)| v), _ => None },
929 )
930 }
931 });
932 }
933 (WrapperKind::HashSet, Some(inner_ty)) => {
934 tokens.extend(quote! {
935 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
936 key_paths_core::KeyPaths::readable_enum(
937 #name::#v_ident,
938 |e: &#name| match e { #name::#v_ident(v) => v.iter().next(), _ => None }
939 )
940 }
941 });
942 }
943 (WrapperKind::BTreeSet, Some(inner_ty)) => {
944 tokens.extend(quote! {
945 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
946 key_paths_core::KeyPaths::readable_enum(
947 #name::#v_ident,
948 |e: &#name| match e { #name::#v_ident(v) => v.iter().next(), _ => None }
949 )
950 }
951 });
952 }
953 (WrapperKind::VecDeque, Some(inner_ty)) => {
954 tokens.extend(quote! {
955 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
956 key_paths_core::KeyPaths::readable_enum(
957 #name::#v_ident,
958 |e: &#name| match e { #name::#v_ident(v) => v.front(), _ => None }
959 )
960 }
961 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
962 key_paths_core::KeyPaths::writable_enum(
963 #name::#v_ident,
964 |e: &#name| match e { #name::#v_ident(v) => v.front(), _ => None },
965 |e: &mut #name| match e { #name::#v_ident(v) => v.front_mut(), _ => None },
966 )
967 }
968 });
969 }
970 (WrapperKind::LinkedList, Some(inner_ty)) => {
971 tokens.extend(quote! {
972 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
973 key_paths_core::KeyPaths::readable_enum(
974 #name::#v_ident,
975 |e: &#name| match e { #name::#v_ident(v) => v.front(), _ => None }
976 )
977 }
978 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
979 key_paths_core::KeyPaths::writable_enum(
980 #name::#v_ident,
981 |e: &#name| match e { #name::#v_ident(v) => v.front(), _ => None },
982 |e: &mut #name| match e { #name::#v_ident(v) => v.front_mut(), _ => None },
983 )
984 }
985 });
986 }
987 (WrapperKind::BinaryHeap, Some(inner_ty)) => {
988 tokens.extend(quote! {
989 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
990 key_paths_core::KeyPaths::readable_enum(
991 #name::#v_ident,
992 |e: &#name| match e { #name::#v_ident(v) => v.peek(), _ => None }
993 )
994 }
995 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
996 key_paths_core::KeyPaths::writable_enum(
997 #name::#v_ident,
998 |e: &#name| match e { #name::#v_ident(v) => v.peek(), _ => None },
999 |e: &mut #name| match e { #name::#v_ident(v) => v.peek_mut().map(|v| &mut **v), _ => None },
1000 )
1001 }
1002 });
1003 }
1004 // Nested container combinations for enums - COMMENTED OUT FOR NOW
1005 /*
1006 (WrapperKind::OptionBox, Some(inner_ty)) => {
1007 tokens.extend(quote! {
1008 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1009 key_paths_core::KeyPaths::readable_enum(
1010 #name::#v_ident,
1011 |e: &#name| match e { #name::#v_ident(v) => v.as_ref().map(|b| &**b), _ => None }
1012 )
1013 }
1014 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1015 key_paths_core::KeyPaths::writable_enum(
1016 #name::#v_ident,
1017 |e: &#name| match e { #name::#v_ident(v) => v.as_ref().map(|b| &**b), _ => None },
1018 |e: &mut #name| match e { #name::#v_ident(v) => v.as_mut().map(|b| &mut **b), _ => None },
1019 )
1020 }
1021 });
1022 }
1023 (WrapperKind::OptionRc, Some(inner_ty)) => {
1024 tokens.extend(quote! {
1025 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1026 key_paths_core::KeyPaths::readable_enum(
1027 #name::#v_ident,
1028 |e: &#name| match e { #name::#v_ident(v) => v.as_ref().map(|r| &**r), _ => None }
1029 )
1030 }
1031 });
1032 }
1033 (WrapperKind::OptionArc, Some(inner_ty)) => {
1034 tokens.extend(quote! {
1035 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1036 key_paths_core::KeyPaths::readable_enum(
1037 #name::#v_ident,
1038 |e: &#name| match e { #name::#v_ident(v) => v.as_ref().map(|a| &**a), _ => None }
1039 )
1040 }
1041 });
1042 }
1043 (WrapperKind::BoxOption, Some(inner_ty)) => {
1044 tokens.extend(quote! {
1045 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #field_ty> {
1046 key_paths_core::KeyPaths::readable_enum(
1047 #name::#v_ident,
1048 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None }
1049 )
1050 }
1051 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #field_ty> {
1052 key_paths_core::KeyPaths::writable_enum(
1053 #name::#v_ident,
1054 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None },
1055 |e: &mut #name| match e { #name::#v_ident(v) => Some(&mut *v), _ => None },
1056 )
1057 }
1058 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1059 key_paths_core::KeyPaths::readable_enum(
1060 #name::#v_ident,
1061 |e: &#name| match e { #name::#v_ident(v) => (*v).as_ref(), _ => None }
1062 )
1063 }
1064 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1065 key_paths_core::KeyPaths::writable_enum(
1066 #name::#v_ident,
1067 |e: &#name| match e { #name::#v_ident(v) => (*v).as_ref(), _ => None },
1068 |e: &mut #name| match e { #name::#v_ident(v) => (*v).as_mut(), _ => None },
1069 )
1070 }
1071 });
1072 }
1073 (WrapperKind::RcOption, Some(inner_ty)) => {
1074 tokens.extend(quote! {
1075 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #field_ty> {
1076 key_paths_core::KeyPaths::readable_enum(
1077 #name::#v_ident,
1078 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None }
1079 )
1080 }
1081 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1082 key_paths_core::KeyPaths::readable_enum(
1083 #name::#v_ident,
1084 |e: &#name| match e { #name::#v_ident(v) => (*v).as_ref(), _ => None }
1085 )
1086 }
1087 });
1088 }
1089 (WrapperKind::ArcOption, Some(inner_ty)) => {
1090 tokens.extend(quote! {
1091 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #field_ty> {
1092 key_paths_core::KeyPaths::readable_enum(
1093 #name::#v_ident,
1094 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None }
1095 )
1096 }
1097 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1098 key_paths_core::KeyPaths::readable_enum(
1099 #name::#v_ident,
1100 |e: &#name| match e { #name::#v_ident(v) => (*v).as_ref(), _ => None }
1101 )
1102 }
1103 });
1104 }
1105 (WrapperKind::VecOption, Some(inner_ty)) => {
1106 tokens.extend(quote! {
1107 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1108 key_paths_core::KeyPaths::readable_enum(
1109 #name::#v_ident,
1110 |e: &#name| match e { #name::#v_ident(v) => v.first().and_then(|opt| opt.as_ref()), _ => None }
1111 )
1112 }
1113 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1114 key_paths_core::KeyPaths::writable_enum(
1115 #name::#v_ident,
1116 |e: &#name| match e { #name::#v_ident(v) => v.first().and_then(|opt| opt.as_ref()), _ => None },
1117 |e: &mut #name| match e { #name::#v_ident(v) => v.first_mut().and_then(|opt| opt.as_mut()), _ => None },
1118 )
1119 }
1120 });
1121 }
1122 (WrapperKind::OptionVec, Some(inner_ty)) => {
1123 tokens.extend(quote! {
1124 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1125 key_paths_core::KeyPaths::readable_enum(
1126 #name::#v_ident,
1127 |e: &#name| match e { #name::#v_ident(v) => v.as_ref().and_then(|vec| vec.first()), _ => None }
1128 )
1129 }
1130 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1131 key_paths_core::KeyPaths::writable_enum(
1132 #name::#v_ident,
1133 |e: &#name| match e { #name::#v_ident(v) => v.as_ref().and_then(|vec| vec.first()), _ => None },
1134 |e: &mut #name| match e { #name::#v_ident(v) => v.as_mut().and_then(|vec| vec.first_mut()), _ => None },
1135 )
1136 }
1137 });
1138 }
1139 (WrapperKind::HashMapOption, Some(inner_ty)) => {
1140 tokens.extend(quote! {
1141 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1142 key_paths_core::KeyPaths::readable_enum(
1143 #name::#v_ident,
1144 |e: &#name| match e { #name::#v_ident(v) => v.first().and_then(|(_, opt)| opt.as_ref()), _ => None }
1145 )
1146 }
1147 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1148 key_paths_core::KeyPaths::writable_enum(
1149 #name::#v_ident,
1150 |e: &#name| match e { #name::#v_ident(v) => v.first().and_then(|(_, opt)| opt.as_ref()), _ => None },
1151 |e: &mut #name| match e { #name::#v_ident(v) => v.first_mut().and_then(|(_, opt)| opt.as_mut()), _ => None },
1152 )
1153 }
1154 });
1155 }
1156 (WrapperKind::OptionHashMap, Some(inner_ty)) => {
1157 tokens.extend(quote! {
1158 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1159 key_paths_core::KeyPaths::readable_enum(
1160 #name::#v_ident,
1161 |e: &#name| match e { #name::#v_ident(v) => v.as_ref().and_then(|map| map.first().map(|(_, v)| v)), _ => None }
1162 )
1163 }
1164 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1165 key_paths_core::KeyPaths::writable_enum(
1166 #name::#v_ident,
1167 |e: &#name| match e { #name::#v_ident(v) => v.as_ref().and_then(|map| map.first().map(|(_, v)| v)), _ => None },
1168 |e: &mut #name| match e { #name::#v_ident(v) => v.as_mut().and_then(|map| map.first_mut().map(|(_, v)| v)), _ => None },
1169 )
1170 }
1171 });
1172 }
1173 */
1174 (WrapperKind::None, None) => {
1175 let inner_ty = field_ty;
1176 tokens.extend(quote! {
1177 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1178 key_paths_core::KeyPaths::readable_enum(
1179 #name::#v_ident,
1180 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None }
1181 )
1182 }
1183 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1184 key_paths_core::KeyPaths::writable_enum(
1185 #name::#v_ident,
1186 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None },
1187 |e: &mut #name| match e { #name::#v_ident(v) => Some(v), _ => None },
1188 )
1189 }
1190 });
1191 }
1192 _ => {
1193 tokens.extend(quote! {
1194 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #field_ty> {
1195 key_paths_core::KeyPaths::readable_enum(
1196 #name::#v_ident,
1197 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None }
1198 )
1199 }
1200 });
1201 }
1202 }
1203 }
1204 _ => {
1205 tokens.extend(quote! {
1206 compile_error!("Casepaths derive supports only unit and single-field tuple variants");
1207 });
1208 }
1209 }
1210 }
1211 tokens
1212 }
1213 _ => quote! {
1214 compile_error!("Keypaths derive supports only structs and enums");
1215 },
1216 };
1217
1218 let expanded = quote! {
1219 impl #name {
1220 #methods
1221 }
1222 };
1223
1224 TokenStream::from(expanded)
1225}
1226
1227fn extract_wrapper_inner_type(ty: &Type) -> (WrapperKind, Option<Type>) {
1228 use syn::{GenericArgument, PathArguments};
1229
1230 if let Type::Path(tp) = ty {
1231 if let Some(seg) = tp.path.segments.last() {
1232 let ident_str = seg.ident.to_string();
1233
1234 if let PathArguments::AngleBracketed(ab) = &seg.arguments {
1235 let args: Vec<_> = ab.args.iter().collect();
1236
1237 // Handle map types (HashMap, BTreeMap) - they have K, V parameters
1238 if ident_str == "HashMap" || ident_str == "BTreeMap" {
1239 if let (Some(_key_arg), Some(value_arg)) = (args.get(0), args.get(1)) {
1240 if let GenericArgument::Type(inner) = value_arg {
1241 eprintln!("Detected {} type, extracting value type", ident_str);
1242 return match ident_str.as_str() {
1243 "HashMap" => (WrapperKind::HashMap, Some(inner.clone())),
1244 "BTreeMap" => (WrapperKind::BTreeMap, Some(inner.clone())),
1245 _ => (WrapperKind::None, None),
1246 };
1247 }
1248 }
1249 }
1250 // Handle single-parameter container types
1251 else if let Some(arg) = args.get(0) {
1252 if let GenericArgument::Type(inner) = arg {
1253 // Check for nested containers first
1254 let (inner_kind, inner_inner) = extract_wrapper_inner_type(inner);
1255
1256 // Handle nested combinations
1257 match (ident_str.as_str(), inner_kind) {
1258 ("Option", WrapperKind::Box) => {
1259 return (WrapperKind::OptionBox, inner_inner);
1260 }
1261 ("Option", WrapperKind::Rc) => {
1262 return (WrapperKind::OptionRc, inner_inner);
1263 }
1264 ("Option", WrapperKind::Arc) => {
1265 return (WrapperKind::OptionArc, inner_inner);
1266 }
1267 ("Option", WrapperKind::Vec) => {
1268 return (WrapperKind::OptionVec, inner_inner);
1269 }
1270 ("Option", WrapperKind::HashMap) => {
1271 return (WrapperKind::OptionHashMap, inner_inner);
1272 }
1273 ("Box", WrapperKind::Option) => {
1274 return (WrapperKind::BoxOption, inner_inner);
1275 }
1276 ("Rc", WrapperKind::Option) => {
1277 return (WrapperKind::RcOption, inner_inner);
1278 }
1279 ("Arc", WrapperKind::Option) => {
1280 return (WrapperKind::ArcOption, inner_inner);
1281 }
1282 ("Vec", WrapperKind::Option) => {
1283 return (WrapperKind::VecOption, inner_inner);
1284 }
1285 ("HashMap", WrapperKind::Option) => {
1286 return (WrapperKind::HashMapOption, inner_inner);
1287 }
1288 _ => {
1289 // Handle single-level containers
1290 return match ident_str.as_str() {
1291 "Option" => (WrapperKind::Option, Some(inner.clone())),
1292 "Box" => (WrapperKind::Box, Some(inner.clone())),
1293 "Rc" => (WrapperKind::Rc, Some(inner.clone())),
1294 "Arc" => (WrapperKind::Arc, Some(inner.clone())),
1295 "Vec" => (WrapperKind::Vec, Some(inner.clone())),
1296 "HashSet" => (WrapperKind::HashSet, Some(inner.clone())),
1297 "BTreeSet" => (WrapperKind::BTreeSet, Some(inner.clone())),
1298 "VecDeque" => (WrapperKind::VecDeque, Some(inner.clone())),
1299 "LinkedList" => (WrapperKind::LinkedList, Some(inner.clone())),
1300 "BinaryHeap" => (WrapperKind::BinaryHeap, Some(inner.clone())),
1301 _ => (WrapperKind::None, None),
1302 };
1303 }
1304 }
1305 }
1306 }
1307 }
1308 }
1309 }
1310 (WrapperKind::None, None)
1311}
1312
1313
1314fn to_snake_case(name: &str) -> String {
1315 let mut out = String::new();
1316 for (i, c) in name.chars().enumerate() {
1317 if c.is_uppercase() {
1318 if i != 0 {
1319 out.push('_');
1320 }
1321 out.push(c.to_ascii_lowercase());
1322 } else {
1323 out.push(c);
1324 }
1325 }
1326 out
1327}
1328
1329#[proc_macro_derive(Casepaths)]
1330pub fn derive_casepaths(input: TokenStream) -> TokenStream {
1331 let input = parse_macro_input!(input as DeriveInput);
1332 let name = input.ident;
1333
1334 let tokens = match input.data {
1335 Data::Enum(data_enum) => {
1336 let mut tokens = proc_macro2::TokenStream::new();
1337 for variant in data_enum.variants.iter() {
1338 let v_ident = &variant.ident;
1339 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
1340 let r_fn = format_ident!("{}_case_r", snake);
1341 let w_fn = format_ident!("{}_case_w", snake);
1342
1343 match &variant.fields {
1344 Fields::Unit => {
1345 tokens.extend(quote! {
1346 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, ()> {
1347 static UNIT: () = ();
1348 key_paths_core::KeyPaths::readable_enum(
1349 |_| #name::#v_ident,
1350 |e: &#name| match e { #name::#v_ident => Some(&UNIT), _ => None }
1351 )
1352 }
1353 });
1354 }
1355 Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => {
1356 let inner_ty = &unnamed.unnamed.first().unwrap().ty;
1357 tokens.extend(quote! {
1358 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1359 key_paths_core::KeyPaths::readable_enum(
1360 #name::#v_ident,
1361 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None }
1362 )
1363 }
1364 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
1365 key_paths_core::KeyPaths::writable_enum(
1366 #name::#v_ident,
1367 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None },
1368 |e: &mut #name| match e { #name::#v_ident(v) => Some(v), _ => None },
1369 )
1370 }
1371 });
1372 }
1373 _ => {
1374 tokens.extend(quote! {
1375 compile_error!("Casepaths derive supports only unit and single-field tuple variants");
1376 });
1377 }
1378 }
1379 }
1380 tokens
1381 }
1382 _ => quote! { compile_error!("Casepaths can only be derived for enums"); },
1383 };
1384
1385 let expanded = quote! {
1386 impl #name {
1387 #tokens
1388 }
1389 };
1390
1391 TokenStream::from(expanded)
1392}