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}
15
16#[proc_macro_derive(Keypaths)]
17pub fn derive_keypaths(input: TokenStream) -> TokenStream {
18 let input = parse_macro_input!(input as DeriveInput);
19 let name = input.ident;
20
21 let methods = match input.data {
22 Data::Struct(data_struct) => match data_struct.fields {
23 Fields::Named(fields_named) => {
24 let mut tokens = proc_macro2::TokenStream::new();
25 for field in fields_named.named.iter() {
26 let field_ident = field.ident.as_ref().unwrap();
27 let ty = &field.ty;
28
29 let r_fn = format_ident!("{}_r", field_ident);
30 let w_fn = format_ident!("{}_w", field_ident);
31 let fr_fn = format_ident!("{}_fr", field_ident);
32 let fw_fn = format_ident!("{}_fw", field_ident);
33 let fr_at_fn = format_ident!("{}_fr_at", field_ident);
34 let fw_at_fn = format_ident!("{}_fw_at", field_ident);
35
36 let (kind, inner_ty) = extract_wrapper_inner_type(ty);
37
38 match (kind, inner_ty) {
39 (WrapperKind::Option, Some(inner_ty)) => {
40 tokens.extend(quote! {
41 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
42 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
43 }
44 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
45 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
46 }
47 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
48 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.as_ref())
49 }
50 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
51 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#field_ident.as_mut())
52 }
53 });
54 }
55 (WrapperKind::Vec, Some(inner_ty)) => {
56 tokens.extend(quote! {
57 pub fn #fr_at_fn(index: usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
58 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.get(index))
59 }
60 pub fn #fw_at_fn(index: usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
61 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.get_mut(index))
62 }
63 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
64 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
65 }
66 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
67 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
68 }
69 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
70 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.first())
71 }
72 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
73 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#field_ident.first_mut())
74 }
75 });
76 }
77 (WrapperKind::HashMap, Some(inner_ty)) => {
78 tokens.extend(quote! {
79 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
80 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
81 }
82 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
83 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
84 }
85 pub fn #fr_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
86 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.get(&key))
87 }
88 pub fn #fw_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
89 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.get_mut(&key))
90 }
91 pub fn #fr_at_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
92 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#field_ident.get(&key))
93 }
94 pub fn #fw_at_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
95 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#field_ident.get_mut(&key))
96 }
97 });
98 }
99 (WrapperKind::Box, Some(inner_ty)) => {
100 tokens.extend(quote! {
101 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
102 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#field_ident)
103 }
104 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
105 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut *s.#field_ident)
106 }
107 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
108 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#field_ident))
109 }
110 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
111 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut *s.#field_ident))
112 }
113 });
114 }
115 (WrapperKind::Rc, Some(inner_ty)) | (WrapperKind::Arc, Some(inner_ty)) => {
116 tokens.extend(quote! {
117 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
118 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#field_ident)
119 }
120 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
121 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#field_ident))
122 }
123 });
124 }
125 (WrapperKind::None, None) => {
126 tokens.extend(quote! {
127 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
128 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
129 }
130 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
131 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
132 }
133 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #ty> {
134 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&s.#field_ident))
135 }
136 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #ty> {
137 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut s.#field_ident))
138 }
139 });
140 }
141 _ => {
142 tokens.extend(quote! {
143 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
144 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
145 }
146 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
147 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
148 }
149 });
150 }
151 }
152 }
153 tokens
154 }
155 Fields::Unnamed(unnamed) => {
156 let mut tokens = proc_macro2::TokenStream::new();
157 for (idx, field) in unnamed.unnamed.iter().enumerate() {
158 let idx_lit = syn::Index::from(idx);
159 let ty = &field.ty;
160
161 let r_fn = format_ident!("f{}_r", idx);
162 let w_fn = format_ident!("f{}_w", idx);
163 let fr_fn = format_ident!("f{}_fr", idx);
164 let fw_fn = format_ident!("f{}_fw", idx);
165 let fr_at_fn = format_ident!("f{}_fr_at", idx);
166 let fw_at_fn = format_ident!("f{}_fw_at", idx);
167
168 let (kind, inner_ty) = extract_wrapper_inner_type(ty);
169
170 match (kind, inner_ty) {
171 (WrapperKind::Option, Some(inner_ty)) => {
172 tokens.extend(quote! {
173 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
174 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
175 }
176 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
177 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
178 }
179 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
180 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.as_ref())
181 }
182 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
183 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.as_mut())
184 }
185 });
186 }
187 (WrapperKind::Vec, Some(inner_ty)) => {
188 tokens.extend(quote! {
189 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
190 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
191 }
192 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
193 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
194 }
195 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
196 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.first())
197 }
198 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
199 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.first_mut())
200 }
201 pub fn #fr_at_fn(index: &'static usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
202 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.get(*index))
203 }
204 pub fn #fw_at_fn(index: &'static usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
205 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.get_mut(*index))
206 }
207 });
208 }
209 (WrapperKind::HashMap, Some(inner_ty)) => {
210 tokens.extend(quote! {
211 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
212 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
213 }
214 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
215 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
216 }
217 pub fn #fr_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
218 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#idx_lit.get(&key))
219 }
220 pub fn #fw_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
221 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#idx_lit.get_mut(&key))
222 }
223 pub fn #fr_at_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
224 key_paths_core::KeyPaths::failable_readable(move |s: &#name| s.#idx_lit.get(&key))
225 }
226 pub fn #fw_at_fn(key: String) -> key_paths_core::KeyPaths<#name, #inner_ty> {
227 key_paths_core::KeyPaths::failable_writable(move |s: &mut #name| s.#idx_lit.get_mut(&key))
228 }
229 });
230 }
231 (WrapperKind::Box, Some(inner_ty)) => {
232 tokens.extend(quote! {
233 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
234 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#idx_lit)
235 }
236 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
237 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut *s.#idx_lit)
238 }
239 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
240 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#idx_lit))
241 }
242 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
243 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut *s.#idx_lit))
244 }
245 });
246 }
247 (WrapperKind::Rc, Some(inner_ty)) | (WrapperKind::Arc, Some(inner_ty)) => {
248 tokens.extend(quote! {
249 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
250 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#idx_lit)
251 }
252 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
253 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#idx_lit))
254 }
255 });
256 }
257 (WrapperKind::None, None) => {
258 tokens.extend(quote! {
259 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
260 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
261 }
262 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
263 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
264 }
265 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #ty> {
266 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&s.#idx_lit))
267 }
268 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #ty> {
269 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut s.#idx_lit))
270 }
271 });
272 }
273 _ => {
274 tokens.extend(quote! {
275 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
276 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
277 }
278 });
279 }
280 }
281 }
282 tokens
283 }
284 _ => quote! {
285 compile_error!("Keypaths derive supports only structs with named or unnamed fields");
286 },
287 },
288 Data::Enum(data_enum) => {
289 let mut tokens = proc_macro2::TokenStream::new();
290 for variant in data_enum.variants.iter() {
291 let v_ident = &variant.ident;
292 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
293 let r_fn = format_ident!("{}_case_r", snake);
294 let w_fn = format_ident!("{}_case_w", snake);
295 let fr_at_fn = format_ident!("{}_case_fr_at", snake);
296 let fw_at_fn = format_ident!("{}_case_fw_at", snake);
297
298 match &variant.fields {
299 Fields::Unit => {
300 tokens.extend(quote! {
301 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, ()> {
302 static UNIT: () = ();
303 key_paths_core::KeyPaths::readable_enum(
304 |_| #name::#v_ident,
305 |e: &#name| match e { #name::#v_ident => Some(&UNIT), _ => None }
306 )
307 }
308 });
309 }
310 Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => {
311 let field_ty = &unnamed.unnamed.first().unwrap().ty;
312 let (kind, inner_ty_opt) = extract_wrapper_inner_type(field_ty);
313
314 match (kind, inner_ty_opt) {
315 (WrapperKind::Option, Some(inner_ty)) => {
316 tokens.extend(quote! {
317 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
318 key_paths_core::KeyPaths::readable_enum(
319 #name::#v_ident,
320 |e: &#name| match e { #name::#v_ident(v) => v.as_ref(), _ => None }
321 )
322 }
323 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
324 key_paths_core::KeyPaths::writable_enum(
325 #name::#v_ident,
326 |e: &#name| match e { #name::#v_ident(v) => v.as_ref(), _ => None },
327 |e: &mut #name| match e { #name::#v_ident(v) => v.as_mut(), _ => None },
328 )
329 }
330 });
331 }
332 (WrapperKind::Vec, Some(inner_ty)) => {
333 tokens.extend(quote! {
334 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
335 key_paths_core::KeyPaths::readable_enum(
336 #name::#v_ident,
337 |e: &#name| match e { #name::#v_ident(v) => v.first(), _ => None }
338 )
339 }
340 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
341 key_paths_core::KeyPaths::writable_enum(
342 #name::#v_ident,
343 |e: &#name| match e { #name::#v_ident(v) => v.first(), _ => None },
344 |e: &mut #name| match e { #name::#v_ident(v) => v.first_mut(), _ => None },
345 )
346 }
347 pub fn #fr_at_fn(index: &'static usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
348 key_paths_core::KeyPaths::readable_enum(
349 #name::#v_ident,
350 |e: &#name| match e { #name::#v_ident(v) => v.get(*index), _ => None }
351 )
352 }
353 pub fn #fw_at_fn(index: &'static usize) -> key_paths_core::KeyPaths<#name, #inner_ty> {
354 key_paths_core::KeyPaths::writable_enum(
355 #name::#v_ident,
356 |e: &#name| match e { #name::#v_ident(v) => v.get(*index), _ => None },
357 |e: &mut #name| match e { #name::#v_ident(v) => v.get_mut(*index), _ => None },
358 )
359 }
360 });
361 }
362 (WrapperKind::HashMap, Some(inner_ty)) => {
363 tokens.extend(quote! {
364 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
365 key_paths_core::KeyPaths::readable_enum(
366 #name::#v_ident,
367 |e: &#name| match e { #name::#v_ident(v) => v.first().map(|(_, v)| v), _ => None }
368 )
369 }
370 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
371 key_paths_core::KeyPaths::writable_enum(
372 #name::#v_ident,
373 |e: &#name| match e { #name::#v_ident(v) => v.first().map(|(_, v)| v), _ => None },
374 |e: &mut #name| match e { #name::#v_ident(v) => v.first_mut().map(|(_, v)| v), _ => None },
375 )
376 }
377 pub fn #fr_at_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: &'static K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
378 key_paths_core::KeyPaths::readable_enum(
379 #name::#v_ident,
380 |e: &#name| match e { #name::#v_ident(v) => v.get(key), _ => None }
381 )
382 }
383 pub fn #fw_at_fn<K: ::std::hash::Hash + ::std::cmp::Eq + 'static>(key: &'static K) -> key_paths_core::KeyPaths<#name, #inner_ty> {
384 key_paths_core::KeyPaths::writable_enum(
385 #name::#v_ident,
386 |e: &#name| match e { #name::#v_ident(v) => v.get(key), _ => None },
387 |e: &mut #name| match e { #name::#v_ident(v) => v.get_mut(key), _ => None },
388 )
389 }
390 });
391 }
392 (WrapperKind::Box, Some(inner_ty)) => {
393 tokens.extend(quote! {
394 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
395 key_paths_core::KeyPaths::readable_enum(
396 #name::#v_ident,
397 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None }
398 )
399 }
400 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
401 key_paths_core::KeyPaths::writable_enum(
402 #name::#v_ident,
403 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None },
404 |e: &mut #name| match e { #name::#v_ident(v) => Some(&mut *v), _ => None },
405 )
406 }
407 });
408 }
409 (WrapperKind::Rc, Some(inner_ty))
410 | (WrapperKind::Arc, Some(inner_ty)) => {
411 tokens.extend(quote! {
412 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
413 key_paths_core::KeyPaths::readable_enum(
414 #name::#v_ident,
415 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None }
416 )
417 }
418 });
419 }
420 (WrapperKind::None, None) => {
421 let inner_ty = field_ty;
422 tokens.extend(quote! {
423 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
424 key_paths_core::KeyPaths::readable_enum(
425 #name::#v_ident,
426 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None }
427 )
428 }
429 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
430 key_paths_core::KeyPaths::writable_enum(
431 #name::#v_ident,
432 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None },
433 |e: &mut #name| match e { #name::#v_ident(v) => Some(v), _ => None },
434 )
435 }
436 });
437 }
438 _ => {
439 tokens.extend(quote! {
440 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #field_ty> {
441 key_paths_core::KeyPaths::readable_enum(
442 #name::#v_ident,
443 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None }
444 )
445 }
446 });
447 }
448 }
449 }
450 _ => {
451 tokens.extend(quote! {
452 compile_error!("Casepaths derive supports only unit and single-field tuple variants");
453 });
454 }
455 }
456 }
457 tokens
458 }
459 _ => quote! {
460 compile_error!("Keypaths derive supports only structs and enums");
461 },
462 };
463
464 let expanded = quote! {
465 impl #name {
466 #methods
467 }
468 };
469
470 TokenStream::from(expanded)
471}
472
473fn extract_wrapper_inner_type(ty: &Type) -> (WrapperKind, Option<Type>) {
474 use syn::{GenericArgument, PathArguments};
475 if let Type::Path(tp) = ty {
476 let is_vec = tp.path.segments.iter().any(|seg| seg.ident == "Vec" || seg.ident == "vec");
477 let is_hashmap = tp.path.segments.iter().any(|seg| seg.ident == "HashMap" || seg.ident == "hash_map");
478 if let Some(seg) = tp.path.segments.last() {
479 let ident_str = seg.ident.to_string();
480 if let PathArguments::AngleBracketed(ab) = &seg.arguments {
481 let mut args = ab.args.iter();
482 if is_hashmap {
483 if let (Some(_key_arg), Some(value_arg)) = (args.next(), args.next()) {
485 if let GenericArgument::Type(inner) = value_arg {
486 eprintln!("Detected HashMap type: {}, extracting value type", ident_str);
487 return (WrapperKind::HashMap, Some(inner.clone()));
488 }
489 }
490 } else {
491 if let Some(arg) = args.next() {
493 if let GenericArgument::Type(inner) = arg {
494 eprintln!("Detected type: {}, is_vec: {}", ident_str, is_vec);
495 return match ident_str.as_str() {
496 "Option" => (WrapperKind::Option, Some(inner.clone())),
497 "Box" => (WrapperKind::Box, Some(inner.clone())),
498 "Rc" => (WrapperKind::Rc, Some(inner.clone())),
499 "Arc" => (WrapperKind::Arc, Some(inner.clone())),
500 _ if is_vec => (WrapperKind::Vec, Some(inner.clone())),
501 _ => (WrapperKind::None, None),
502 };
503 }
504 }
505 }
506 }
507 }
508 }
509 (WrapperKind::None, None)
510}
511
512
513fn to_snake_case(name: &str) -> String {
514 let mut out = String::new();
515 for (i, c) in name.chars().enumerate() {
516 if c.is_uppercase() {
517 if i != 0 {
518 out.push('_');
519 }
520 out.push(c.to_ascii_lowercase());
521 } else {
522 out.push(c);
523 }
524 }
525 out
526}
527
528#[proc_macro_derive(Casepaths)]
529pub fn derive_casepaths(input: TokenStream) -> TokenStream {
530 let input = parse_macro_input!(input as DeriveInput);
531 let name = input.ident;
532
533 let tokens = match input.data {
534 Data::Enum(data_enum) => {
535 let mut tokens = proc_macro2::TokenStream::new();
536 for variant in data_enum.variants.iter() {
537 let v_ident = &variant.ident;
538 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
539 let r_fn = format_ident!("{}_case_r", snake);
540 let w_fn = format_ident!("{}_case_w", snake);
541
542 match &variant.fields {
543 Fields::Unit => {
544 tokens.extend(quote! {
545 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, ()> {
546 static UNIT: () = ();
547 key_paths_core::KeyPaths::readable_enum(
548 |_| #name::#v_ident,
549 |e: &#name| match e { #name::#v_ident => Some(&UNIT), _ => None }
550 )
551 }
552 });
553 }
554 Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => {
555 let inner_ty = &unnamed.unnamed.first().unwrap().ty;
556 tokens.extend(quote! {
557 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
558 key_paths_core::KeyPaths::readable_enum(
559 #name::#v_ident,
560 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None }
561 )
562 }
563 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
564 key_paths_core::KeyPaths::writable_enum(
565 #name::#v_ident,
566 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None },
567 |e: &mut #name| match e { #name::#v_ident(v) => Some(v), _ => None },
568 )
569 }
570 });
571 }
572 _ => {
573 tokens.extend(quote! {
574 compile_error!("Casepaths derive supports only unit and single-field tuple variants");
575 });
576 }
577 }
578 }
579 tokens
580 }
581 _ => quote! { compile_error!("Casepaths can only be derived for enums"); },
582 };
583
584 let expanded = quote! {
585 impl #name {
586 #tokens
587 }
588 };
589
590 TokenStream::from(expanded)
591}