Skip to main content

pathmap_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput, Data, Fields};
4
5// See the docs for `PolyZipper` in `pathmap::zipper::PolyZipper`
6#[proc_macro_derive(PolyZipper)]
7pub fn derive_poly_zipper(input: TokenStream) -> TokenStream {
8    let input = parse_macro_input!(input as DeriveInput);
9
10    let enum_name = &input.ident;
11    let generics = &input.generics;
12    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
13
14    // Extract enum variants
15    let variants = match &input.data {
16        Data::Enum(data_enum) => &data_enum.variants,
17        _ => panic!("PolyZipper can only be derived for enums"),
18    };
19
20    // Generate From and TryFrom impls for each variant
21    let from_impls = variants.iter().map(|variant| {
22        let variant_name = &variant.ident;
23
24        // Get the inner type (assuming single unnamed field)
25        let inner_type = match &variant.fields {
26            Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
27                &fields.unnamed[0].ty
28            }
29            _ => panic!("Each variant must have exactly one unnamed field"),
30        };
31
32        quote! {
33            impl #impl_generics From<#inner_type> for #enum_name #ty_generics #where_clause {
34                fn from(value: #inner_type) -> Self {
35                    #enum_name::#variant_name(value)
36                }
37            }
38
39            impl #impl_generics core::convert::TryFrom<#enum_name #ty_generics> for #inner_type #where_clause {
40                type Error = ();
41
42                fn try_from(value: #enum_name #ty_generics) -> Result<Self, Self::Error> {
43                    match value {
44                        #enum_name::#variant_name(inner) => Ok(inner),
45                        _ => Err(()),
46                    }
47                }
48            }
49        }
50    });
51
52    let variant_arms: Vec<_> = variants.iter().map(|variant| {
53        let variant_name = &variant.ident;
54        quote! { Self::#variant_name(inner) }
55    }).collect();
56
57    let inner_types: Vec<_> = variants.iter().map(|variant| {
58        match &variant.fields {
59            Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
60                &fields.unnamed[0].ty
61            }
62            _ => panic!("Each variant must have exactly one unnamed field"),
63        }
64    }).collect();
65
66    // Generate Zipper trait implementation
67    let zipper_impl = {
68        let variant_arms = &variant_arms;
69        quote! {
70            impl #impl_generics pathmap::zipper::Zipper for #enum_name #ty_generics #where_clause {
71                fn path_exists(&self) -> bool {
72                    match self {
73                        #(#variant_arms => inner.path_exists(),)*
74                    }
75                }
76
77                fn is_val(&self) -> bool {
78                    match self {
79                        #(#variant_arms => inner.is_val(),)*
80                    }
81                }
82
83                fn child_count(&self) -> usize {
84                    match self {
85                        #(#variant_arms => inner.child_count(),)*
86                    }
87                }
88
89                fn child_mask(&self) -> pathmap::utils::ByteMask {
90                    match self {
91                        #(#variant_arms => inner.child_mask(),)*
92                    }
93                }
94            }
95        }
96    };
97
98    // Generate ZipperValues trait implementation
99    let zipper_values_impl = {
100        let variant_arms = &variant_arms;
101        quote! {
102            impl #impl_generics pathmap::zipper::ZipperValues<V> for #enum_name #ty_generics
103            where
104                #(#inner_types: pathmap::zipper::ZipperValues<V>,)*
105                #where_clause
106            {
107                fn val(&self) -> Option<&V> {
108                    match self {
109                        #(#variant_arms => inner.val(),)*
110                    }
111                }
112            }
113        }
114    };
115
116    // Generate ZipperReadOnlyValues trait implementation
117    let zipper_read_only_values_impl = {
118        let variant_arms = &variant_arms;
119        quote! {
120            impl #impl_generics pathmap::zipper::ZipperReadOnlyValues<'trie, V> for #enum_name #ty_generics
121            where
122                #(#inner_types: pathmap::zipper::ZipperReadOnlyValues<'trie, V>,)*
123                #where_clause
124            {
125                fn get_val(&self) -> Option<&'trie V> {
126                    match self {
127                        #(#variant_arms => inner.get_val(),)*
128                    }
129                }
130            }
131        }
132    };
133
134    // Generate witness enum name and variant names for conditional traits
135    let witness_enum_name = syn::Ident::new(&format!("{}Witness", enum_name), enum_name.span());
136    let variant_names: Vec<_> = variants.iter().map(|variant| &variant.ident).collect();
137
138    // Generate ZipperReadOnlyConditionalValues trait implementation with witness enum
139    let zipper_read_only_conditional_values_impl = {
140        quote! {
141            //GOAT TODO: I think we could get into trouble if the witness types have fewer generics than
142            // the outer PolyZipper enum.  So we probably should add a phantom case too.
143            pub enum #witness_enum_name #impl_generics
144            where
145                #(#inner_types: pathmap::zipper::ZipperReadOnlyConditionalValues<'trie, V>,)*
146                #where_clause
147            {
148                #(#variant_names(<#inner_types as pathmap::zipper::ZipperReadOnlyConditionalValues<'trie, V>>::WitnessT),)*
149            }
150
151            impl #impl_generics pathmap::zipper::ZipperReadOnlyConditionalValues<'trie, V> for #enum_name #ty_generics
152            where
153                #(#inner_types: pathmap::zipper::ZipperReadOnlyConditionalValues<'trie, V>,)*
154                #where_clause
155            {
156                type WitnessT = #witness_enum_name #ty_generics;
157
158                fn witness<'w>(&self) -> Self::WitnessT {
159                    match self {
160                        #(Self::#variant_names(inner) => #witness_enum_name::#variant_names(inner.witness()),)*
161                    }
162                }
163
164                fn get_val_with_witness<'w>(&self, witness: &'w Self::WitnessT) -> Option<&'w V> where 'trie: 'w {
165                    match (self, witness) {
166                        #((Self::#variant_names(inner), #witness_enum_name::#variant_names(w)) => inner.get_val_with_witness(w),)*
167                        _ => {
168                            debug_assert!(false, "Witness variant must match zipper + variant");
169                            None
170                        },
171                    }
172                }
173            }
174        }
175    };
176
177    //GOAT, this is probably dead code given the decision (described in the PolyZipper docs)
178    // not to support a `ZipperForking` impl
179    //
180    //I can't seem to figure out how to align the `'a` and the `'read_z` lifetimes without changing the trait definition,
181    // and ideally we'd want to return a new enum-based zipper.  The elegant way to do that would be to actually invoke
182    // the PolyZipper macro recursively to get maximum support on the new zipper, but there are a bunch of obnoxious details
183    // to make that work. - like what to do about the recursion so we don't have infinite recursion in the macro, and how
184    // to map the lifetimes required by the trait onto the lifetimes of the new zippers.
185    //
186    // // Generate ZipperForking trait implementation
187    // let zipper_forking_impl = {
188    //     let variant_arms = &variant_arms;
189    //     let first_inner_type = &inner_types[0];
190    //     let other_inner_types = &inner_types[1..];
191
192    //     // Create modified generics with additional lifetime
193    //     let mut forking_generics = generics.clone();
194    //     forking_generics.params.insert(0, syn::parse_quote!('read_z));
195    //     let (forking_impl_generics, _, _) = forking_generics.split_for_impl();
196
197    //     quote! {
198    //         impl #forking_impl_generics pathmap::zipper::ZipperForking<V> for #enum_name #ty_generics
199    //         where
200    //             #(#inner_types: pathmap::zipper::ZipperForking<V>,)*
201    //             #(#other_inner_types: pathmap::zipper::ZipperForking<V, ReadZipperT<'read_z> = <#first_inner_type as pathmap::zipper::ZipperForking<V>>::ReadZipperT<'read_z>>,)*
202    //             Self: 'read_z,
203    //             #where_clause
204    //         {
205    //             type ReadZipperT<'a> = <#first_inner_type as pathmap::zipper::ZipperForking<V>>::ReadZipperT<'a> where Self: 'a;
206
207    //             fn fork_read_zipper<'a>(&'a self) -> Self::ReadZipperT<'a> {
208    //                 match self {
209    //                     #(#variant_arms => inner.fork_read_zipper(),)*
210    //                 }
211    //             }
212    //         }
213    //     }
214    // };
215
216    // Generate ZipperMoving trait implementation
217    let zipper_moving_impl = {
218        let variant_arms = &variant_arms;
219        quote! {
220            impl #impl_generics pathmap::zipper::ZipperMoving for #enum_name #ty_generics
221            where
222                #(#inner_types: pathmap::zipper::ZipperMoving,)*
223                #where_clause
224            {
225                fn path(&self) -> &[u8] {
226                    match self {
227                        #(#variant_arms => inner.path(),)*
228                    }
229                }
230
231                fn val_count(&self) -> usize {
232                    match self {
233                        #(#variant_arms => inner.val_count(),)*
234                    }
235                }
236
237                fn descend_to<K: AsRef<[u8]>>(&mut self, k: K) {
238                    match self {
239                        #(#variant_arms => inner.descend_to(k),)*
240                    }
241                }
242
243                fn descend_to_check<K: AsRef<[u8]>>(&mut self, k: K) -> bool {
244                    match self {
245                        #(#variant_arms => inner.descend_to_check(k),)*
246                    }
247                }
248
249                #[inline]
250                fn descend_to_byte(&mut self, k: u8) {
251                    match self {
252                        #(#variant_arms => inner.descend_to_byte(k),)*
253                    }
254                }
255
256                #[inline]
257                fn descend_to_existing_byte(&mut self, k: u8) -> bool {
258                    match self {
259                        #(#variant_arms => inner.descend_to_existing_byte(k),)*
260                    }
261                }
262
263                fn ascend(&mut self, steps: usize) -> bool {
264                    match self {
265                        #(#variant_arms => inner.ascend(steps),)*
266                    }
267                }
268
269                fn ascend_until(&mut self) -> bool {
270                    match self {
271                        #(#variant_arms => inner.ascend_until(),)*
272                    }
273                }
274
275                fn ascend_until_branch(&mut self) -> bool {
276                    match self {
277                        #(#variant_arms => inner.ascend_until_branch(),)*
278                    }
279                }
280            }
281        }
282    };
283
284    // Generate ZipperConcrete trait implementation
285    let zipper_concrete_impl = {
286        let variant_arms = &variant_arms;
287        quote! {
288            impl #impl_generics pathmap::zipper::ZipperConcrete for #enum_name #ty_generics
289            where
290                #(#inner_types: pathmap::zipper::ZipperConcrete,)*
291                #where_clause
292            {
293                fn shared_node_id(&self) -> Option<u64> {
294                    match self {
295                        #(#variant_arms => inner.shared_node_id(),)*
296                    }
297                }
298                fn is_shared(&self) -> bool {
299                    match self {
300                        #(#variant_arms => inner.is_shared(),)*
301                    }
302                }
303            }
304        }
305    };
306
307    // Generate ZipperAbsolutePath trait implementation
308    let zipper_absolute_path_impl = {
309        let variant_arms = &variant_arms;
310        quote! {
311            impl #impl_generics pathmap::zipper::ZipperAbsolutePath for #enum_name #ty_generics
312            where
313                #(#inner_types: pathmap::zipper::ZipperAbsolutePath,)*
314                #where_clause
315            {
316                fn origin_path(&self) -> &[u8] {
317                    match self {
318                        #(#variant_arms => inner.origin_path(),)*
319                    }
320                }
321
322                fn root_prefix_path(&self) -> &[u8] {
323                    match self {
324                        #(#variant_arms => inner.root_prefix_path(),)*
325                    }
326                }
327            }
328        }
329    };
330
331    // Generate ZipperPathBuffer trait implementation
332    let zipper_path_buffer_impl = {
333        let variant_arms = &variant_arms;
334        quote! {
335            impl #impl_generics pathmap::zipper::ZipperPathBuffer for #enum_name #ty_generics
336            where
337                #(#inner_types: pathmap::zipper::ZipperPathBuffer,)*
338                #where_clause
339            {
340                unsafe fn origin_path_assert_len(&self, len: usize) -> &[u8] {
341                    match self {
342                        #(#variant_arms => unsafe { inner.origin_path_assert_len(len) },)*
343                    }
344                }
345
346                fn prepare_buffers(&mut self) {
347                    match self {
348                        #(#variant_arms => inner.prepare_buffers(),)*
349                    }
350                }
351
352                fn reserve_buffers(&mut self, path_len: usize, stack_depth: usize) {
353                    match self {
354                        #(#variant_arms => inner.reserve_buffers(path_len, stack_depth),)*
355                    }
356                }
357            }
358        }
359    };
360
361    // Generate ZipperIteration trait implementation
362    let zipper_iteration_impl = {
363        let variant_arms = &variant_arms;
364        quote! {
365            impl #impl_generics pathmap::zipper::ZipperIteration for #enum_name #ty_generics
366            where
367                #(#inner_types: pathmap::zipper::ZipperIteration,)*
368                #where_clause
369            {
370                fn to_next_val(&mut self) -> bool {
371                    match self {
372                        #(#variant_arms => inner.to_next_val(),)*
373                    }
374                }
375
376                fn descend_last_path(&mut self) -> bool {
377                    match self {
378                        #(#variant_arms => inner.descend_last_path(),)*
379                    }
380                }
381
382                fn descend_first_k_path(&mut self, k: usize) -> bool {
383                    match self {
384                        #(#variant_arms => inner.descend_first_k_path(k),)*
385                    }
386                }
387
388                fn to_next_k_path(&mut self, k: usize) -> bool {
389                    match self {
390                        #(#variant_arms => inner.to_next_k_path(k),)*
391                    }
392                }
393            }
394        }
395    };
396
397    // Generate ZipperReadOnlyIteration trait implementation
398    let zipper_read_only_iteration_impl = {
399        let variant_arms = &variant_arms;
400        quote! {
401            impl #impl_generics pathmap::zipper::ZipperReadOnlyIteration<'trie, V> for #enum_name #ty_generics
402            where
403                #(#inner_types: pathmap::zipper::ZipperReadOnlyIteration<'trie, V>,)*
404                #where_clause
405            {
406                fn to_next_get_val(&mut self) -> Option<&'trie V> {
407                    match self {
408                        #(#variant_arms => inner.to_next_get_val(),)*
409                    }
410                }
411
412                #[deprecated]
413                fn to_next_get_value(&mut self) -> Option<&'trie V> {
414                    match self {
415                        #(#variant_arms => inner.to_next_get_value(),)*
416                    }
417                }
418            }
419        }
420    };
421
422    // Generate ZipperReadOnlyConditionalIteration trait implementation
423    let zipper_read_only_conditional_iteration_impl = {
424        quote! {
425            impl #impl_generics pathmap::zipper::ZipperReadOnlyConditionalIteration<'trie, V> for #enum_name #ty_generics
426            where
427                #(#inner_types: pathmap::zipper::ZipperReadOnlyConditionalIteration<'trie, V>,)*
428                #where_clause
429            {
430                fn to_next_get_val_with_witness<'w>(&mut self, witness: &'w Self::WitnessT) -> Option<&'w V> where 'trie: 'w {
431                    match (self, witness) {
432                        #((Self::#variant_names(inner), #witness_enum_name::#variant_names(w)) => inner.to_next_get_val_with_witness(w),)*
433                        _ => {
434                            debug_assert!(false, "Witness variant must match zipper variant");
435                            None
436                        },
437                    }
438                }
439            }
440        }
441    };
442
443    let expanded = quote! {
444        #(#from_impls)*
445        #zipper_impl
446        #zipper_values_impl
447        #zipper_read_only_values_impl
448        #zipper_read_only_conditional_values_impl
449        // #zipper_forking_impl
450        #zipper_moving_impl
451        #zipper_concrete_impl
452        #zipper_absolute_path_impl
453        #zipper_path_buffer_impl
454        #zipper_iteration_impl
455        #zipper_read_only_iteration_impl
456        #zipper_read_only_conditional_iteration_impl
457    };
458
459    TokenStream::from(expanded)
460}