1use quote::{quote, quote_spanned};
2use syn::punctuated::Punctuated;
3use syn::spanned::Spanned;
4use syn::{ItemImpl, Meta, Token};
5
6pub(crate) mod common;
7mod contract_impl;
8mod delegate_impl;
9
10struct AttributeArgs {
11 args: Punctuated<Meta, Token![,]>,
12}
13
14impl syn::parse::Parse for AttributeArgs {
15 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
16 let mut args = Punctuated::new();
17 let mut punctuated;
18 let mut count = 0;
19 while !input.is_empty() {
20 punctuated = input.parse::<Token![,]>().ok().is_some();
21 if count > 0 && !punctuated {
22 return Err(syn::Error::new(
23 input.span(),
24 "arguments must be comma separated",
25 ));
26 }
27 let meta = input.parse::<Meta>()?;
28 args.push(meta);
29 count += 1;
30 }
31 Ok(AttributeArgs { args })
32 }
33}
34
35enum ContractType {
36 Raw,
37 Typed,
38 Composable,
39}
40
41#[proc_macro_attribute]
43pub fn contract(
44 args: proc_macro::TokenStream,
45 input: proc_macro::TokenStream,
46) -> proc_macro::TokenStream {
47 let args = syn::parse_macro_input!(args as AttributeArgs);
48 let input = syn::parse_macro_input!(input as ItemImpl);
49 let Some((_, path, _)) = &input.trait_ else {
50 return proc_macro::TokenStream::from(quote_spanned! {
51 input.span() =>
52 compile_error!("only allowed for traits");
53 });
54 };
55 match path.segments.last() {
56 Some(segment) => {
57 let c_type = match segment.ident.to_string().as_str() {
58 "ContractInterface" => ContractType::Raw,
59 "TypedContract" => ContractType::Typed,
60 "ContractComponent" => ContractType::Composable,
61 _ => {
62 return proc_macro::TokenStream::from(quote_spanned! {
63 segment.ident.span() =>
64 compile_error!("trait not supported for contract interaction");
65 })
66 }
67 };
68 contract_impl::contract_ffi_impl(&input, &args, c_type)
69 }
70 None => proc_macro::TokenStream::from(quote_spanned! {
71 path.span() =>
72 compile_error!("missing trait identifier");
73 }),
74 }
75}
76
77#[proc_macro_attribute]
79pub fn delegate(
80 args: proc_macro::TokenStream,
81 input: proc_macro::TokenStream,
82) -> proc_macro::TokenStream {
83 let _args = syn::parse_macro_input!(args as AttributeArgs);
84 let input = syn::parse_macro_input!(input as ItemImpl);
85 let output = delegate_impl::ffi_impl_wrap(&input);
86 proc_macro::TokenStream::from(quote! {
89 #input
90 #output
91 })
92}