oasis_runtime_sdk_macros/lib.rs
1#![feature(proc_macro_diagnostic)]
2#![deny(rust_2018_idioms)]
3
4use proc_macro::TokenStream;
5
6mod error_derive;
7mod event_derive;
8mod evm_derive;
9mod generators;
10mod module_derive;
11#[cfg(test)]
12mod test_utils;
13mod version_from_cargo;
14
15/// Emits a compile-time error `msg` from a macro, and uses the span of the
16/// macro invocation if possible. If span info is not available (should only
17/// happen in unit tests), panics with `msg`.
18fn emit_compile_error<S: Into<String> + Clone + std::panic::RefUnwindSafe>(msg: S) -> ! {
19 std::panic::catch_unwind(|| {
20 proc_macro2::Span::call_site()
21 .unwrap()
22 .error(msg.clone().into())
23 .emit();
24 })
25 .ok()
26 .or_else(|| {
27 panic!("{}", msg.into());
28 });
29 unreachable!(); // error().emit() already halts compilation, but type checker doesn't know that
30}
31
32/// Derives the `Event` trait on an enum.
33#[proc_macro_derive(Event, attributes(sdk_event))]
34pub fn event_derive(input: TokenStream) -> TokenStream {
35 let input = syn::parse_macro_input!(input as syn::DeriveInput);
36 event_derive::derive_event(input).into()
37}
38
39/// Derives the `Error` trait on an enum.
40// The helper attribute is `sdk_error` to avoid conflict with `thiserror::Error`.
41#[proc_macro_derive(Error, attributes(sdk_error, source, from))]
42pub fn error_derive(input: TokenStream) -> TokenStream {
43 let input = syn::parse_macro_input!(input as syn::DeriveInput);
44 error_derive::derive_error(input).into()
45}
46
47/// Derives the `EvmEvent` trait on a struct.
48#[proc_macro_derive(EvmEvent, attributes(evm_event))]
49pub fn evm_event_derive(input: TokenStream) -> TokenStream {
50 let input = syn::parse_macro_input!(input as syn::DeriveInput);
51 evm_derive::derive_evm_event(input).into()
52}
53
54/// Derives the `EvmError` trait on an enum.
55#[proc_macro_derive(EvmError, attributes(evm_error))]
56pub fn evm_error_derive(input: TokenStream) -> TokenStream {
57 let input = syn::parse_macro_input!(input as syn::DeriveInput);
58 evm_derive::derive_evm_error(input).into()
59}
60
61/// Derives traits from a non-trait `impl` block (rather than from a `struct`).
62///
63/// Only the `Module` and `EvmContract` traits are supported. In other words,
64/// given an `impl MyModule` block, the macro derives implementations needed either
65/// for implementing a module (see also the `#[handler]` and `#[migration]` attributes)
66/// or for implementing an EVM contract (see also the `#[evm_method]` attribute).
67#[proc_macro_attribute]
68pub fn sdk_derive(args: TokenStream, input: TokenStream) -> TokenStream {
69 let input = syn::parse_macro_input!(input as syn::ItemImpl);
70 match args.to_string().as_str() {
71 "Module" => module_derive::derive_module(input).into(),
72 "EvmContract" => evm_derive::derive_evm_contract(input).into(),
73 _ => emit_compile_error(
74 "#[sdk_derive] only supports #[sdk_derive(Module)] and #[sdk_derive(EvmContract)]",
75 ),
76 }
77}
78
79/// A helper attribute for `#[sdk_derive(...)]`. It doesn't do anyting on its own;
80/// it only marks functions that represent a paratime method handler.
81/// The permitted forms are:
82/// - `#[handler(call = "my_module.MyCall")]`: Marks a function that handles
83/// the "my_module.MyCall" call and can be passed to
84/// oasis_runtime_sdk::module::dispatch_call.
85///
86/// - `#[handler(prefetch = "my_module.MyCall")]`: Marks a function that handles
87/// the request to prefetch any data ahead of the "my_module.MyCall" call.
88/// Its signature should be `Fn(
89/// add_prefix: &mut dyn FnMut(Prefix) -> (),
90/// body: cbor::Value,
91/// auth_info: &AuthInfo,
92/// ) -> Result<(), oasis_runtime_sdk::error::RuntimeError>`
93///
94/// - `#[handler(query = "my_module.MyQuery")]`: Marks a function that handles
95/// the "my_module.MyQuery" query and can be passed to
96/// oasis_runtime_sdk::module::dispatch_query.
97///
98/// - `#[handler(message_result = "my_module.MyMR")]`: Marks a function that handles
99/// the "my_module.MyMR" message result and can be passed to
100/// oasis_runtime_sdk::module::dispatch_message_result.
101///
102/// Query handler can also contain the `expensive` tag. Example:
103/// `#[handler(query = "my_module.MyQuery", expensive)]`.
104/// Queries tagged `expensive` can be enabled/disabled are disabled by default to avoid
105/// excessive costs to the node operator. This can be overridden in the node config.
106///
107/// NOTE: This attribute is parsed by the `#[sdk_derive(...)]` macro, which cannot
108/// interpret the attribute name semantically. Use `#[handler]`, not
109/// `#[oasis_runtime_sdk_macros::handler]` or other paths/aliases.
110#[proc_macro_attribute]
111pub fn handler(_args: TokenStream, input: TokenStream) -> TokenStream {
112 input
113}
114
115/// A helper attribute for `#[sdk_derive(...)]`. It doesn't do anything on its own;
116/// it only marks functions that represent a module state migration.
117///
118/// The permitted forms are:
119/// - `#[migration(init)]`: Marks the initial (genesis) migration.
120/// - `#[migration(from = v)]`: Marks a migration from version v to v+1, where v is
121/// a non-negative integer.
122#[proc_macro_attribute]
123pub fn migration(_args: TokenStream, input: TokenStream) -> TokenStream {
124 input
125}
126
127/// A helper attribute for `#[sdk_derive(...)]`. It doesn't do anything on its own;
128/// it only marks functions that represent contract methods.
129///
130/// The permitted forms are:
131/// - `#[evm_method(signature = "...")]`: The method selector is computed from
132/// a Solidity method signature, and the method takes the precompute handle
133/// and data offset as parameters.
134/// - `#[evm_method(signature = "...", convert)]`: The method selector is
135/// computed from the signature, the arguments are automatically decoded and
136/// passed to the marked method, which must have the appropriate number and
137/// type of arguments.
138#[proc_macro_attribute]
139pub fn evm_method(_args: TokenStream, input: TokenStream) -> TokenStream {
140 input
141}
142
143/// A helper attribute for `#[sdk_derive(...)]`. It doesn't do anything on its own;
144/// it only marks the function within a contract implementation that returns its address.
145///
146/// The method marked with this attribute should take no arguments and return
147/// an object of type `primitive_types::H160`.
148#[proc_macro_attribute]
149pub fn evm_contract_address(_args: TokenStream, input: TokenStream) -> TokenStream {
150 input
151}
152
153/// Constructs an `oasis_sdk::core::common::version::Version` from the Cargo.toml version.
154#[proc_macro]
155pub fn version_from_cargo(_input: TokenStream) -> TokenStream {
156 version_from_cargo::version_from_cargo().into()
157}