@@ -195,3 +195,129 @@ pub struct NewChallengeSeed {
195195#[ActorEventBus (" blockchain_service" )]
196196pub struct BlockchainServiceEventBusProvider ;
197197```
198+
199+ # StorageHub Actors Command Macros
200+
201+ This crate provides procedural macros to simplify actor command boilerplate code in the StorageHub actors framework.
202+
203+ ## Features
204+
205+ - ` actor_command ` attribute macro: Automatically enhances command enums with callbacks and generates the Interface trait.
206+ - ` command ` attribute macro: Specifies behavior for individual command variants.
207+
208+ ## Usage
209+
210+ ### Basic Command Definition
211+
212+ ``` rust
213+ #[actor_command(
214+ service = FileTransferService ,
215+ default_mode = " SyncAwait" ,
216+ default_error_type = RequestError
217+ )]
218+ pub enum FileTransferServiceCommand {
219+ #[command(mode = " AsyncAwait" , success_type = (Vec <u8 >, ProtocolName ), error_type = RequestFailure )]
220+ UploadRequest {
221+ peer_id : PeerId ,
222+ file_key : FileKey ,
223+ file_key_proof : FileKeyProof ,
224+ bucket_id : Option <BucketId >,
225+ },
226+
227+ UploadResponse {
228+ request_id : UploadRequestId ,
229+ file_complete : bool ,
230+ },
231+
232+ // Other commands...
233+ }
234+ ```
235+
236+ ### Command Modes
237+
238+ The macro supports three command modes:
239+
240+ 1 . ** FireAndForget** : No response is expected
241+ 2 . ** SyncAwait** : Wait for a direct response from the actor
242+ 3 . ** AsyncAwait** : Wait for an asynchronous response (e.g., from a network operation)
243+
244+ ### Extension Traits
245+
246+ You can define extension traits in addition to the automatically generated Interface trait:
247+
248+ ``` rust
249+ #[async_trait:: async_trait]
250+ pub trait FileTransferServiceInterfaceExt {
251+ fn parse_remote_upload_data_response (
252+ & self ,
253+ data : Vec <u8 >,
254+ ) -> Result <schema :: v1 :: provider :: RemoteUploadDataResponse , RequestError >;
255+
256+ async fn extract_peer_ids_and_register_known_addresses (
257+ & self ,
258+ multiaddresses : Vec <Multiaddr >,
259+ ) -> Vec <PeerId >;
260+ }
261+
262+ #[async_trait:: async_trait]
263+ impl FileTransferServiceInterfaceExt for ActorHandle <FileTransferService > {
264+ // Implementations...
265+ }
266+ ```
267+
268+ ## Attribute Parameters
269+
270+ ### ` actor_command ` Parameters
271+
272+ - ` service ` : (Required) The service type that processes these commands
273+ - ` default_mode ` : (Optional) Default command mode, one of: "FireAndForget", "SyncAwait", "AsyncAwait"
274+ - ` default_error_type ` : (Optional) Default error type for command responses
275+ - ` default_inner_channel_type ` : (Optional) Default channel type for AsyncAwait mode
276+
277+ ### ` command ` Parameters
278+
279+ - ` mode ` : (Optional) Override the default command mode
280+ - ` success_type ` : (Optional) The success type returned in the Result
281+ - ` error_type ` : (Optional) Override the default error type
282+ - ` inner_channel_type ` : (Optional) Override the default channel type for AsyncAwait mode
283+
284+ ## Generated Code
285+
286+ The macro automatically:
287+
288+ 1 . Adds a ` callback ` field to each command variant based on the mode
289+ 2 . Generates a trait with a method for each command
290+ 3 . Implements the trait for ActorHandle<ServiceType >
291+
292+ This eliminates boilerplate code and ensures consistent error handling.
293+
294+ ## Real World Example
295+
296+ Here's an example from the StorageHub codebase:
297+
298+ ``` rust
299+ #[actor_command(
300+ service = BlockchainService <FSH : ForestStorageHandler + Clone + Send + Sync + 'static >,
301+ default_mode = " SyncAwait" ,
302+ default_inner_channel_type = tokio:: sync:: oneshot:: Receiver ,
303+ )]
304+ pub enum BlockchainServiceCommand {
305+ #[command(success_type = SubmittedTransaction )]
306+ SendExtrinsic {
307+ call : storage_hub_runtime :: RuntimeCall ,
308+ options : SendExtrinsicOptions ,
309+ },
310+ #[command(success_type = Extrinsic )]
311+ GetExtrinsicFromBlock {
312+ block_hash : H256 ,
313+ extrinsic_hash : H256 ,
314+ },
315+ #[command(mode = " AsyncAwait" , inner_channel_type = tokio:: sync:: oneshot:: Receiver )]
316+ WaitForBlock {
317+ block_number : BlockNumber ,
318+ },
319+ // ... more commands
320+ }
321+ ```
322+
323+ This generates a trait ` BlockchainServiceCommandInterface ` with methods like ` send_extrinsic ` , ` get_extrinsic_from_block ` , etc., which can be called on an ` ActorHandle<BlockchainService> ` .
0 commit comments