1use proc_macro::TokenStream;
2use proc_macro2::Ident;
3use proc_macro_crate::FoundCrate;
4use quote::{format_ident, quote};
5use syn::{parse_macro_input, token::PathSep, Data, DeriveInput, Fields};
6
7fn get_crate_path() -> proc_macro2::TokenStream {
8 let (col, ident) = crate_path_ident();
9 quote!(#col #ident)
10}
11
12fn crate_path_ident() -> (Option<PathSep>, Ident) {
13 match crate_path_fixed() {
14 Some(FoundCrate::Itself) => (None, format_ident!("crate")),
15 Some(FoundCrate::Name(name)) => (Some(Default::default()), format_ident!("{}", name)),
16 None => (None, format_ident!("iris_ztd")),
17 }
18}
19
20fn crate_path_fixed() -> Option<FoundCrate> {
21 let found_crate = proc_macro_crate::crate_name("iris-ztd").ok()?;
22
23 let ret = match found_crate {
24 FoundCrate::Itself => {
25 let has_doc_env = std::env::vars().any(|(k, _)| {
26 k == "UNSTABLE_RUSTDOC_TEST_LINE" || k == "UNSTABLE_RUSTDOC_TEST_PATH"
27 });
28
29 if has_doc_env {
30 FoundCrate::Name("iris_ztd".to_string())
31 } else {
32 FoundCrate::Itself
33 }
34 }
35 x => x,
36 };
37
38 Some(ret)
39}
40
41#[proc_macro_derive(Hashable)]
67pub fn derive_hashable(input: TokenStream) -> TokenStream {
68 let input = parse_macro_input!(input as DeriveInput);
69 let name = &input.ident;
70 let crate_path = get_crate_path();
71
72 let mut generics = input.generics.clone();
73 let where_clause = generics.make_where_clause();
74
75 let [hash_expr, leaf_expr] = [quote!(hash), quote!(leaf_count)].map(|func| {
76 match &input.data {
77 Data::Struct(data) => {
78 for field in &data.fields {
79 let ty = &field.ty;
80 where_clause
81 .predicates
82 .push(syn::parse_quote!(#ty: #crate_path::Hashable));
83 }
84
85 match &data.fields {
86 Fields::Named(fields) => {
87 let field_names: Vec<_> = fields.named.iter().map(|f| &f.ident).collect();
88
89 if field_names.is_empty() {
90 quote! { ().#func() }
92 } else if field_names.len() == 1 {
93 let field = &field_names[0];
95 quote! { self.#field.#func() }
96 } else {
97 let expr = build_nested_tuple_refs(&field_names);
99 quote! { #expr.#func() }
100 }
101 }
102 Fields::Unnamed(fields) => {
103 let field_count = fields.unnamed.len();
104
105 if field_count == 0 {
106 quote! { ().#func() }
107 } else if field_count == 1 {
108 quote! { self.0.#func() }
109 } else {
110 let indices: Vec<_> = (0..field_count).map(syn::Index::from).collect();
112 let expr = build_nested_tuple_refs_indexed(&indices);
113 quote! { #expr.#func() }
114 }
115 }
116 Fields::Unit => {
117 quote! { ().#func() }
118 }
119 }
120 }
121 Data::Enum(_) => {
122 syn::Error::new_spanned(&input, "Hashable derive macro does not support enums yet")
123 .to_compile_error()
124 }
125 Data::Union(_) => {
126 syn::Error::new_spanned(&input, "Hashable derive macro does not support unions")
127 .to_compile_error()
128 }
129 }
130 });
131
132 let pair_expr = match &input.data {
133 Data::Struct(data) => {
134 for field in &data.fields {
135 let ty = &field.ty;
136 where_clause
137 .predicates
138 .push(syn::parse_quote!(#ty: #crate_path::Hashable));
139 }
140
141 match &data.fields {
142 Fields::Named(fields) => {
143 let field_names: Vec<_> = fields.named.iter().map(|f| &f.ident).collect();
144
145 if field_names.is_empty() {
146 quote! { ().hashable_pair() }
148 } else if field_names.len() == 1 {
149 let field = &field_names[0];
151 quote! { self.#field.hashable_pair() }
152 } else {
153 let expr = build_nested_tuple_refs(&field_names);
155 quote! { Some(#expr) }
156 }
157 }
158 Fields::Unnamed(fields) => {
159 let field_count = fields.unnamed.len();
160
161 if field_count == 0 {
162 quote! { ().hashable_pair() }
163 } else if field_count == 1 {
164 quote! { self.0.hashable_pair() }
165 } else {
166 let indices: Vec<_> = (0..field_count).map(syn::Index::from).collect();
168 let expr = build_nested_tuple_refs_indexed(&indices);
169 quote! { Some(#expr) }
170 }
171 }
172 Fields::Unit => {
173 quote! { ().hashable_pair() }
174 }
175 }
176 }
177 Data::Enum(_) => {
178 return syn::Error::new_spanned(
179 &input,
180 "Hashable derive macro does not support enums yet",
181 )
182 .to_compile_error()
183 .into();
184 }
185 Data::Union(_) => {
186 return syn::Error::new_spanned(
187 &input,
188 "Hashable derive macro does not support unions",
189 )
190 .to_compile_error()
191 .into();
192 }
193 };
194
195 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
196
197 TokenStream::from(quote! {
198 impl #impl_generics #crate_path::Hashable for #name #ty_generics #where_clause {
199 fn hash(&self) -> #crate_path::Digest {
200 #hash_expr
201 }
202
203 fn leaf_count(&self) -> usize {
204 #leaf_expr
205 }
206
207 fn hashable_pair(&self) -> Option<(impl #crate_path::Hashable + '_, impl #crate_path::Hashable + '_)> {
208 #pair_expr
209 }
210 }
211 })
212}
213
214#[proc_macro_derive(NounEncode, attributes(noun_tag))]
216pub fn derive_noun_encode(input: TokenStream) -> TokenStream {
217 let input = parse_macro_input!(input as DeriveInput);
218 let name = &input.ident;
219 let crate_path = get_crate_path();
220
221 let mut generics = input.generics.clone();
222 let where_clause = generics.make_where_clause();
223
224 let impl_body = match &input.data {
225 Data::Struct(data) => {
226 for field in &data.fields {
227 let ty = &field.ty;
228 where_clause
229 .predicates
230 .push(syn::parse_quote!(#ty: #crate_path::NounEncode));
231 }
232
233 match &data.fields {
234 Fields::Named(fields) => {
235 let field_names: Vec<_> = fields.named.iter().map(|f| &f.ident).collect();
236
237 if field_names.is_empty() {
238 quote! { #crate_path::NounEncode::to_noun(&0u64) }
239 } else if field_names.len() == 1 {
240 let field = &field_names[0];
241 quote! { #crate_path::NounEncode::to_noun(&self.#field) }
242 } else {
243 let tuple_expr = build_nested_tuple_refs(&field_names);
244 quote! { #crate_path::NounEncode::to_noun(&#tuple_expr) }
245 }
246 }
247 Fields::Unnamed(fields) => {
248 let field_count = fields.unnamed.len();
249
250 if field_count == 0 {
251 quote! { #crate_path::NounEncode::to_noun(&0u64) }
252 } else if field_count == 1 {
253 quote! { #crate_path::NounEncode::to_noun(&self.0) }
254 } else {
255 let indices: Vec<_> = (0..field_count).map(syn::Index::from).collect();
256 let tuple_expr = build_nested_tuple_refs_indexed(&indices);
257 quote! { #crate_path::NounEncode::to_noun(&#tuple_expr) }
258 }
259 }
260 Fields::Unit => quote! { #crate_path::NounEncode::to_noun(&0u64) },
261 }
262 }
263 Data::Enum(data) => {
264 let mut match_arms = Vec::new();
265
266 for variant in &data.variants {
267 let variant_ident = &variant.ident;
268 let mut tag_value = None;
269
270 for attr in &variant.attrs {
271 if attr.path().is_ident("noun_tag") {
272 if let Ok(lit) = attr.parse_args::<syn::LitStr>() {
273 tag_value = Some(lit.value());
274 }
275 }
276 }
277
278 let tag_value = tag_value.unwrap_or_else(|| variant_ident.to_string());
279
280 match &variant.fields {
281 Fields::Unit => {
282 match_arms.push(quote! {
283 Self::#variant_ident => #crate_path::NounEncode::to_noun(&#tag_value),
284 });
285 }
286 Fields::Unnamed(fields) => {
287 let field_count = fields.unnamed.len();
288 if field_count == 0 {
289 match_arms.push(quote! {
290 Self::#variant_ident => #crate_path::NounEncode::to_noun(&#tag_value),
291 });
292 } else if field_count == 1 {
293 match_arms.push(quote! {
294 Self::#variant_ident(v0) => {
295 let tag_noun = #crate_path::NounEncode::to_noun(&#tag_value);
296 let rest_noun = #crate_path::NounEncode::to_noun(v0);
297 #crate_path::NounEncode::to_noun(&(tag_noun, rest_noun))
298 }
299 });
300 } else {
301 let idents: Vec<_> =
302 (0..field_count).map(|i| format_ident!("v{}", i)).collect();
303 let ref_idents: Vec<_> = idents.iter().collect();
304 let tuple_expr = build_nested_tuple_expr_for_idents(&ref_idents);
305 match_arms.push(quote! {
306 Self::#variant_ident( #( #idents ),* ) => {
307 let tag_noun = #crate_path::NounEncode::to_noun(&#tag_value);
308 let rest_noun = #crate_path::NounEncode::to_noun(&#tuple_expr);
309 #crate_path::NounEncode::to_noun(&(tag_noun, rest_noun))
310 }
311 });
312 }
313 }
314 Fields::Named(fields) => {
315 let field_names: Vec<_> = fields
316 .named
317 .iter()
318 .map(|f| f.ident.as_ref().unwrap())
319 .collect();
320 if field_names.is_empty() {
321 match_arms.push(quote! {
322 Self::#variant_ident {} => #crate_path::NounEncode::to_noun(&#tag_value),
323 });
324 } else if field_names.len() == 1 {
325 let field = field_names[0];
326 match_arms.push(quote! {
327 Self::#variant_ident { #field } => {
328 let tag_noun = #crate_path::NounEncode::to_noun(&#tag_value);
329 let rest_noun = #crate_path::NounEncode::to_noun(#field);
330 #crate_path::NounEncode::to_noun(&(tag_noun, rest_noun))
331 }
332 });
333 } else {
334 let tuple_expr = build_nested_tuple_expr_for_idents(&field_names);
335 match_arms.push(quote! {
336 Self::#variant_ident { #( #field_names ),* } => {
337 let tag_noun = #crate_path::NounEncode::to_noun(&#tag_value);
338 let rest_noun = #crate_path::NounEncode::to_noun(&#tuple_expr);
339 #crate_path::NounEncode::to_noun(&(tag_noun, rest_noun))
340 }
341 });
342 }
343 }
344 }
345 }
346
347 quote! {
348 match self {
349 #( #match_arms )*
350 }
351 }
352 }
353 Data::Union(_) => {
354 return syn::Error::new_spanned(
355 &input,
356 "NounEncode derive macro does not support unions",
357 )
358 .to_compile_error()
359 .into();
360 }
361 };
362
363 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
364
365 TokenStream::from(quote! {
366 impl #impl_generics #crate_path::NounEncode for #name #ty_generics #where_clause {
367 fn to_noun(&self) -> #crate_path::Noun {
368 #impl_body
369 }
370 }
371 })
372}
373
374#[proc_macro_derive(NounDecode, attributes(noun_tag))]
376pub fn derive_noun_decode(input: TokenStream) -> TokenStream {
377 let input = parse_macro_input!(input as DeriveInput);
378 let name = &input.ident;
379 let crate_path = get_crate_path();
380
381 let mut generics = input.generics.clone();
382 let where_clause = generics.make_where_clause();
383
384 let impl_body = match &input.data {
385 Data::Struct(data) => {
386 for field in &data.fields {
387 let ty = &field.ty;
388 where_clause
389 .predicates
390 .push(syn::parse_quote!(#ty: #crate_path::NounDecode));
391 }
392
393 match &data.fields {
394 Fields::Named(fields) => {
395 let field_names: Vec<_> = fields.named.iter().map(|f| &f.ident).collect();
396
397 if field_names.is_empty() {
398 quote! {
399 if noun == #crate_path::noun::atom(0) {
400 Some(Self)
401 } else {
402 None
403 }
404 }
405 } else {
406 quote! {
407 let (#( #field_names ),* ) = #crate_path::NounDecode::from_noun(noun)?;
408 Some(Self {
409 #( #field_names ),*
410 })
411 }
412 }
413 }
414 Fields::Unnamed(fields) => {
415 let field_count = fields.unnamed.len();
416
417 if field_count == 0 {
418 quote! {
419 if noun == #crate_path::noun::atom(0) {
420 Some(Self)
421 } else {
422 None
423 }
424 }
425 } else if field_count == 1 {
426 quote! { Some(Self(#crate_path::NounDecode::from_noun(noun)?)) }
427 } else {
428 let indices: Vec<_> = (0..field_count).map(syn::Index::from).collect();
429 quote! {
430 let tup = #crate_path::NounDecode::from_noun(noun)?;
431 Some(Self(
432 #( tup.#indices ),*
433 ))
434 }
435 }
436 }
437 Fields::Unit => quote! {
438 if noun == #crate_path::noun::atom(0) {
439 Some(Self)
440 } else {
441 None
442 }
443 },
444 }
445 }
446 Data::Enum(data) => {
447 let mut cell_match_arms = Vec::new();
448 let mut atom_match_arms = Vec::new();
449
450 for variant in &data.variants {
451 let variant_ident = &variant.ident;
452 let mut tag_value = None;
453
454 for attr in &variant.attrs {
455 if attr.path().is_ident("noun_tag") {
456 if let Ok(lit) = attr.parse_args::<syn::LitStr>() {
457 tag_value = Some(lit.value());
458 }
459 }
460 }
461
462 let tag_value = tag_value.unwrap_or_else(|| variant_ident.to_string());
463
464 match &variant.fields {
465 Fields::Unit => {
466 atom_match_arms.push(quote! {
467 #tag_value => Some(Self::#variant_ident),
468 });
469 }
470 Fields::Unnamed(fields) => {
471 let field_count = fields.unnamed.len();
472 if field_count == 0 {
473 atom_match_arms.push(quote! {
474 #tag_value => Some(Self::#variant_ident()),
475 });
476 } else if field_count == 1 {
477 cell_match_arms.push(quote! {
478 #tag_value => {
479 Some(Self::#variant_ident(#crate_path::NounDecode::from_noun(&rest)?))
480 }
481 });
482 } else {
483 let idents: Vec<_> =
484 (0..field_count).map(|i| format_ident!("v{}", i)).collect();
485 cell_match_arms.push(quote! {
486 #tag_value => {
487 let ( #( #idents ),* ) = #crate_path::NounDecode::from_noun(&rest)?;
488 Some(Self::#variant_ident( #( #idents ),* ))
489 }
490 });
491 }
492 }
493 Fields::Named(fields) => {
494 let field_names: Vec<_> = fields.named.iter().map(|f| &f.ident).collect();
495 if field_names.is_empty() {
496 atom_match_arms.push(quote! {
497 #tag_value => Some(Self::#variant_ident {}),
498 });
499 } else {
500 cell_match_arms.push(quote! {
501 #tag_value => {
502 let ( #( #field_names ),* ) = #crate_path::NounDecode::from_noun(&rest)?;
503 Some(Self::#variant_ident { #( #field_names ),* })
504 }
505 });
506 }
507 }
508 }
509 }
510
511 quote! {
512 match noun {
513 #crate_path::Noun::Atom(atom) => {
514 let a = atom.to_le_bytes();
515 let s = core::str::from_utf8(&a).ok()?;
516 match s {
517 #( #atom_match_arms )*
518 _ => None,
519 }
520 }
521 #crate_path::Noun::Cell(ref a, rest) => {
522 if let #crate_path::Noun::Atom(a) = &**a {
523 let a = a.to_le_bytes();
524 let tag = core::str::from_utf8(&a).ok()?;
525 match tag {
526 #( #cell_match_arms )*
527 _ => None,
528 }
529 } else {
530 None
531 }
532 }
533 }
534 }
535 }
536 Data::Union(_) => {
537 return syn::Error::new_spanned(
538 &input,
539 "NounDecode derive macro does not support unions",
540 )
541 .to_compile_error()
542 .into();
543 }
544 };
545
546 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
547
548 TokenStream::from(quote! {
549 impl #impl_generics #crate_path::NounDecode for #name #ty_generics #where_clause {
550 fn from_noun(noun: &#crate_path::Noun) -> Option<Self> {
551 #impl_body
552 }
553 }
554 })
555}
556
557fn build_nested_tuple_refs(field_names: &[&Option<syn::Ident>]) -> proc_macro2::TokenStream {
559 let mut iter = field_names.iter().rev();
560 let last = iter.next().unwrap();
561
562 let mut result = quote! { &self.#last };
563
564 for field in iter {
565 result = quote! { (&self.#field, #result) };
566 }
567
568 result
569}
570
571fn build_nested_tuple_refs_indexed(indices: &[syn::Index]) -> proc_macro2::TokenStream {
573 let mut iter = indices.iter().rev();
574 let last = iter.next().unwrap();
575
576 let mut result = quote! { &self.#last };
577
578 for index in iter {
579 result = quote! { (&self.#index, #result) };
580 }
581
582 result
583}
584
585fn build_nested_tuple_expr_for_idents(idents: &[&syn::Ident]) -> proc_macro2::TokenStream {
587 let mut iter = idents.iter().rev();
588 let last = iter.next().unwrap();
589
590 let mut result = quote! { #last };
591
592 for ident in iter {
593 result = quote! { (#ident, &#result) };
594 }
595
596 result
597}
598
599fn to_snake_case(s: &str) -> String {
601 let mut out = String::new();
602 for (i, c) in s.char_indices() {
603 if c.is_uppercase() {
604 if i > 0 && !s.as_bytes()[i - 1].is_ascii_uppercase() {
605 out.push('_');
606 }
607 out.push(c.to_ascii_lowercase());
608 } else {
609 out.push(c);
610 }
611 }
612 out
613}
614
615fn to_lower_camel_case(s: &str) -> String {
617 let mut out = String::new();
618 let mut capitalize_next = false;
619 let mut first = true;
620 for c in s.chars() {
621 if c == '_' {
622 capitalize_next = true;
623 } else if first {
624 out.push(c.to_ascii_lowercase());
625 first = false;
626 } else if capitalize_next {
627 out.push(c.to_ascii_uppercase());
628 capitalize_next = false;
629 } else {
630 out.push(c);
631 }
632 }
633 out
634}
635
636#[proc_macro_attribute]
639pub fn wasm_noun_codec(attr: TokenStream, item: TokenStream) -> TokenStream {
640 let input = parse_macro_input!(item as DeriveInput);
641 let name = &input.ident;
642 let name_str = name.to_string();
643
644 let attr_str = attr.to_string();
645 let no_hash = attr_str.contains("no_hash");
646 let no_noun = attr_str.contains("no_noun");
647 let with_prove = attr_str.contains("with_prove");
648 let no_derive = attr_str.contains("no_derive");
649
650 let snake_name = to_snake_case(&name_str);
651 let camel_name = to_lower_camel_case(&name_str);
652
653 let to_noun_snake = format_ident!("{}_to_noun", snake_name);
654 let from_noun_snake = format_ident!("{}_from_noun", snake_name);
655 let hash_snake = format_ident!("{}_hash", snake_name);
656 let prove_snake = format_ident!("{}_prove", snake_name);
657
658 let to_noun_camel = format!("{}ToNoun", camel_name);
659 let from_noun_camel = format!("{}FromNoun", camel_name);
660 let hash_camel = format!("{}Hash", camel_name);
661 let prove_camel = format!("{}Prove", camel_name);
662
663 let mod_name = format_ident!("__wasm_noun_codec_{}", snake_name);
664
665 let crate_path = get_crate_path();
666
667 let derive_attrs = if no_derive {
668 quote! {}
669 } else {
670 quote! {
671 #[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
672 #[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
673 }
674 };
675
676 let hash_fn = if no_hash {
677 quote! {}
678 } else {
679 quote! {
680 #[wasm_bindgen(js_name = #hash_camel)]
681 pub fn #hash_snake(v: &#name) -> #crate_path::Digest {
682 #crate_path::Hashable::hash(v)
683 }
684 }
685 };
686
687 let prove_fn = if with_prove {
688 quote! {
689 #[wasm_bindgen(js_name = #prove_camel)]
693 pub fn #prove_snake(v: &#name, leaf_index: u32) -> #crate_path::MerkleProvenAxis {
694 #crate_path::MerkleProof::prove_hashable(v, leaf_index as usize)
695 }
696 }
697 } else {
698 quote! {}
699 };
700
701 let noun_fn = if no_noun {
702 quote! {}
703 } else {
704 quote! {
705 #[wasm_bindgen(js_name = #to_noun_camel)]
707 pub fn #to_noun_snake(v: &#name) -> #crate_path::Noun {
708 #crate_path::NounEncode::to_noun(v)
709 }
710
711 #[wasm_bindgen(js_name = #from_noun_camel)]
713 pub fn #from_noun_snake(noun: &#crate_path::Noun) -> ::core::result::Result<#name, JsValue> {
714 #crate_path::NounDecode::from_noun(noun)
715 .ok_or_else(|| JsValue::from_str("Failed to decode noun"))
716 }
717 }
718 };
719
720 let expanded = quote! {
721 #derive_attrs
722 #input
723
724 #[cfg(feature = "wasm")]
725 mod #mod_name {
726 use super::*;
727 use wasm_bindgen::prelude::*;
728
729 #noun_fn
730 #hash_fn
731 #prove_fn
732 }
733 };
734
735 TokenStream::from(expanded)
736}
737
738#[proc_macro_attribute]
741pub fn wasm_member_methods(_attr: TokenStream, item: TokenStream) -> TokenStream {
742 let mut input = parse_macro_input!(item as syn::ItemImpl);
743
744 let ty = &input.self_ty;
746 let type_name = if let syn::Type::Path(type_path) = &**ty {
747 if let Some(segment) = type_path.path.segments.last() {
748 segment.ident.to_string()
749 } else {
750 return syn::Error::new_spanned(ty, "Expected a type path")
751 .to_compile_error()
752 .into();
753 }
754 } else {
755 return syn::Error::new_spanned(ty, "Expected a type path")
756 .to_compile_error()
757 .into();
758 };
759
760 let snake_name = to_snake_case(&type_name);
761 let mod_name = format_ident!("__wasm_member_methods_{}", snake_name);
762
763 let mut generated_methods = Vec::new();
764
765 for item in &mut input.items {
766 if let syn::ImplItem::Fn(method) = item {
767 if !matches!(method.vis, syn::Visibility::Public(_)) {
769 continue;
770 }
771
772 let sig = &method.sig;
773 let method_ident = &sig.ident;
774 let method_name_str = method_ident.to_string();
775
776 let mut transform_output: Option<(syn::Type, syn::Expr)> = None;
778
779 method.attrs.retain(|attr| {
780 if attr.path().is_ident("transform_output") {
781 if let syn::Meta::List(meta_list) = &attr.meta {
783 let tokens: Vec<_> = meta_list.tokens.clone().into_iter().collect();
784
785 let mut ty_tokens = proc_macro2::TokenStream::new();
786 let mut expr_tokens = proc_macro2::TokenStream::new();
787 let mut in_ty = true;
788
789 for t in tokens {
790 if in_ty {
791 if let proc_macro2::TokenTree::Punct(ref p) = t {
792 if p.as_char() == ',' {
793 in_ty = false;
794 continue;
795 }
796 }
797 ty_tokens.extend(std::iter::once(t));
798 } else {
799 expr_tokens.extend(std::iter::once(t));
800 }
801 }
802
803 if let Ok(ty) = syn::parse2(ty_tokens) {
805 if let Ok(expr) = syn::parse2(expr_tokens) {
806 transform_output = Some((ty, expr));
807 }
808 }
809 }
810 false } else {
812 true }
814 });
815
816 let camel_method_name = to_lower_camel_case(&method_name_str);
817 let struct_camel = to_lower_camel_case(&type_name);
818 let camel_method_capitalized = {
819 let mut c = camel_method_name.chars();
820 match c.next() {
821 None => String::new(),
822 Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
823 }
824 };
825 let js_name_combined = format!("{}{}", struct_camel, camel_method_capitalized);
826 let js_name_attr =
827 quote! { #[wasm_bindgen::prelude::wasm_bindgen(js_name = #js_name_combined)] };
828
829 let doc_comments: Vec<_> = method
831 .attrs
832 .iter()
833 .filter(|a| a.path().is_ident("doc"))
834 .collect();
835
836 let mut args = Vec::new();
837 let mut pass_args = Vec::new();
838 let mut has_receiver = false;
839
840 for arg in &sig.inputs {
841 match arg {
842 syn::FnArg::Receiver(r) => {
843 has_receiver = true;
844 let mutf = r.mutability;
845 if r.reference.is_some() {
847 if mutf.is_some() {
848 args.push(quote! { obj: &mut #ty });
849 } else {
850 args.push(quote! { obj: &#ty });
851 }
852 } else {
853 args.push(quote! { obj: #ty });
854 }
855 }
856 syn::FnArg::Typed(pat_type) => {
857 let pat = &pat_type.pat;
858 let pt_ty = &pat_type.ty;
859 args.push(quote! { #pat: #pt_ty });
860 pass_args.push(quote! { #pat });
861 }
862 }
863 }
864
865 let call_expr = if has_receiver {
866 quote! { obj.#method_ident(#(#pass_args),*) }
867 } else {
868 quote! { <#ty>::#method_ident(#(#pass_args),*) }
869 };
870
871 let (final_output, body) =
872 if let Some((transform_ty, transform_expr)) = transform_output {
873 let out = quote! { -> #transform_ty };
874 let b = quote! {
875 let out = #call_expr;
876 #transform_expr
877 };
878 (out, b)
879 } else {
880 let mut output = sig.output.clone();
881 if let syn::ReturnType::Type(_, ret_ty) = &mut output {
883 if let syn::Type::Path(type_path) = &mut **ret_ty {
884 if type_path.path.is_ident("Self") {
885 **ret_ty = *(*ty).clone();
886 }
887 }
888 }
889 let out = quote! { #output };
890 let b = quote! { #call_expr };
891 (out, b)
892 };
893
894 generated_methods.push(quote! {
895 #(#doc_comments)*
896 #js_name_attr
897 pub fn #method_ident(#(#args),*) #final_output {
898 #body
899 }
900 });
901 }
902 }
903
904 let expanded = quote! {
905 #input
906
907 #[cfg(feature = "wasm")]
908 #[allow(non_snake_case)]
909 mod #mod_name {
910 use super::*;
911
912 #(#generated_methods)*
913 }
914 };
915
916 TokenStream::from(expanded)
917}
918
919#[proc_macro_attribute]
924pub fn noun_derive(attr: TokenStream, item: TokenStream) -> TokenStream {
925 let mut input = parse_macro_input!(item as syn::ItemEnum);
926 let crate_path = get_crate_path();
927 let enum_name = &input.ident;
928 let vis = &input.vis;
929
930 let attr_args = proc_macro2::TokenStream::from(attr);
932 let requested_derives: Vec<String> = attr_args
933 .into_iter()
934 .filter_map(|tt| {
935 if let proc_macro2::TokenTree::Ident(ident) = tt {
936 Some(ident.to_string())
937 } else {
938 None
939 }
940 })
941 .collect();
942
943 let wants_noun_encode = requested_derives.iter().any(|s| s == "NounEncode");
944 let wants_noun_decode = requested_derives.iter().any(|s| s == "NounDecode");
945 let wants_hashable = requested_derives.iter().any(|s| s == "Hashable");
946 let wants_serialize = requested_derives.iter().any(|s| s == "Serialize");
947 let wants_deserialize = requested_derives.iter().any(|s| s == "Deserialize");
948 let wants_wasm = requested_derives.iter().any(|s| s == "tsify_wasm");
949
950 let handled = [
952 "NounEncode",
953 "NounDecode",
954 "Hashable",
955 "Serialize",
956 "Deserialize",
957 "tsify_wasm",
958 ];
959 let passthrough_derives: Vec<proc_macro2::TokenStream> = requested_derives
960 .iter()
961 .filter(|s| !handled.contains(&s.as_str()))
962 .map(|s| {
963 let ident = format_ident!("{}", s);
964 quote! { #ident }
965 })
966 .collect();
967
968 let mut tag_ident = format_ident!("tag");
970 input.attrs.retain(|attr| {
971 if !attr.path().is_ident("noun") {
972 return true;
973 }
974 let _ = attr.parse_nested_meta(|meta| {
975 if meta.path.is_ident("tag_ident") {
976 if let Ok(value) = meta.value() {
977 if let Ok(ident) = value.parse::<syn::Ident>() {
978 tag_ident = ident;
979 }
980 }
981 }
982 Ok(())
983 });
984 false
985 });
986
987 enum NounVariantKind {
989 Cell, TagU64(u64), TagStr(String), EmbedTag, }
994
995 struct VariantFieldInfo {
997 decl_pattern: proc_macro2::TokenStream,
999 vars: Vec<syn::Ident>,
1001 types: Vec<syn::Type>,
1003 named: bool,
1005 }
1006
1007 impl VariantFieldInfo {
1008 fn from_fields(fields: &syn::Fields) -> Self {
1009 match fields {
1010 Fields::Named(f) => {
1011 let names: Vec<syn::Ident> = f
1012 .named
1013 .iter()
1014 .map(|fld| fld.ident.clone().unwrap())
1015 .collect();
1016 let types: Vec<syn::Type> = f.named.iter().map(|fld| fld.ty.clone()).collect();
1017 let decl_pattern = if names.is_empty() {
1018 quote! { {} }
1019 } else {
1020 quote! { { #(#names),* } }
1021 };
1022 VariantFieldInfo {
1023 decl_pattern,
1024 vars: names,
1025 types,
1026 named: true,
1027 }
1028 }
1029 Fields::Unnamed(f) => {
1030 let count = f.unnamed.len();
1031 let names: Vec<syn::Ident> =
1032 (0..count).map(|i| format_ident!("v{}", i)).collect();
1033 let types: Vec<syn::Type> =
1034 f.unnamed.iter().map(|fld| fld.ty.clone()).collect();
1035 let decl_pattern = if count == 0 {
1036 quote! {}
1037 } else {
1038 quote! { ( #(#names),* ) }
1039 };
1040 VariantFieldInfo {
1041 decl_pattern,
1042 vars: names,
1043 types,
1044 named: false,
1045 }
1046 }
1047 Fields::Unit => VariantFieldInfo {
1048 decl_pattern: quote! {},
1049 vars: vec![],
1050 types: vec![],
1051 named: false,
1052 },
1053 }
1054 }
1055
1056 fn len(&self) -> usize {
1057 self.vars.len()
1058 }
1059
1060 fn nested_tuple_expr(&self) -> proc_macro2::TokenStream {
1062 if self.vars.is_empty() {
1063 return quote! {};
1064 }
1065 let mut iter = self.vars.iter().rev();
1066 let last = iter.next().unwrap();
1067 let mut result = quote! { #last };
1068 for ident in iter {
1069 result = quote! { (#ident, &#result) };
1070 }
1071 result
1072 }
1073 }
1074
1075 struct VariantInfo {
1076 ident: syn::Ident,
1077 kind: NounVariantKind,
1078 fields: syn::Fields,
1079 fi: VariantFieldInfo,
1080 }
1081
1082 let mut variants_info = Vec::new();
1083 let mut cell_count = 0;
1084
1085 for variant in &mut input.variants {
1086 let mut kind = None;
1087
1088 variant.attrs.retain(|attr| {
1089 if !attr.path().is_ident("noun") {
1090 return true;
1091 }
1092 let _ = attr.parse_nested_meta(|meta| {
1093 if meta.path.is_ident("cell") {
1094 kind = Some(NounVariantKind::Cell);
1095 cell_count += 1;
1096 } else if meta.path.is_ident("tag") {
1097 let value = meta.value().expect("noun(tag = ...) requires a value");
1098 let lit: syn::Lit = value.parse().expect("tag value must be a literal");
1099 match lit {
1100 syn::Lit::Int(lit_int) => {
1101 let v: u64 = lit_int.base10_parse().expect("tag must be u64");
1102 kind = Some(NounVariantKind::TagU64(v));
1103 }
1104 syn::Lit::Str(lit_str) => {
1105 kind = Some(NounVariantKind::TagStr(lit_str.value()));
1106 }
1107 _ => panic!("noun(tag = ...) value must be an integer or string literal"),
1108 }
1109 } else if meta.path.is_ident("embedded_tag") {
1110 kind = Some(NounVariantKind::EmbedTag);
1111 }
1112
1113 Ok(())
1114 });
1115 false });
1117
1118 let kind = kind.unwrap_or_else(|| NounVariantKind::TagStr(variant.ident.to_string()));
1119
1120 let fi = VariantFieldInfo::from_fields(&variant.fields);
1121 variants_info.push(VariantInfo {
1122 ident: variant.ident.clone(),
1123 kind,
1124 fields: variant.fields.clone(),
1125 fi,
1126 });
1127 }
1128
1129 if cell_count > 1 {
1130 panic!("Only one #[noun(cell)] variant is allowed per enum");
1131 }
1132
1133 let noun_encode_impl = if wants_noun_encode {
1135 let mut arms = Vec::new();
1136 for vi in &variants_info {
1137 let vident = &vi.ident;
1138 let decl = &vi.fi.decl_pattern;
1139 match &vi.kind {
1140 NounVariantKind::Cell | NounVariantKind::EmbedTag => {
1141 if vi.fi.len() == 0 {
1143 arms.push(quote! {
1144 Self::#vident #decl => #crate_path::NounEncode::to_noun(&0u64),
1145 });
1146 } else if vi.fi.len() == 1 {
1147 let var = &vi.fi.vars[0];
1148 arms.push(quote! {
1149 Self::#vident #decl => #crate_path::NounEncode::to_noun(#var),
1150 });
1151 } else {
1152 let tuple_expr = vi.fi.nested_tuple_expr();
1153 arms.push(quote! {
1154 Self::#vident #decl => #crate_path::NounEncode::to_noun(&(#tuple_expr)),
1155 });
1156 }
1157 }
1158 NounVariantKind::TagU64(tag) => {
1159 if vi.fi.len() == 0 {
1160 arms.push(quote! {
1161 Self::#vident => #crate_path::NounEncode::to_noun(&(#tag as u64)),
1162 });
1163 } else {
1164 let rest_expr = if vi.fi.len() == 1 {
1165 let var = &vi.fi.vars[0];
1166 quote! { #crate_path::NounEncode::to_noun(#var) }
1167 } else {
1168 let tuple_expr = vi.fi.nested_tuple_expr();
1169 quote! { #crate_path::NounEncode::to_noun(&(#tuple_expr)) }
1170 };
1171 arms.push(quote! {
1172 Self::#vident #decl => {
1173 let tag_noun = #crate_path::NounEncode::to_noun(&(#tag as u64));
1174 let rest_noun = #rest_expr;
1175 #crate_path::NounEncode::to_noun(&(tag_noun, rest_noun))
1176 }
1177 });
1178 }
1179 }
1180 NounVariantKind::TagStr(tag) => {
1181 if vi.fi.len() == 0 {
1182 arms.push(quote! {
1183 Self::#vident => #crate_path::NounEncode::to_noun(&#tag),
1184 });
1185 } else {
1186 let rest_expr = if vi.fi.len() == 1 {
1187 let var = &vi.fi.vars[0];
1188 quote! { #crate_path::NounEncode::to_noun(#var) }
1189 } else {
1190 let tuple_expr = vi.fi.nested_tuple_expr();
1191 quote! { #crate_path::NounEncode::to_noun(&(#tuple_expr)) }
1192 };
1193 arms.push(quote! {
1194 Self::#vident #decl => {
1195 let tag_noun = #crate_path::NounEncode::to_noun(&#tag);
1196 let rest_noun = #rest_expr;
1197 #crate_path::NounEncode::to_noun(&(tag_noun, rest_noun))
1198 }
1199 });
1200 }
1201 }
1202 }
1203 }
1204 quote! {
1205 impl #crate_path::NounEncode for #enum_name {
1206 fn to_noun(&self) -> #crate_path::Noun {
1207 match self {
1208 #(#arms)*
1209 }
1210 }
1211 }
1212 }
1213 } else {
1214 quote! {}
1215 };
1216
1217 let noun_decode_impl = if wants_noun_decode {
1219 let cell_variant = variants_info
1221 .iter()
1222 .find(|v| matches!(v.kind, NounVariantKind::Cell));
1223
1224 let cell_fallback = if let Some(cv) = cell_variant {
1225 let cv_ident = &cv.ident;
1226 if cv.fi.len() == 0 {
1227 quote! {
1228 return Some(Self::#cv_ident);
1229 }
1230 } else if cv.fi.len() == 1 {
1231 let var = &cv.fi.vars[0];
1232 let construct = if cv.fi.named {
1233 quote! { Self::#cv_ident { #var: #crate_path::NounDecode::from_noun(noun)? } }
1234 } else {
1235 quote! { Self::#cv_ident(#crate_path::NounDecode::from_noun(noun)?) }
1236 };
1237 quote! {
1238 return Some(#construct);
1239 }
1240 } else {
1241 let vars = &cv.fi.vars;
1242 let construct = if cv.fi.named {
1243 quote! { Self::#cv_ident { #(#vars),* } }
1244 } else {
1245 quote! { Self::#cv_ident( #(#vars),* ) }
1246 };
1247 quote! {
1248 let ( #(#vars),* ) = #crate_path::NounDecode::from_noun(noun)?;
1249 return Some(#construct);
1250 }
1251 }
1252 } else {
1253 quote! { return None; }
1254 };
1255
1256 let mut u64_tag_arms = Vec::new();
1258 for vi in &variants_info {
1259 if let NounVariantKind::TagU64(tag) = &vi.kind {
1260 let vident = &vi.ident;
1261 if vi.fi.len() == 0 {
1262 u64_tag_arms.push(quote! {
1263 #tag => return Some(Self::#vident),
1264 });
1265 } else if vi.fi.len() == 1 {
1266 let var = &vi.fi.vars[0];
1267 let construct = if vi.fi.named {
1268 quote! { Self::#vident { #var: #crate_path::NounDecode::from_noun(rest)? } }
1269 } else {
1270 quote! { Self::#vident(#crate_path::NounDecode::from_noun(rest)?) }
1271 };
1272 u64_tag_arms.push(quote! {
1273 #tag => return Some(#construct),
1274 });
1275 } else {
1276 let vars = &vi.fi.vars;
1277 let construct = if vi.fi.named {
1278 quote! { Self::#vident { #(#vars),* } }
1279 } else {
1280 quote! { Self::#vident( #(#vars),* ) }
1281 };
1282 u64_tag_arms.push(quote! {
1283 #tag => {
1284 let ( #(#vars),* ) = #crate_path::NounDecode::from_noun(rest)?;
1285 return Some(#construct);
1286 }
1287 });
1288 }
1289 }
1290 }
1291
1292 let mut str_tag_arms = Vec::new();
1294 for vi in &variants_info {
1295 if let NounVariantKind::TagStr(tag) = &vi.kind {
1296 let vident = &vi.ident;
1297 if vi.fi.len() == 0 {
1298 str_tag_arms.push(quote! {
1299 #tag => return Some(Self::#vident),
1300 });
1301 } else if vi.fi.len() == 1 {
1302 let var = &vi.fi.vars[0];
1303 let construct = if vi.fi.named {
1304 quote! { Self::#vident { #var: #crate_path::NounDecode::from_noun(rest)? } }
1305 } else {
1306 quote! { Self::#vident(#crate_path::NounDecode::from_noun(rest)?) }
1307 };
1308 str_tag_arms.push(quote! {
1309 #tag => return Some(#construct),
1310 });
1311 } else {
1312 let vars = &vi.fi.vars;
1313 let construct = if vi.fi.named {
1314 quote! { Self::#vident { #(#vars),* } }
1315 } else {
1316 quote! { Self::#vident( #(#vars),* ) }
1317 };
1318 str_tag_arms.push(quote! {
1319 #tag => {
1320 let ( #(#vars),* ) = #crate_path::NounDecode::from_noun(rest)?;
1321 return Some(#construct);
1322 }
1323 });
1324 }
1325 }
1326 }
1327
1328 let has_u64_tags = !u64_tag_arms.is_empty();
1329 let has_str_tags = !str_tag_arms.is_empty();
1330
1331 let u64_match_block = if has_u64_tags {
1332 quote! {
1333 if let Some(tag_u64) = <u64 as #crate_path::NounDecode>::from_noun(tag_noun) {
1334 match tag_u64 {
1335 #(#u64_tag_arms)*
1336 _ => {}
1337 }
1338 }
1339 }
1340 } else {
1341 quote! {}
1342 };
1343
1344 let str_match_block = if has_str_tags {
1345 quote! {
1346 {
1347 let a_bytes = tag_atom.to_le_bytes();
1348 if let Ok(tag_str) = core::str::from_utf8(&a_bytes) {
1349 match tag_str {
1350 #(#str_tag_arms)*
1351 _ => {}
1352 }
1353 }
1354 }
1355 }
1356 } else {
1357 quote! {}
1358 };
1359
1360 let mut embed_match_arms = Vec::new();
1361 for vi in &variants_info {
1362 if let NounVariantKind::EmbedTag = vi.kind {
1363 let vident = &vi.ident;
1364 match &vi.fields {
1365 Fields::Unnamed(f) if f.unnamed.len() == 1 => {
1366 embed_match_arms.push(quote! {
1367 if let Some(v) = #crate_path::NounDecode::from_noun(noun).map(Self::#vident) {
1368 return Some(v);
1369 }
1370 });
1371 }
1372 Fields::Named(f) if f.named.len() == 1 => {
1373 let var = &vi.fi.vars[0];
1374 embed_match_arms.push(quote! {
1375 if let Some(#var) = #crate_path::NounDecode::from_noun(noun) {
1376 return Some(Self::#vident { #var });
1377 }
1378 });
1379 }
1380 Fields::Unit => {
1381 panic!("Cannot use embedded_tag on unit variants");
1382 }
1383 _ => {}
1384 }
1385 }
1386 }
1387
1388 let embed_match_block = quote! {
1389 {
1390 #(#embed_match_arms)*
1391 }
1392 };
1393
1394 quote! {
1395 impl #crate_path::NounDecode for #enum_name {
1396 fn from_noun(noun: &#crate_path::Noun) -> Option<Self> {
1397 match noun {
1398 #crate_path::Noun::Cell(ref tag_noun, ref rest) => {
1399 if let #crate_path::Noun::Atom(ref tag_atom) = **tag_noun {
1401 #u64_match_block
1403 #str_match_block
1405 #embed_match_block
1407 None
1409 } else {
1410 #cell_fallback
1412 }
1413 }
1414 #crate_path::Noun::Atom(_) => {
1415 #cell_fallback
1417 }
1418 }
1419 }
1420 }
1421 }
1422 } else {
1423 quote! {}
1424 };
1425
1426 let hashable_impl = if wants_hashable {
1428 let mut arms_hash = Vec::new();
1429 let mut arms_leaves = Vec::new();
1430 let mut arms_pairs = Vec::new();
1431 let mut eithers = vec![];
1432 for (i, vi) in variants_info.iter().enumerate() {
1433 let vident = &vi.ident;
1434 let cur_eithers = if i == variants_info.len() - 1 {
1435 eithers.clone()
1436 } else {
1437 [&eithers[..], &[quote!(#crate_path::Either::Left)][..]].concat()
1438 };
1439 let nest = |a: proc_macro2::TokenStream| {
1440 cur_eithers.iter().rev().fold(a, |acc, f| quote!(#f(#acc)))
1441 };
1442 match &vi.kind {
1443 NounVariantKind::Cell | NounVariantKind::EmbedTag => {
1444 let decl = &vi.fi.decl_pattern;
1445 if vi.fi.len() >= 1 {
1446 let var = &vi.fi.vars[0];
1447 arms_hash.push(quote! {
1448 Self::#vident #decl => #crate_path::Hashable::hash(#var),
1449 });
1450 arms_leaves.push(quote! {
1451 Self::#vident #decl => #crate_path::Hashable::leaf_count(#var),
1452 });
1453 let nested_a = nest(quote!(a));
1454 let nested_b = nest(quote!(b));
1455 arms_pairs.push(quote! {
1456 Self::#vident #decl => #crate_path::Hashable::hashable_pair(#var).map(|(a, b)| (#nested_a, #nested_b)),
1457 });
1458 } else {
1459 arms_hash.push(quote! {
1460 Self::#vident => #crate_path::Hashable::hash(&0u64),
1461 });
1462 arms_leaves.push(quote! {
1463 Self::#vident => #crate_path::Hashable::leaf_count(&0u64),
1464 });
1465 let nested_a = nest(quote!(a));
1466 let nested_b = nest(quote!(b));
1467 arms_pairs.push(quote! {
1468 Self::#vident => Option::<((),())>::None.map(|(a, b)| (#nested_a, #nested_b)),
1469 });
1470 }
1471 }
1472 NounVariantKind::TagU64(tag) => {
1473 let decl = &vi.fi.decl_pattern;
1474 if vi.fi.len() >= 1 {
1475 let tuple_expr = vi.fi.nested_tuple_expr();
1477 arms_hash.push(quote! {
1478 Self::#vident #decl => #crate_path::Hashable::hash(&(#tag as u64, &(#tuple_expr))),
1479 });
1480 arms_leaves.push(quote! {
1481 Self::#vident #decl => #crate_path::Hashable::leaf_count(&(#tag as u64, &(#tuple_expr))),
1482 });
1483 let nested_tag = nest(quote!(#tag as u64));
1484 let nested_value = nest(quote!(v_rest));
1485 arms_pairs.push(quote! {
1486 Self::#vident #decl => { let v_rest = (#tuple_expr); Some((#nested_tag, #nested_value)) },
1487 });
1488 } else {
1489 arms_hash.push(quote! {
1490 Self::#vident => #crate_path::Hashable::hash(&(#tag as u64)),
1491 });
1492 arms_leaves.push(quote! {
1493 Self::#vident => #crate_path::Hashable::leaf_count(&(#tag as u64)),
1494 });
1495 let nested_a = nest(quote!(a));
1496 let nested_b = nest(quote!(b));
1497 arms_pairs.push(quote! {
1498 Self::#vident => Option::<((),())>::None.map(|(a, b)| (#nested_a, #nested_b)),
1499 });
1500 }
1501 }
1502 NounVariantKind::TagStr(tag) => {
1503 let decl = &vi.fi.decl_pattern;
1504 if vi.fi.len() >= 1 {
1505 let tuple_expr = vi.fi.nested_tuple_expr();
1506 arms_hash.push(quote! {
1507 Self::#vident #decl => #crate_path::Hashable::hash(&(#tag, &(#tuple_expr))),
1508 });
1509 arms_leaves.push(quote! {
1510 Self::#vident #decl => #crate_path::Hashable::leaf_count(&(#tag, &(#tuple_expr))),
1511 });
1512 let nested_tag = nest(quote!(#tag));
1513 let nested_value = nest(quote!(v_rest));
1514 arms_pairs.push(quote! {
1515 Self::#vident #decl => { let v_rest = (#tuple_expr); Some((#nested_tag, #nested_value)) },
1516 });
1517 } else {
1518 arms_hash.push(quote! {
1519 Self::#vident => #crate_path::Hashable::hash(&#tag),
1520 });
1521 arms_leaves.push(quote! {
1522 Self::#vident => #crate_path::Hashable::leaf_count(&#tag),
1523 });
1524 let nested_a = nest(quote!(a));
1525 let nested_b = nest(quote!(b));
1526 arms_pairs.push(quote! {
1527 Self::#vident => Option::<((),())>::None.map(|(a, b)| (#nested_a, #nested_b)),
1528 });
1529 }
1530 }
1531 }
1532 eithers.push(quote!(#crate_path::Either::Right));
1533 }
1534 quote! {
1535 impl #crate_path::Hashable for #enum_name {
1536 fn hash(&self) -> #crate_path::Digest {
1537 match self {
1538 #(#arms_hash)*
1539 }
1540 }
1541
1542 fn leaf_count(&self) -> usize {
1543 match self {
1544 #(#arms_leaves)*
1545 }
1546 }
1547
1548 fn hashable_pair<'a>(&'a self) -> Option<(impl #crate_path::Hashable + 'a, impl #crate_path::Hashable + 'a)> {
1549 match self {
1550 #(#arms_pairs)*
1551 }
1552 }
1553 }
1554 }
1555 } else {
1556 quote! {}
1557 };
1558
1559 let serde_impl = if wants_serialize || wants_deserialize {
1561 let shadow_mod_name = format_ident!(
1562 "__noun_derive_shadow_{}",
1563 to_snake_case(&enum_name.to_string())
1564 );
1565 let shadow_owned_name = enum_name.clone();
1566 let shadow_borrowed_name = format_ident!("__ShadowBorrowed{}", enum_name);
1567
1568 let mut shadow_owned_variants = Vec::new();
1570 let mut shadow_borrowed_variants = Vec::new();
1571 let mut from_shadow_arms = Vec::new(); let mut to_shadow_owned_arms = Vec::new(); let mut into_shadow_arms = Vec::new(); for vi in &variants_info {
1576 let vident = &vi.ident;
1577 match &vi.kind {
1578 NounVariantKind::Cell | NounVariantKind::EmbedTag => {
1579 let decl = &vi.fi.decl_pattern;
1581 let vars = &vi.fi.vars;
1582 let types = &vi.fi.types;
1583 if vi.fi.len() == 0 {
1584 shadow_owned_variants.push(quote! {
1585 #vident,
1586 });
1587 shadow_borrowed_variants.push(quote! {
1588 #vident,
1589 });
1590 from_shadow_arms.push(quote! {
1591 #shadow_owned_name::#vident => super::#enum_name::#vident,
1592 });
1593 to_shadow_owned_arms.push(quote! {
1594 super::#enum_name::#vident => #shadow_owned_name::#vident,
1595 });
1596 into_shadow_arms.push(quote! {
1597 super::#enum_name::#vident => #shadow_borrowed_name::#vident,
1598 });
1599 } else if vi.fi.len() == 1 && !vi.fi.named {
1600 let inner_ty = &types[0];
1601 shadow_owned_variants.push(quote! {
1602 #vident(#inner_ty),
1603 });
1604 shadow_borrowed_variants.push(quote! {
1605 #vident(&'a #inner_ty),
1606 });
1607 from_shadow_arms.push(quote! {
1608 #shadow_owned_name::#vident(v) => super::#enum_name::#vident(v),
1609 });
1610 to_shadow_owned_arms.push(quote! {
1611 super::#enum_name::#vident(v) => #shadow_owned_name::#vident(v),
1612 });
1613 into_shadow_arms.push(quote! {
1614 super::#enum_name::#vident(ref v) => #shadow_borrowed_name::#vident(v),
1615 });
1616 } else {
1617 shadow_owned_variants.push(quote! {
1619 #vident { #(#vars: #types),* },
1620 });
1621 shadow_borrowed_variants.push(quote! {
1622 #vident { #(#vars: &'a #types),* },
1623 });
1624 from_shadow_arms.push(quote! {
1625 #shadow_owned_name::#vident { #(#vars),* } => super::#enum_name::#vident #decl,
1626 });
1627 to_shadow_owned_arms.push(quote! {
1628 super::#enum_name::#vident #decl => #shadow_owned_name::#vident { #(#vars),* },
1629 });
1630 into_shadow_arms.push(quote! {
1631 super::#enum_name::#vident { #(ref #vars),* } => #shadow_borrowed_name::#vident { #(#vars),* },
1632 });
1633 }
1634 }
1635 NounVariantKind::TagU64(tag) => {
1636 let tag_str = tag.to_string();
1637 let decl = &vi.fi.decl_pattern;
1638 let vars = &vi.fi.vars;
1639 let types = &vi.fi.types;
1640 if vi.fi.len() == 0 {
1641 shadow_owned_variants.push(quote! {
1642 #vident {
1643 #[cfg_attr(feature = "wasm", tsify(type = #tag_str))]
1644 #tag_ident: u64
1645 },
1646 });
1647 shadow_borrowed_variants.push(quote! {
1648 #vident { #tag_ident: u64 },
1649 });
1650 from_shadow_arms.push(quote! {
1651 #shadow_owned_name::#vident { .. } => super::#enum_name::#vident,
1652 });
1653 to_shadow_owned_arms.push(quote! {
1654 super::#enum_name::#vident => #shadow_owned_name::#vident { #tag_ident: #tag },
1655 });
1656 into_shadow_arms.push(quote! {
1657 super::#enum_name::#vident => #shadow_borrowed_name::#vident { #tag_ident: #tag },
1658 });
1659 } else if vi.fi.len() == 1 && !vi.fi.named {
1660 let inner_ty = &types[0];
1661 shadow_owned_variants.push(quote! {
1662 #vident {
1663 #[cfg_attr(feature = "wasm", tsify(type = #tag_str))]
1664 #tag_ident: u64,
1665 #[serde(flatten)]
1666 value: #inner_ty,
1667 },
1668 });
1669 shadow_borrowed_variants.push(quote! {
1670 #vident {
1671 #tag_ident: u64,
1672 #[serde(flatten)]
1673 value: &'a #inner_ty,
1674 },
1675 });
1676 from_shadow_arms.push(quote! {
1677 #shadow_owned_name::#vident { value, .. } => super::#enum_name::#vident(value),
1678 });
1679 to_shadow_owned_arms.push(quote! {
1680 super::#enum_name::#vident(v) => #shadow_owned_name::#vident { #tag_ident: #tag, value: v },
1681 });
1682 into_shadow_arms.push(quote! {
1683 super::#enum_name::#vident(ref v) => #shadow_borrowed_name::#vident { #tag_ident: #tag, value: v },
1684 });
1685 } else {
1686 shadow_owned_variants.push(quote! {
1688 #vident {
1689 #[cfg_attr(feature = "wasm", tsify(type = #tag_str))]
1690 #tag_ident: u64,
1691 #(#vars: #types),*
1692 },
1693 });
1694 shadow_borrowed_variants.push(quote! {
1695 #vident {
1696 #tag_ident: u64,
1697 #(#vars: &'a #types),*
1698 },
1699 });
1700 from_shadow_arms.push(quote! {
1701 #shadow_owned_name::#vident { #(#vars),*, .. } => super::#enum_name::#vident #decl,
1702 });
1703 to_shadow_owned_arms.push(quote! {
1704 super::#enum_name::#vident #decl => #shadow_owned_name::#vident { #tag_ident: #tag, #(#vars),* },
1705 });
1706 into_shadow_arms.push(quote! {
1707 super::#enum_name::#vident { #(ref #vars),* } => #shadow_borrowed_name::#vident { #tag_ident: #tag, #(#vars),* },
1708 });
1709 }
1710 }
1711 NounVariantKind::TagStr(tag) => {
1712 let tag_str = format!("\"{}\"", tag);
1713 let decl = &vi.fi.decl_pattern;
1714 let vars = &vi.fi.vars;
1715 let types = &vi.fi.types;
1716 if vi.fi.len() == 0 {
1717 shadow_owned_variants.push(quote! {
1718 #vident {
1719 #[cfg_attr(feature = "wasm", tsify(type = #tag_str))]
1720 #tag_ident: alloc::string::String
1721 },
1722 });
1723 shadow_borrowed_variants.push(quote! {
1724 #vident { #tag_ident: &'a str },
1725 });
1726 from_shadow_arms.push(quote! {
1727 #shadow_owned_name::#vident { .. } => super::#enum_name::#vident,
1728 });
1729 to_shadow_owned_arms.push(quote! {
1730 super::#enum_name::#vident => #shadow_owned_name::#vident { #tag_ident: #tag.into() },
1731 });
1732 into_shadow_arms.push(quote! {
1733 super::#enum_name::#vident => #shadow_borrowed_name::#vident { #tag_ident: #tag },
1734 });
1735 } else if vi.fi.len() == 1 && !vi.fi.named {
1736 let inner_ty = &types[0];
1737 shadow_owned_variants.push(quote! {
1738 #vident {
1739 #[cfg_attr(feature = "wasm", tsify(type = #tag_str))]
1740 #tag_ident: alloc::string::String,
1741 #[serde(flatten)]
1742 value: #inner_ty,
1743 },
1744 });
1745 shadow_borrowed_variants.push(quote! {
1746 #vident {
1747 #tag_ident: &'a str,
1748 #[serde(flatten)]
1749 value: &'a #inner_ty,
1750 },
1751 });
1752 from_shadow_arms.push(quote! {
1753 #shadow_owned_name::#vident { value, .. } => super::#enum_name::#vident(value),
1754 });
1755 to_shadow_owned_arms.push(quote! {
1756 super::#enum_name::#vident(v) => #shadow_owned_name::#vident { #tag_ident: #tag.into(), value: v },
1757 });
1758 into_shadow_arms.push(quote! {
1759 super::#enum_name::#vident(ref v) => #shadow_borrowed_name::#vident { #tag_ident: #tag, value: v },
1760 });
1761 } else {
1762 shadow_owned_variants.push(quote! {
1764 #vident {
1765 #[cfg_attr(feature = "wasm", tsify(type = #tag_str))]
1766 #tag_ident: alloc::string::String,
1767 #(#vars: #types),*
1768 },
1769 });
1770 shadow_borrowed_variants.push(quote! {
1771 #vident {
1772 #tag_ident: &'a str,
1773 #(#vars: &'a #types),*
1774 },
1775 });
1776 from_shadow_arms.push(quote! {
1777 #shadow_owned_name::#vident { #(#vars),*, .. } => super::#enum_name::#vident #decl,
1778 });
1779 to_shadow_owned_arms.push(quote! {
1780 super::#enum_name::#vident #decl => #shadow_owned_name::#vident { #tag_ident: #tag.into(), #(#vars),* },
1781 });
1782 into_shadow_arms.push(quote! {
1783 super::#enum_name::#vident { #(ref #vars),* } => #shadow_borrowed_name::#vident { #tag_ident: #tag, #(#vars),* },
1784 });
1785 }
1786 }
1787 }
1788 }
1789
1790 let serialize_impl = if wants_serialize {
1791 quote! {
1792 impl serde::Serialize for #enum_name {
1793 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1794 where
1795 S: serde::Serializer,
1796 {
1797 let shadow: #shadow_mod_name::#shadow_borrowed_name = self.into();
1798 shadow.serialize(serializer)
1799 }
1800 }
1801 }
1802 } else {
1803 quote! {}
1804 };
1805
1806 let deserialize_impl = if wants_deserialize {
1807 quote! {
1808 impl<'de> serde::Deserialize<'de> for #enum_name {
1809 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1810 where
1811 D: serde::Deserializer<'de>,
1812 {
1813 let shadow = #shadow_mod_name::#shadow_owned_name::deserialize(deserializer)?;
1814 Ok(shadow.into())
1815 }
1816 }
1817 }
1818 } else {
1819 quote! {}
1820 };
1821
1822 let wasm_impl = if wants_wasm {
1823 quote! {
1824 #[cfg(feature = "wasm")]
1825 const _: () = {
1826 use tsify::Tsify;
1827 use wasm_bindgen::JsValue;
1828 use wasm_bindgen::convert::*;
1829 use wasm_bindgen::describe::*;
1830 use wasm_bindgen::UnwrapThrowExt;
1831
1832 impl Tsify for #enum_name {
1833 type JsType = <#shadow_mod_name::#shadow_owned_name as Tsify>::JsType;
1834 const DECL: &'static str = <#shadow_mod_name::#shadow_owned_name as Tsify>::DECL;
1835 }
1836
1837 impl WasmDescribe for #enum_name {
1838 #[inline]
1839 fn describe() {
1840 <#shadow_mod_name::#shadow_owned_name as WasmDescribe>::describe()
1841 }
1842 }
1843
1844 #[automatically_derived]
1845 impl WasmDescribeVector for #enum_name {
1846 #[inline]
1847 fn describe_vector() {
1848 <#shadow_mod_name::#shadow_owned_name as WasmDescribeVector>::describe_vector()
1849 }
1850 }
1851
1852 impl IntoWasmAbi for #enum_name {
1853 type Abi = <#shadow_mod_name::#shadow_owned_name as IntoWasmAbi>::Abi;
1854
1855 #[inline]
1856 fn into_abi(self) -> Self::Abi {
1857 let shadow: #shadow_mod_name::#shadow_owned_name = self.into();
1858 shadow.into_abi()
1859 }
1860 }
1861
1862 impl FromWasmAbi for #enum_name {
1863 type Abi = <#shadow_mod_name::#shadow_owned_name as FromWasmAbi>::Abi;
1864
1865 #[inline]
1866 unsafe fn from_abi(js: Self::Abi) -> Self {
1867 let shadow = <#shadow_mod_name::#shadow_owned_name as FromWasmAbi>::from_abi(js);
1868 shadow.into()
1869 }
1870 }
1871
1872 #[automatically_derived]
1873 impl OptionFromWasmAbi for #enum_name {
1874 #[inline]
1875 fn is_none(js: &Self::Abi) -> bool {
1876 <<Self as Tsify>::JsType as OptionFromWasmAbi>::is_none(js)
1877 }
1878 }
1879
1880 pub struct SelfOwner<T>(T);
1881
1882 #[automatically_derived]
1883 impl<T> ::core::ops::Deref for SelfOwner<T> {
1884 type Target = T;
1885
1886 fn deref(&self) -> &Self::Target {
1887 &self.0
1888 }
1889 }
1890
1891 impl RefFromWasmAbi for #enum_name {
1892 type Abi = <<Self as Tsify>::JsType as RefFromWasmAbi>::Abi;
1893
1894 type Anchor = SelfOwner<Self>;
1895
1896 unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor {
1897 let result = <Self as Tsify>::from_js(&*<<Self as Tsify>::JsType as RefFromWasmAbi>::ref_from_abi(js));
1898 if let Err(err) = result {
1899 wasm_bindgen::throw_str(err.to_string().as_ref());
1900 }
1901 SelfOwner(result.unwrap_throw())
1902 }
1903 }
1904
1905 #[automatically_derived]
1906 impl VectorFromWasmAbi for #enum_name {
1907 type Abi = <<Self as Tsify>::JsType as VectorFromWasmAbi>::Abi;
1908
1909 #[inline]
1910 unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]> {
1911 <<Self as Tsify>::JsType as VectorFromWasmAbi>::vector_from_abi(js)
1912 .into_iter()
1913 .map(|value| {
1914 let result = Self::from_js(value);
1915 if let Err(err) = result {
1916 wasm_bindgen::throw_str(err.to_string().as_ref());
1917 }
1918 result.unwrap_throw()
1919 })
1920 .collect()
1921 }
1922 }
1923 };
1924 }
1925 } else {
1926 quote! {}
1927 };
1928
1929 quote! {
1930 #[allow(non_snake_case)]
1931 mod #shadow_mod_name {
1932 use super::*;
1933
1934 #[derive(serde::Serialize, serde::Deserialize)]
1935 #[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
1936 #[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
1937 #[serde(untagged)]
1938 pub enum #shadow_owned_name {
1939 #(#shadow_owned_variants)*
1940 }
1941
1942 #[derive(serde::Serialize)]
1943 #[serde(untagged)]
1944 pub enum #shadow_borrowed_name<'a> {
1945 #(#shadow_borrowed_variants)*
1946 }
1947
1948 impl From<#shadow_owned_name> for super::#enum_name {
1949 fn from(shadow: #shadow_owned_name) -> Self {
1950 match shadow {
1951 #(#from_shadow_arms)*
1952 }
1953 }
1954 }
1955
1956 impl From<super::#enum_name> for #shadow_owned_name {
1957 fn from(val: super::#enum_name) -> Self {
1958 match val {
1959 #(#to_shadow_owned_arms)*
1960 }
1961 }
1962 }
1963
1964 impl<'a> From<&'a super::#enum_name> for #shadow_borrowed_name<'a> {
1965 fn from(val: &'a super::#enum_name) -> Self {
1966 match val {
1967 #(#into_shadow_arms)*
1968 }
1969 }
1970 }
1971 }
1972
1973 #serialize_impl
1974 #deserialize_impl
1975 #wasm_impl
1976 }
1977 } else {
1978 quote! {}
1979 };
1980
1981 let passthrough_derive_attr = if !passthrough_derives.is_empty() {
1984 quote! { #[derive(#(#passthrough_derives),*)] }
1985 } else {
1986 quote! {}
1987 };
1988
1989 let variants = &input.variants;
1990 let original_attrs = &input.attrs;
1991
1992 let expanded = quote! {
1993 #(#original_attrs)*
1994 #passthrough_derive_attr
1995 #vis enum #enum_name {
1996 #variants
1997 }
1998
1999 #noun_encode_impl
2000 #noun_decode_impl
2001 #hashable_impl
2002 #serde_impl
2003 };
2004
2005 TokenStream::from(expanded)
2006}