plutus_datum_derive/
lib.rs1use proc_macro::TokenStream;
4use quote::quote;
5
6extern crate alloc;
7
8use alloc::vec::Vec;
9use syn::{GenericParam, TypeParamBound};
10
11#[proc_macro_derive(ToDatum, attributes(constructor_datum))]
17pub fn to_datum_derive(input: TokenStream) -> TokenStream {
18 let ast = syn::parse(input).expect("Cannot parse source");
19
20 impl_to_datum_derive(&ast)
21}
22
23fn impl_to_datum_derive(ast: &syn::DeriveInput) -> TokenStream {
24 let name = &ast.ident;
25 let data = &ast.data;
26 let generics = &ast.generics;
27 let mut bounded_generics = generics.clone();
28 for generic_param in bounded_generics.params.iter_mut() {
29 if let GenericParam::Type(ref mut type_param) = *generic_param {
30 type_param
31 .bounds
32 .push(TypeParamBound::Trait(syn::parse_quote!(plutus::ToDatum)));
33 }
34 }
35 let has_constructor_datum_attribute = ast.attrs.iter().any(|attr| {
36 attr.path()
37 .segments
38 .first()
39 .filter(|ps| ps.ident == *"constructor_datum")
40 .is_some()
41 });
42 let body = match data {
43 syn::Data::Struct(ds) => match &ds.fields {
44 syn::Fields::Unnamed(fields_unnamed) => {
45 let len = fields_unnamed.unnamed.len();
46 if len == 1 && !has_constructor_datum_attribute {
47 quote! { self.0.to_datum() }
48 } else {
49 let fields: Vec<_> = core::ops::Range { start: 0, end: len }
50 .map(syn::Index::from)
51 .map(|i| quote! { self.#i.to_datum()})
52 .collect();
53 quote! {
54 plutus::Datum::ConstructorDatum { constructor: 0, fields: vec![#(#fields),*] }
55 }
56 }
57 },
58 syn::Fields::Named(fields_named) => {
59 let idents: Vec<_> =
60 fields_named.named.iter().filter_map(|f| f.ident.clone()).collect();
61 match (&idents[..], has_constructor_datum_attribute) {
62 ([ident], false) => quote! { self.#ident.to_datum() },
63 (idents, _) => {
64 let fields: Vec<_> =
65 idents.iter().map(|token| quote! { self.#token.to_datum()}).collect();
66 quote! {
67 plutus::Datum::ConstructorDatum { constructor: 0, fields: vec![#(#fields),*] }
68 }
69 },
70 }
71 },
72 syn::Fields::Unit => quote! {
73 plutus::Datum::ConstructorDatum { constructor: 0, fields: Vec::new() }
74 },
75 },
76 syn::Data::Enum(_de) => quote! {
77 compile_error!("ToDatum isn't yet implemented for Enum types"),
78 },
79 syn::Data::Union(_du) => quote! {
80 compile_error!("ToDatum isn't yet implemented for Union types"),
81 },
82 };
83 let gen_token_stream = quote! {
84 impl #bounded_generics plutus::ToDatum for #name #generics {
85 fn to_datum(&self) -> plutus::Datum {
86 #body
87 }
88 }
89 };
90 gen_token_stream.into()
91}