diff --git a/module-system/sov-modules-macros/Cargo.toml b/module-system/sov-modules-macros/Cargo.toml index aee6d7ff62..1f604e19ac 100644 --- a/module-system/sov-modules-macros/Cargo.toml +++ b/module-system/sov-modules-macros/Cargo.toml @@ -20,11 +20,14 @@ name = "tests" path = "tests/all_tests.rs" [dev-dependencies] +serde_json = "1" +jsonrpsee = { workspace = true, features = ["macros", "http-client", "server"] } +tempfile = { workspace = true } trybuild = "1.0" + sov-modules-api = { path = "../sov-modules-api", version = "0.1", default-features = false } -jsonrpsee = { workspace = true, features = ["macros", "http-client", "server"] } sov-state = { path = "../sov-state", version = "0.1", default-features = false } -tempfile = { workspace = true } +sov-bank = { path = "../module-implementations/sov-bank", version = "0.1" } [dependencies] anyhow = { workspace = true } diff --git a/module-system/sov-modules-macros/src/lib.rs b/module-system/sov-modules-macros/src/lib.rs index 4b4018abd0..df448a1818 100644 --- a/module-system/sov-modules-macros/src/lib.rs +++ b/module-system/sov-modules-macros/src/lib.rs @@ -1,4 +1,8 @@ +//! Procedural macros to assist in the creation of Sovereign modules. + +#![deny(missing_docs)] #![feature(log_syntax)] + mod cli_parser; mod common; mod default_runtime; @@ -17,31 +21,44 @@ use proc_macro::TokenStream; use rpc::ExposeRpcMacro; use syn::parse_macro_input; -/// Derives the `sov-modules-api::ModuleInfo` implementation for the underlying type. +/// Derives the [`sov_modules_api::ModuleInfo`] trait for the underlying `struct`. +/// +/// The underlying type must respect the following conditions, or compilation +/// will fail: +/// - It must be a named `struct`. Tuple `struct`s, `enum`s, and others are +/// not supported. +/// - It must have *exactly one* field with the `#[address]` attribute. This field +/// represents the **module address**. +/// - All other fields must have either the `#[state]` or `#[module]` attribute. +/// - `#[state]` is used for state members. +/// - `#[module]` is used for module members. +/// +/// In addition to implementing [`sov_modules_api::ModuleInfo`], this macro will +/// also generate so-called "prefix" methods. See the [`sov_modules_api`] docs +/// for more information about prefix methods. /// -/// See `sov-modules-api` for definition of `prefix`. /// ## Example /// -/// ``` ignore -/// #[derive(ModuleInfo)] -/// pub(crate) struct TestModule { -/// #[state] -/// pub test_state1: TestState, +/// ``` +/// use sov_modules_macros::ModuleInfo; +/// use sov_modules_api::{Context, ModuleInfo}; +/// use sov_state::StateMap; +/// +/// #[derive(ModuleInfo)] +/// struct TestModule { +/// #[address] +/// admin: C::Address, /// /// #[state] -/// pub test_state2: TestState, -/// } +/// pub state_map: StateMap, +/// } +/// +/// // You can then get the prefix of `state_map` like this: +/// fn get_prefix(some_storage: C::Storage) { +/// let test_struct = TestModule::::default(); +/// let prefix1 = test_struct.state_map.prefix(); +/// } /// ``` -/// allows getting a prefix of a member field like: -/// ```ignore -/// let test_struct = as sov_modules_api::ModuleInfo>::new(some_storage); -/// let prefix1 = test_struct.test_state1.prefix; -/// ```` -/// ## Attributes -/// -/// * `state` - attribute for state members -/// * `module` - attribute for module members -/// * `address` - attribute for module address #[proc_macro_derive(ModuleInfo, attributes(state, module, address))] pub fn module_info(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input); @@ -61,7 +78,8 @@ pub fn default_runtime(input: TokenStream) -> TokenStream { handle_macro_error(default_config_macro.derive_default_runtime(input)) } -/// Derives the `sov-modules-api::Genesis` implementation for the underlying type. +/// Derives the [`sov_modules_api::Genesis`] trait for the underlying runtime +/// `struct`. #[proc_macro_derive(Genesis)] pub fn genesis(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input); @@ -70,7 +88,7 @@ pub fn genesis(input: TokenStream) -> TokenStream { handle_macro_error(genesis_macro.derive_genesis(input)) } -/// Derives the `sov-modules-api::DispatchCall` implementation for the underlying type. +/// Derives the [`sov_modules_api::DispatchCall`] trait for the underlying type. #[proc_macro_derive(DispatchCall, attributes(serialization))] pub fn dispatch_call(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input); @@ -90,8 +108,9 @@ pub fn dispatch_call(input: TokenStream) -> TokenStream { /// use sov_modules_api::default_context::ZkDefaultContext; /// use sov_modules_macros::{ModuleInfo, ModuleCallJsonSchema}; /// use sov_state::StateMap; +/// use sov_bank::call::CallMessage; /// -/// #[derive(ModuleInfo, ModuleCallJsonSchema, schemars::JsonSchema)] +/// #[derive(ModuleInfo, ModuleCallJsonSchema)] /// struct TestModule { /// #[address] /// admin: C::Address, @@ -103,6 +122,7 @@ pub fn dispatch_call(input: TokenStream) -> TokenStream { /// impl Module for TestModule { /// type Context = C; /// type Config = PhantomData; +/// type CallMessage = CallMessage; /// } /// /// println!("JSON Schema: {}", TestModule::::json_schema()); @@ -122,7 +142,7 @@ pub fn codec(input: TokenStream) -> TokenStream { handle_macro_error(codec_macro.derive_message_codec(input)) } -/// Derive a `jsonrpsee` implementation for the underlying type. Any code relying on this macro +/// Derives a [`jsonrpsee`] implementation for the underlying type. Any code relying on this macro /// must take jsonrpsee as a dependency with at least the following features enabled: `["macros", "client-core", "server"]`. /// /// Syntax is identical to `jsonrpsee`'s `#[rpc]` execept that: @@ -131,42 +151,58 @@ pub fn codec(input: TokenStream) -> TokenStream { /// 3. `#[method]` is renamed to with `#[rpc_method]` to avoid import confusion and clarify the purpose of the annotation /// /// ## Example -/// ```rust,ignore -/// struct MyModule {}; +/// ``` +/// use sov_modules_macros::{rpc_gen, ModuleInfo}; +/// use sov_modules_api::Context; +/// +/// #[derive(ModuleInfo)] +/// struct MyModule { +/// #[address] +/// addr: C::Address, +/// // ... +/// } /// -/// #[rpc_gen(client, server, namespace ="myNamespace")] -/// impl MyModule { -/// #[rpc_method(name = "myMethod")] +/// #[rpc_gen(client, server, namespace = "myNamespace")] +/// impl MyModule { +/// #[rpc_method(name = "myMethod")] /// fn my_method(&self, param: u32) -> u32 { -/// 1 +/// 1 /// } /// } /// ``` /// /// This is exactly equivalent to hand-writing -/// ```rust,ignore +/// +/// ``` +/// use sov_modules_macros::{rpc_gen, ModuleInfo}; +/// use sov_modules_api::Context; +/// use sov_state::WorkingSet; +/// +/// #[derive(ModuleInfo)] /// struct MyModule { -/// ... +/// #[address] +/// addr: C::Address, +/// // ... /// }; /// -/// impl MyModule { +/// impl MyModule { /// fn my_method(&self, working_set: &mut WorkingSet, param: u32) -> u32 { /// 1 /// } /// } /// -/// #[jsonrpsee::rpc(client, server, namespace ="myNamespace")] +/// #[jsonrpsee::proc_macros::rpc(client, server, namespace ="myNamespace")] /// pub trait MyModuleRpc { -/// #[jsonrpsee::method(name = "myMethod")] -/// fn my_method(&self, param: u32) -> Result; -/// #[method(name = "health")] -/// fn health() -> Result<(), jsonrpsee::Error> { -/// Ok(()) -/// } +/// #[method(name = "myMethod")] +/// fn my_method(&self, param: u32) -> Result; +/// +/// #[method(name = "health")] +/// fn health(&self) -> Result<(), jsonrpsee::core::Error> { +/// Ok(()) +/// } /// } /// ``` /// -/// /// This proc macro also generates an implementation trait intended to be used by a Runtime struct. This trait /// is named `MyModuleRpcImpl`, and allows a Runtime to be converted into a functional RPC server /// by simply implementing the two required methods - `get_backing_impl(&self) -> MyModule` and `get_working_set(&self) -> ::sov_modules_api::WorkingSet` @@ -205,6 +241,22 @@ pub fn expose_rpc(attr: TokenStream, input: TokenStream) -> TokenStream { handle_macro_error(expose_macro.generate_rpc(original, input, context_type)) } +/// Generates a CLI arguments parser for the specified runtime. +/// +/// ## Examples +/// ``` +/// use sov_modules_api::Context; +/// use sov_modules_api::default_context::DefaultContext; +/// use sov_modules_macros::{DispatchCall, MessageCodec, cli_parser}; +/// +/// #[derive(DispatchCall, MessageCodec)] +/// #[serialization(borsh::BorshDeserialize, borsh::BorshSerialize)] +/// #[cli_parser(DefaultContext)] +/// pub struct Runtime { +/// pub bank: sov_bank::Bank, +/// // ... +/// } +/// ``` #[proc_macro_attribute] pub fn cli_parser(attr: TokenStream, input: TokenStream) -> TokenStream { let context_type = parse_macro_input!(attr);