sails_macros/lib.rs
1//! Procedural macros for the `Sails` framework.
2
3use proc_macro::TokenStream;
4use proc_macro_error::proc_macro_error;
5
6/// Generates code for turning a Rust impl block into a Sails service
7/// based on a set of public methods of the block. See
8/// [documentation](https://github.com/gear-tech/sails?tab=readme-ov-file#application)
9/// for details.
10///
11/// The macro can be customized with the following arguments:
12/// - `crate` - specifies path to the `sails-rs` crate allowing the latter
13/// to be imported with a different name, for example, when the
14/// `sails-rs` create is re-exported from another crate.
15/// - `events` - specifies a Rust enum type denoting events that the service can emit.
16/// See [documentation](https://github.com/gear-tech/sails?tab=readme-ov-file#events)
17/// for details.
18/// - `extends` - specifies a list of other services the service extends using the mixin pattern.
19/// See [documentation](https://github.com/gear-tech/sails?tab=readme-ov-file#service-extending-mixins)
20/// for details.
21///
22/// # Examples
23///
24/// ```rust
25/// mod my_service {
26/// use sails_rs::{export, service};
27///
28/// #[derive(parity_scale_codec::Encode, scale_info::TypeInfo)]
29/// pub enum MyServiceEvents {
30/// SomethingDone,
31/// }
32///
33/// pub struct MyService;
34///
35/// #[service(events = MyServiceEvents)]
36/// impl MyService {
37/// #[export]
38/// pub fn do_something(&mut self) -> u32 {
39/// self.emit_event(MyServiceEvents::SomethingDone).unwrap();
40/// 0
41/// }
42///
43/// #[export]
44/// pub fn get_something(&self) -> u32 {
45/// 0
46/// }
47/// }
48/// }
49/// ```
50#[proc_macro_error]
51#[proc_macro_attribute]
52pub fn service(args: TokenStream, impl_tokens: TokenStream) -> TokenStream {
53 sails_macros_core::gservice(args.into(), impl_tokens.into()).into()
54}
55
56/// Generates code for turning a Rust impl block into a Sails program
57/// based on a set of public methods of the block. See
58/// [documentation](https://github.com/gear-tech/sails?tab=readme-ov-file#application)
59/// for details.
60///
61/// The macro can be customized with the following arguments:
62/// - `crate` - specifies path to the `sails-rs` crate allowing the latter
63/// to be imported with a different name, for example, when the
64/// `sails-rs` create is re-exported from another crate.
65/// - `handle_signal` - specifies a path to a function that will be called
66/// after standard signal handling provided by the `gstd` crate.
67/// - `payable` - specifies that the program can accept transfers of value.
68///
69/// The macro also accepts a `handle_reply` attribute that can be used to specify a function
70/// that will handle replies. This function should be defined within the program and accepts `&self`.
71/// The function will be called automatically when a reply is received.
72///
73/// # Examples
74///
75/// ```rust
76/// mod my_program {
77/// use sails_rs::program;
78///
79/// pub struct MyProgram;
80///
81/// #[program(payable)]
82/// impl MyProgram {
83/// pub fn default() -> Self {
84/// Self
85/// }
86///
87/// pub fn from_seed(_seed: u32) -> Self {
88/// Self
89/// }
90///
91/// #[handle_reply]
92/// fn inspect_reply(&self) {
93/// // Handle reply here
94/// }
95/// }
96/// }
97/// ```
98#[proc_macro_error]
99#[proc_macro_attribute]
100pub fn program(args: TokenStream, impl_tokens: TokenStream) -> TokenStream {
101 sails_macros_core::gprogram(args.into(), impl_tokens.into()).into()
102}
103
104/// Customizes how a service/program method is exposed based on specified arguments.
105///
106/// The attribute accepts two optional arguments:
107/// - `route` - Defines a custom route for the method.
108/// By default, every exposed service method is accessible via a route derived from its name,
109/// converted to PascalCase. This argument allows you to override the default route with a
110/// string of your choice.
111/// - `unwrap_result` - Indicates that the method's `Result<T, E>` return value should be unwrapped.
112/// If specified, the method will panic if the result is an `Err`.
113///
114/// # Examples
115///
116/// The following example demonstrates the use of the `export` attribute applied to the `do_something` method.
117/// - The `route` argument customizes the route to "Something" (convertered to PascalCase).
118/// - The `unwrap_result` argument ensures that the method's result is unwrapped, causing it to panic
119/// with the message "Something went wrong" if the result is an `Err`.
120///
121/// ```rust
122/// mod my_service {
123/// use sails_rs::{export, service};
124///
125/// struct MyService;
126///
127/// #[service]
128/// impl MyService {
129/// #[export(route = "something", unwrap_result)]
130/// pub fn do_something(&mut self) -> Result<u32, String> {
131/// Err("Something went wrong".to_string())
132/// }
133/// }
134/// }
135/// ```
136#[proc_macro_error]
137#[proc_macro_attribute]
138pub fn export(args: TokenStream, impl_item_fn_tokens: TokenStream) -> TokenStream {
139 sails_macros_core::export(args.into(), impl_item_fn_tokens.into()).into()
140}
141
142/// Defines event for using within Gear and Ethereum ecosystem.
143///
144/// Trait `SailsEvent` provides a uniform interface to encode an event into a tuple
145/// of variant name and data payload.
146///
147/// Trait `EthEvent` provides a uniform interface to convert an event into the topics and data payload
148/// that are used to emit logs in the Ethereum Virtual Machine (EVM). The logs generated by the EVM
149/// consist of:
150///
151/// - **Topics:** An array of 32-byte values. The first topic is always the keccak256 hash of the event
152/// signature, while the remaining topics correspond to indexed fields. For dynamic types (as determined
153/// by `<T as alloy_sol_types::SolType>::IS_DYNAMIC`), the ABI-encoded value is hashed before being stored.
154/// For static types, the ABI-encoded value is left-padded with zeros to 32 bytes.
155/// - **Data:** A byte array containing the ABI-encoded non-indexed fields of the event, encoded as a tuple.
156///
157/// This is intended to be used with the `#[sails_rs::event]` procedural macro, which automatically
158/// implements the trait for your enum-based event definitions.
159///
160/// # Examples
161///
162/// Given an event definition:
163///
164/// ```rust,ignore
165/// #[sails_rs::event]
166/// #[derive(sails_rs::Encode, sails_rs::TypeInfo)]
167/// #[codec(crate = sails_rs::scale_codec)]
168/// #[scale_info(crate = sails_rs::scale_info)]
169/// pub enum Events {
170/// MyEvent {
171/// #[indexed]
172/// sender: uint128,
173/// amount: uint128,
174/// note: String,
175/// },
176/// }
177/// ```
178///
179/// Calling the methods:
180///
181/// ```rust,ignore
182/// let event = Events::MyEvent {
183/// sender: 123,
184/// amount: 1000,
185/// note: "Hello, Ethereum".to_owned(),
186/// };
187///
188/// let topics = event.topics();
189/// let data = event.data();
190/// ```
191///
192/// The first topic will be the hash of the event signature (e.g. `"MyEvent(uint128,uint128,String)"`),
193/// and additional topics and the data payload will be computed based on the field attributes.
194///
195/// # Methods
196///
197/// - `topics()`: Returns a vector of 32-byte topics (`alloy_primitives::B256`) for the event.
198/// - `data()`: Returns the ABI-encoded data payload (a `Vec<u8>`) for the non-indexed fields.
199#[proc_macro_error]
200#[proc_macro_attribute]
201pub fn event(args: TokenStream, input: TokenStream) -> TokenStream {
202 sails_macros_core::event(args.into(), input.into()).into()
203}