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}
13
14#[proc_macro_derive(Keypaths)]
15pub fn derive_keypaths(input: TokenStream) -> TokenStream {
16 let input = parse_macro_input!(input as DeriveInput);
17 let name = input.ident;
18
19 let methods = match input.data {
20 Data::Struct(data_struct) => match data_struct.fields {
21 Fields::Named(fields_named) => {
22 let mut tokens = proc_macro2::TokenStream::new();
23 for field in fields_named.named.iter() {
24 let field_ident = field.ident.as_ref().unwrap();
25 let ty = &field.ty;
26
27 let r_fn = format_ident!("{}_r", field_ident);
28 let w_fn = format_ident!("{}_w", field_ident);
29 let fr_fn = format_ident!("{}_fr", field_ident);
30 let fw_fn = format_ident!("{}_fw", field_ident);
31
32 let (kind, inner_ty) = extract_wrapper_inner_type(ty);
33
34 match (kind, inner_ty) {
35 (WrapperKind::Option, Some(inner_ty)) => {
36 tokens.extend(quote! {
38 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
39 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
40 }
41 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
42 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
43 }
44 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
45 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_ident.as_ref())
46 }
47 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
48 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#field_ident.as_mut())
49 }
50 });
51 }
52 (WrapperKind::Box, Some(inner_ty)) => {
53 tokens.extend(quote! {
55 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
56 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#field_ident)
57 }
58 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
59 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut *s.#field_ident)
60 }
61 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
62 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#field_ident))
63 }
64 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
65 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut *s.#field_ident))
66 }
67 });
68 }
69 (WrapperKind::Rc, Some(inner_ty)) | (WrapperKind::Arc, Some(inner_ty)) => {
70 tokens.extend(quote! {
72 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
73 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#field_ident)
74 }
75 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
76 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#field_ident))
77 }
78 });
79 }
80 (WrapperKind::None, None) => {
81 tokens.extend(quote! {
83 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
84 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
85 }
86 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
87 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
88 }
89 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #ty> {
90 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&s.#field_ident))
91 }
92 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #ty> {
93 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut s.#field_ident))
94 }
95 });
96 }
97 _ => {
99 tokens.extend(quote! {
100 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
101 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_ident)
102 }
103 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
104 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#field_ident)
105 }
106 });
107 }
108 }
109 }
110 tokens
111 }
112 Fields::Unnamed(unnamed) => {
113 let mut tokens = proc_macro2::TokenStream::new();
115 for (idx, field) in unnamed.unnamed.iter().enumerate() {
116 let idx_lit = syn::Index::from(idx);
117 let ty = &field.ty;
118
119 let r_fn = format_ident!("f{}_r", idx);
120 let w_fn = format_ident!("f{}_w", idx);
121 let fr_fn = format_ident!("f{}_fr", idx);
122 let fw_fn = format_ident!("f{}_fw", idx);
123
124 let (kind, inner_ty) = extract_wrapper_inner_type(ty);
125
126 match (kind, inner_ty) {
127 (WrapperKind::Option, Some(inner_ty)) => {
128 tokens.extend(quote! {
129 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
130 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
131 }
132 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
133 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
134 }
135 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
136 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#idx_lit.as_ref())
137 }
138 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
139 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| s.#idx_lit.as_mut())
140 }
141 });
142 }
143 (WrapperKind::Box, Some(inner_ty)) => {
144 tokens.extend(quote! {
145 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
146 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#idx_lit)
147 }
148 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
149 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut *s.#idx_lit)
150 }
151 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
152 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#idx_lit))
153 }
154 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
155 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut *s.#idx_lit))
156 }
157 });
158 }
159 (WrapperKind::Rc, Some(inner_ty)) | (WrapperKind::Arc, Some(inner_ty)) => {
160 tokens.extend(quote! {
161 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
162 key_paths_core::KeyPaths::readable(|s: &#name| &*s.#idx_lit)
163 }
164 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
165 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&*s.#idx_lit))
166 }
167 });
168 }
169 (WrapperKind::None, None) => {
170 tokens.extend(quote! {
171 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
172 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
173 }
174 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #ty> {
175 key_paths_core::KeyPaths::writable(|s: &mut #name| &mut s.#idx_lit)
176 }
177 pub fn #fr_fn() -> key_paths_core::KeyPaths<#name, #ty> {
178 key_paths_core::KeyPaths::failable_readable(|s: &#name| Some(&s.#idx_lit))
179 }
180 pub fn #fw_fn() -> key_paths_core::KeyPaths<#name, #ty> {
181 key_paths_core::KeyPaths::failable_writable(|s: &mut #name| Some(&mut s.#idx_lit))
182 }
183 });
184 }
185 _ => {
186 tokens.extend(quote! {
188 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #ty> {
189 key_paths_core::KeyPaths::readable(|s: &#name| &s.#idx_lit)
190 }
191 });
192 }
193 }
194 }
195 tokens
196 }
197 _ => quote! {
198 compile_error!("Keypaths derive supports only structs with named or unnamed fields");
199 },
200 },
201 Data::Enum(data_enum) => {
202 let mut tokens = proc_macro2::TokenStream::new();
204 for variant in data_enum.variants.iter() {
205 let v_ident = &variant.ident;
206 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
207 let r_fn = format_ident!("{}_case_r", snake);
208 let w_fn = format_ident!("{}_case_w", snake);
209
210 match &variant.fields {
211 Fields::Unit => {
212 tokens.extend(quote! {
213 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, ()> {
214 static UNIT: () = ();
215 key_paths_core::KeyPaths::readable_enum(
216 |_| #name::#v_ident,
217 |e: &#name| match e { #name::#v_ident => Some(&UNIT), _ => None }
218 )
219 }
220 });
221 }
222 Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => {
223 let field_ty = &unnamed.unnamed.first().unwrap().ty;
224 let (kind, inner_ty_opt) = extract_wrapper_inner_type(field_ty);
225
226 match (kind, inner_ty_opt) {
227 (WrapperKind::Option, Some(inner_ty)) => {
228 tokens.extend(quote! {
229 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
230 key_paths_core::KeyPaths::readable_enum(
231 #name::#v_ident,
232 |e: &#name| match e { #name::#v_ident(v) => v.as_ref(), _ => None }
233 )
234 }
235 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
236 key_paths_core::KeyPaths::writable_enum(
237 #name::#v_ident,
238 |e: &#name| match e { #name::#v_ident(v) => v.as_ref(), _ => None },
239 |e: &mut #name| match e { #name::#v_ident(v) => v.as_mut(), _ => None },
240 )
241 }
242 });
243 }
244 (WrapperKind::Box, Some(inner_ty)) => {
245 tokens.extend(quote! {
246 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
247 key_paths_core::KeyPaths::readable_enum(
248 #name::#v_ident,
249 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None }
250 )
251 }
252 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
253 key_paths_core::KeyPaths::writable_enum(
254 #name::#v_ident,
255 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None },
256 |e: &mut #name| match e { #name::#v_ident(v) => Some(&mut *v), _ => None },
257 )
258 }
259 });
260 }
261 (WrapperKind::Rc, Some(inner_ty)) | (WrapperKind::Arc, Some(inner_ty)) => {
262 tokens.extend(quote! {
263 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
264 key_paths_core::KeyPaths::readable_enum(
265 #name::#v_ident,
266 |e: &#name| match e { #name::#v_ident(v) => Some(&*v), _ => None }
267 )
268 }
269 });
270 }
271 (WrapperKind::None, None) => {
272 let inner_ty = field_ty;
274 tokens.extend(quote! {
275 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
276 key_paths_core::KeyPaths::readable_enum(
277 #name::#v_ident,
278 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None }
279 )
280 }
281 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
282 key_paths_core::KeyPaths::writable_enum(
283 #name::#v_ident,
284 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None },
285 |e: &mut #name| match e { #name::#v_ident(v) => Some(v), _ => None },
286 )
287 }
288 });
289 }
290 _ => {
291 tokens.extend(quote! {
293 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #field_ty> {
294 key_paths_core::KeyPaths::readable_enum(
295 #name::#v_ident,
296 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None }
297 )
298 }
299 });
300 }
301 }
302 }
303 _ => {
304 tokens.extend(quote! {
305 compile_error!("Casepaths derive supports only unit and single-field tuple variants");
306 });
307 }
308 }
309 }
310 tokens
311 }
312 _ => quote! {
313 compile_error!("Keypaths derive supports only structs and enums");
314 },
315 };
316
317 let expanded = quote! {
318 impl #name {
319 #methods
320 }
321 };
322
323 TokenStream::from(expanded)
324}
325
326fn extract_wrapper_inner_type(ty: &Type) -> (WrapperKind, Option<Type>) {
327 use syn::{GenericArgument, PathArguments};
328 if let Type::Path(tp) = ty {
329 if let Some(seg) = tp.path.segments.last() {
330 let ident_str = seg.ident.to_string();
331 if let PathArguments::AngleBracketed(ab) = &seg.arguments {
332 for arg in ab.args.iter() {
333 if let GenericArgument::Type(inner) = arg {
334 return match ident_str.as_str() {
335 "Option" => (WrapperKind::Option, Some(inner.clone())),
336 "Box" => (WrapperKind::Box, Some(inner.clone())),
337 "Rc" => (WrapperKind::Rc, Some(inner.clone())),
338 "Arc" => (WrapperKind::Arc, Some(inner.clone())),
339 _ => (WrapperKind::None, None),
340 };
341 }
342 }
343 }
344 }
345 }
346 (WrapperKind::None, None)
347}
348
349fn to_snake_case(name: &str) -> String {
350 let mut out = String::new();
351 for (i, c) in name.chars().enumerate() {
352 if c.is_uppercase() {
353 if i != 0 {
354 out.push('_');
355 }
356 out.push(c.to_ascii_lowercase());
357 } else {
358 out.push(c);
359 }
360 }
361 out
362}
363
364#[proc_macro_derive(Casepaths)]
396pub fn derive_casepaths(input: TokenStream) -> TokenStream {
397 let input = parse_macro_input!(input as DeriveInput);
398 let name = input.ident;
399
400 let tokens = match input.data {
401 Data::Enum(data_enum) => {
402 let mut tokens = proc_macro2::TokenStream::new();
403 for variant in data_enum.variants.iter() {
404 let v_ident = &variant.ident;
405 let snake = format_ident!("{}", to_snake_case(&v_ident.to_string()));
406 let r_fn = format_ident!("{}_case_r", snake);
407 let w_fn = format_ident!("{}_case_w", snake);
408
409 match &variant.fields {
410 Fields::Unit => {
411 tokens.extend(quote! {
412 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, ()> {
413 static UNIT: () = ();
414 key_paths_core::KeyPaths::readable_enum(
415 |_| #name::#v_ident,
416 |e: &#name| match e { #name::#v_ident => Some(&UNIT), _ => None }
417 )
418 }
419 });
420 }
421 Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => {
422 let inner_ty = &unnamed.unnamed.first().unwrap().ty;
423 tokens.extend(quote! {
424 pub fn #r_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
425 key_paths_core::KeyPaths::readable_enum(
426 #name::#v_ident,
427 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None }
428 )
429 }
430 pub fn #w_fn() -> key_paths_core::KeyPaths<#name, #inner_ty> {
431 key_paths_core::KeyPaths::writable_enum(
432 #name::#v_ident,
433 |e: &#name| match e { #name::#v_ident(v) => Some(v), _ => None },
434 |e: &mut #name| match e { #name::#v_ident(v) => Some(v), _ => None },
435 )
436 }
437 });
438 }
439 _ => {
440 tokens.extend(quote! {
441 compile_error!("Casepaths derive supports only unit and single-field tuple variants");
442 });
443 }
444 }
445 }
446 tokens
447 }
448 _ => quote! { compile_error!("Casepaths can only be derived for enums"); },
449 };
450
451 let expanded = quote! {
452 impl #name {
453 #tokens
454 }
455 };
456
457 TokenStream::from(expanded)
458}