borderless_sdk_macros/
lib.rs

1use agent::AgentArgs;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{parse_macro_input, Item, ItemMod};
5
6mod action;
7mod agent;
8mod contract;
9mod schedule;
10mod state;
11mod utils;
12
13// TODO's:
14// - [ ] Check existence of serde crate
15// - [ ] Check re-naming of borderless crate
16
17#[proc_macro_attribute]
18pub fn contract(_attrs: TokenStream, input: TokenStream) -> TokenStream {
19    let module = parse_macro_input!(input as ItemMod);
20
21    // Check if module has some content
22    if module.content.is_none() {
23        return syn::Error::new_spanned(
24            module,
25            "Macro can only be implemented on modules that have some content",
26        )
27        .to_compile_error()
28        .into();
29    }
30    let (brace, mut items) = module.content.unwrap();
31
32    // Generate new tokens based on the module's content
33    let new_tokens = match contract::parse_module_content(brace.span.join(), &items, &module.ident)
34    {
35        Ok(tokens) => tokens,
36        Err(e) => return e.to_compile_error().into(),
37    };
38
39    // Add these new tokens to the existing items
40    items.push(Item::Verbatim(new_tokens));
41
42    // Also generate an error if the visibility is not public
43    match module.vis {
44        syn::Visibility::Public(_) => (),
45        _ => {
46            let tokens =
47                syn::Error::new_spanned(module.mod_token, "Contract module must be public")
48                    .to_compile_error();
49            items.push(Item::Verbatim(tokens));
50        }
51    }
52
53    let wasm_exports = contract::generate_wasm_exports(&module.ident);
54
55    // Generate a new module from the content of the original module
56    let new_module = ItemMod {
57        attrs: module.attrs,
58        vis: module.vis,
59        unsafety: module.unsafety,
60        mod_token: module.mod_token,
61        ident: module.ident,
62        content: Some((brace, items)),
63        semi: module.semi,
64    };
65
66    // Convert into token stream
67    quote! {
68        #new_module
69        #wasm_exports
70    }
71    .into()
72}
73
74#[proc_macro_attribute]
75pub fn agent(attrs: TokenStream, input: TokenStream) -> TokenStream {
76    let module = parse_macro_input!(input as ItemMod);
77    let parsed_attrs = syn::parse_macro_input!(attrs as AgentArgs);
78
79    // Check if module has some content
80    if module.content.is_none() {
81        return syn::Error::new_spanned(
82            module,
83            "Macro can only be implemented on modules that have some content",
84        )
85        .to_compile_error()
86        .into();
87    }
88    let (brace, mut items) = module.content.unwrap();
89
90    // If the agent has access to the websocket or not depends on the attributes
91    let use_ws = parsed_attrs.websocket.unwrap_or_default();
92
93    // Generate new tokens based on the module's content
94    let new_tokens = match agent::parse_module_content(brace.span.join(), &items, use_ws) {
95        Ok(tokens) => tokens,
96        Err(e) => return e.to_compile_error().into(),
97    };
98
99    // Add these new tokens to the existing items
100    items.push(Item::Verbatim(new_tokens));
101
102    // Also generate an error if the visibility is not public
103    match module.vis {
104        syn::Visibility::Public(_) => (),
105        _ => {
106            let tokens = syn::Error::new_spanned(module.mod_token, "Agent module must be public")
107                .to_compile_error();
108            items.push(Item::Verbatim(tokens));
109        }
110    }
111
112    let wasm_exports = agent::generate_wasm_exports(&module.ident);
113
114    // Generate websocket tokens, if websocket == true
115    let wasm_ws_exports = if use_ws {
116        agent::generate_ws_wasm_exports(&module.ident)
117    } else {
118        quote! {}
119    };
120
121    // Generate a new module from the content of the original module
122    let new_module = ItemMod {
123        attrs: module.attrs,
124        vis: module.vis,
125        unsafety: module.unsafety,
126        mod_token: module.mod_token,
127        ident: module.ident,
128        content: Some((brace, items)),
129        semi: module.semi,
130    };
131
132    // Convert into token stream
133    quote! {
134        #new_module
135        #wasm_exports
136        #wasm_ws_exports
137    }
138    .into()
139}
140
141#[proc_macro_derive(State)]
142pub fn derive_contract_state(input: TokenStream) -> TokenStream {
143    let input = parse_macro_input!(input);
144    let output = state::impl_state(input);
145
146    match output {
147        syn::Result::Ok(token_stream) => token_stream,
148        syn::Result::Err(err) => err.to_compile_error(),
149    }
150    .into()
151}
152
153#[proc_macro_attribute]
154pub fn action(_attrs: TokenStream, input: TokenStream) -> TokenStream {
155    input
156}
157
158#[proc_macro_attribute]
159pub fn schedule(_attrs: TokenStream, input: TokenStream) -> TokenStream {
160    input
161}