1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, Data, DeriveInput, Fields};
4
5#[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 quote! { ().hash() }
43 } else if field_names.len() == 1 {
44 let field = &field_names[0];
46 quote! { self.#field.hash() }
47 } else {
48 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 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 iris_ztd::Hashable for #name {
89 fn hash(&self) -> iris_ztd::Digest {
90 #hash_expr
91 }
92 }
93 })
94}
95
96fn 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
110fn 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#[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! { iris_ztd::NounEncode::to_noun(&0u64) }
137 } else if field_names.len() == 1 {
138 let field = &field_names[0];
139 quote! { iris_ztd::NounEncode::to_noun(&self.#field) }
140 } else {
141 let tuple_expr = build_nested_tuple_refs(&field_names);
142 quote! { iris_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! { iris_ztd::NounEncode::to_noun(&0u64) }
150 } else if field_count == 1 {
151 quote! { iris_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! { iris_ztd::NounEncode::to_noun(&#tuple_expr) }
156 }
157 }
158 Fields::Unit => quote! { iris_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 iris_ztd::NounEncode for #name {
180 fn to_noun(&self) -> iris_ztd::Noun {
181 #impl_body
182 }
183 }
184 })
185}
186
187#[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 == iris_ztd::noun::atom(0) {
201 Some(Self)
202 } else {
203 None
204 }
205 }
206 } else {
207 quote! {
208 let (#( #field_names ),* ) = iris_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 == iris_ztd::noun::atom(0) {
221 Some(Self)
222 } else {
223 None
224 }
225 }
226 } else if field_count == 1 {
227 quote! { Some(Self(iris_ztd::NounDecode::from_noun(noun)?)) }
228 } else {
229 let indices: Vec<_> = (0..field_count).map(syn::Index::from).collect();
230 quote! {
231 let tup = iris_ztd::NounDecode::from_noun(noun)?;
232 Some(Self(
233 #( tup.#indices ),*
234 ))
235 }
236 }
237 }
238 Fields::Unit => quote! {
239 if noun == iris_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 iris_ztd::NounDecode for #name {
266 fn from_noun(noun: &iris_ztd::Noun) -> Option<Self> {
267 #impl_body
268 }
269 }
270 })
271}
272
273fn 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
287fn 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}