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}
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                    // For HashMap<K, V>, we want the value type (V), which is the second argument
484                    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                    // For other types like Vec, take the first argument
492                    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}