rose_ztd_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, Data, DeriveInput, Fields};
4
5/// Derive macro for implementing the Hashable trait.
6///
7/// This macro automatically implements Hashable for structs by creating
8/// nested tuples of field references and calling .hash() on them.
9///
10/// # Example
11///
12/// ```ignore
13/// #[derive(Hashable)]
14/// struct MyStruct {
15///     x: u64,
16///     y: u64,
17///     z: u64,
18/// }
19/// ```
20///
21/// Expands to:
22///
23/// ```ignore
24/// impl Hashable for MyStruct {
25///     fn hash(&self) -> Digest {
26///         (&self.x, &(&self.y, &self.z)).hash()
27///     }
28/// }
29/// ```
30#[proc_macro_derive(Hashable)]
31pub fn derive_hashable(input: TokenStream) -> TokenStream {
32    let input = parse_macro_input!(input as DeriveInput);
33    let name = &input.ident;
34
35    let hash_expr = match &input.data {
36        Data::Struct(data) => match &data.fields {
37            Fields::Named(fields) => {
38                let field_names: Vec<_> = fields.named.iter().map(|f| &f.ident).collect();
39
40                if field_names.is_empty() {
41                    // Empty struct hashes as unit
42                    quote! { ().hash() }
43                } else if field_names.len() == 1 {
44                    // Single field: just hash the field directly
45                    let field = &field_names[0];
46                    quote! { self.#field.hash() }
47                } else {
48                    // Multiple fields: create nested tuples
49                    build_nested_tuple(&field_names)
50                }
51            }
52            Fields::Unnamed(fields) => {
53                let field_count = fields.unnamed.len();
54
55                if field_count == 0 {
56                    quote! { ().hash() }
57                } else if field_count == 1 {
58                    quote! { self.0.hash() }
59                } else {
60                    // Build nested tuples for tuple structs using indices
61                    let indices: Vec<_> = (0..field_count).map(syn::Index::from).collect();
62                    build_nested_tuple_indexed(&indices)
63                }
64            }
65            Fields::Unit => {
66                quote! { ().hash() }
67            }
68        },
69        Data::Enum(_) => {
70            return syn::Error::new_spanned(
71                &input,
72                "Hashable derive macro does not support enums yet",
73            )
74            .to_compile_error()
75            .into();
76        }
77        Data::Union(_) => {
78            return syn::Error::new_spanned(
79                &input,
80                "Hashable derive macro does not support unions",
81            )
82            .to_compile_error()
83            .into();
84        }
85    };
86
87    TokenStream::from(quote! {
88        impl rose_ztd::Hashable for #name {
89            fn hash(&self) -> rose_ztd::Digest {
90                #hash_expr
91            }
92        }
93    })
94}
95
96/// Build nested tuple expression for named fields: (&self.x, &(&self.y, &self.z))
97fn build_nested_tuple(field_names: &[&Option<syn::Ident>]) -> proc_macro2::TokenStream {
98    let mut iter = field_names.iter().rev();
99    let last = iter.next().unwrap();
100
101    let mut result = quote! { &self.#last };
102
103    for field in iter {
104        result = quote! { (&self.#field, #result) };
105    }
106
107    quote! { #result.hash() }
108}
109
110/// Build nested tuple expression for tuple struct fields: (&self.0, &(&self.1, &self.2))
111fn build_nested_tuple_indexed(indices: &[syn::Index]) -> proc_macro2::TokenStream {
112    let mut iter = indices.iter().rev();
113    let last = iter.next().unwrap();
114
115    let mut result = quote! { &self.#last };
116
117    for index in iter {
118        result = quote! { (&self.#index, #result) };
119    }
120
121    quote! { #result.hash() }
122}
123
124/// Derive macro for implementing the `NounEncode` trait.
125#[proc_macro_derive(NounEncode)]
126pub fn derive_noun_encode(input: TokenStream) -> TokenStream {
127    let input = parse_macro_input!(input as DeriveInput);
128    let name = &input.ident;
129
130    let impl_body = match &input.data {
131        Data::Struct(data) => match &data.fields {
132            Fields::Named(fields) => {
133                let field_names: Vec<_> = fields.named.iter().map(|f| &f.ident).collect();
134
135                if field_names.is_empty() {
136                    quote! { rose_ztd::NounEncode::to_noun(&0u64) }
137                } else if field_names.len() == 1 {
138                    let field = &field_names[0];
139                    quote! { rose_ztd::NounEncode::to_noun(&self.#field) }
140                } else {
141                    let tuple_expr = build_nested_tuple_refs(&field_names);
142                    quote! { rose_ztd::NounEncode::to_noun(&#tuple_expr) }
143                }
144            }
145            Fields::Unnamed(fields) => {
146                let field_count = fields.unnamed.len();
147
148                if field_count == 0 {
149                    quote! { rose_ztd::NounEncode::to_noun(&0u64) }
150                } else if field_count == 1 {
151                    quote! { rose_ztd::NounEncode::to_noun(&self.0) }
152                } else {
153                    let indices: Vec<_> = (0..field_count).map(syn::Index::from).collect();
154                    let tuple_expr = build_nested_tuple_refs_indexed(&indices);
155                    quote! { rose_ztd::NounEncode::to_noun(&#tuple_expr) }
156                }
157            }
158            Fields::Unit => quote! { rose_ztd::NounEncode::to_noun(&0u64) },
159        },
160        Data::Enum(_) => {
161            return syn::Error::new_spanned(
162                &input,
163                "NounEncode derive macro does not support enums yet",
164            )
165            .to_compile_error()
166            .into();
167        }
168        Data::Union(_) => {
169            return syn::Error::new_spanned(
170                &input,
171                "NounEncode derive macro does not support unions",
172            )
173            .to_compile_error()
174            .into();
175        }
176    };
177
178    TokenStream::from(quote! {
179        impl rose_ztd::NounEncode for #name {
180            fn to_noun(&self) -> rose_ztd::Noun {
181                #impl_body
182            }
183        }
184    })
185}
186
187/// Derive macro for implementing the `NounDecode` trait.
188#[proc_macro_derive(NounDecode)]
189pub fn derive_noun_decode(input: TokenStream) -> TokenStream {
190    let input = parse_macro_input!(input as DeriveInput);
191    let name = &input.ident;
192
193    let impl_body = match &input.data {
194        Data::Struct(data) => match &data.fields {
195            Fields::Named(fields) => {
196                let field_names: Vec<_> = fields.named.iter().map(|f| &f.ident).collect();
197
198                if field_names.is_empty() {
199                    quote! {
200                        if noun == rose_ztd::noun::atom(0) {
201                            Some(Self)
202                        } else {
203                            None
204                        }
205                    }
206                } else {
207                    quote! {
208                        let (#( #field_names ),* ) = rose_ztd::NounDecode::from_noun(noun)?;
209                        Some(Self {
210                            #( #field_names ),*
211                        })
212                    }
213                }
214            }
215            Fields::Unnamed(fields) => {
216                let field_count = fields.unnamed.len();
217
218                if field_count == 0 {
219                    quote! {
220                        if noun == rose_ztd::noun::atom(0) {
221                            Some(Self)
222                        } else {
223                            None
224                        }
225                    }
226                } else if field_count == 1 {
227                    quote! { Some(Self(rose_ztd::NounDecode::from_noun(noun)?)) }
228                } else {
229                    let indices: Vec<_> = (0..field_count).map(syn::Index::from).collect();
230                    quote! {
231                        let tup = rose_ztd::NounDecode::from_noun(noun)?;
232                        Some(Self(
233                            #( tup.#indices ),*
234                        ))
235                    }
236                }
237            }
238            Fields::Unit => quote! {
239                if noun == rose_ztd::noun::atom(0) {
240                    Some(Self)
241                } else {
242                    None
243                }
244            },
245        },
246        Data::Enum(_) => {
247            return syn::Error::new_spanned(
248                &input,
249                "NounDecode derive macro does not support enums yet",
250            )
251            .to_compile_error()
252            .into();
253        }
254        Data::Union(_) => {
255            return syn::Error::new_spanned(
256                &input,
257                "NounDecode derive macro does not support unions",
258            )
259            .to_compile_error()
260            .into();
261        }
262    };
263
264    TokenStream::from(quote! {
265        impl rose_ztd::NounDecode for #name {
266            fn from_noun(noun: &rose_ztd::Noun) -> Option<Self> {
267                #impl_body
268            }
269        }
270    })
271}
272
273/// Build nested tuple references: (&self.x, (&self.y, &self.z))
274fn build_nested_tuple_refs(field_names: &[&Option<syn::Ident>]) -> proc_macro2::TokenStream {
275    let mut iter = field_names.iter().rev();
276    let last = iter.next().unwrap();
277
278    let mut result = quote! { &self.#last };
279
280    for field in iter {
281        result = quote! { (&self.#field, #result) };
282    }
283
284    result
285}
286
287/// Build nested tuple references for indices: (&self.0, (&self.1, &self.2))
288fn build_nested_tuple_refs_indexed(indices: &[syn::Index]) -> proc_macro2::TokenStream {
289    let mut iter = indices.iter().rev();
290    let last = iter.next().unwrap();
291
292    let mut result = quote! { &self.#last };
293
294    for index in iter {
295        result = quote! { (&self.#index, #result) };
296    }
297
298    result
299}