actr_framework/workload.rs
1//! Workload trait - Executable actor workload
2
3use actr_protocol::{ActorResult, ActrType};
4use async_trait::async_trait;
5
6use crate::{Context, MessageDispatcher};
7
8/// Workload - Executable Actor workload
9///
10/// Represents a complete Actor instance, including:
11/// - Associated dispatcher type (Dispatcher)
12/// - Actor type identifier (actor_type)
13/// - Lifecycle hooks (on_start, on_stop)
14///
15/// # Design Characteristics
16///
17/// - **Bidirectional association**: `Workload::Dispatcher` and `MessageDispatcher::Workload` reference each other
18/// - **Default implementations**: Lifecycle hooks have default no-op implementations, users can optionally override
19/// - **Auto-implementation**: Implemented for wrapper types by code generator
20///
21/// # Code Generation Example
22///
23/// ```rust,ignore
24/// // User-implemented Handler
25/// pub struct MyEchoService { /* ... */ }
26///
27/// impl EchoServiceHandler for MyEchoService {
28/// async fn echo<C: Context>(
29/// &self,
30/// req: EchoRequest,
31/// ctx: &C,
32/// ) -> ActorResult<EchoResponse> {
33/// // Business logic
34/// Ok(EchoResponse { reply: format!("Echo: {}", req.message) })
35/// }
36/// }
37///
38/// // Code-generated Workload wrapper
39/// pub struct EchoServiceWorkload<T: EchoServiceHandler>(pub T);
40///
41/// impl<T: EchoServiceHandler> Workload for EchoServiceWorkload<T> {
42/// type Dispatcher = EchoServiceRouter<T>;
43///
44/// fn actor_type(&self) -> ActrType {
45/// ActrType {
46/// manufacturer: "acme".to_string(),
47/// name: "echo.EchoService".to_string(),
48/// }
49/// }
50/// }
51/// ```
52#[async_trait]
53pub trait Workload: Send + Sync + 'static {
54 /// Associated dispatcher type
55 type Dispatcher: MessageDispatcher<Workload = Self>;
56
57 /// Get Actor type identifier
58 ///
59 /// Used for service discovery and routing.
60 fn actor_type(&self) -> ActrType;
61
62 /// Lifecycle hook: Called when Actor starts
63 ///
64 /// # Default Implementation
65 ///
66 /// Default is a no-op, users can optionally override to perform initialization logic.
67 ///
68 /// # Example
69 ///
70 /// ```rust,ignore
71 /// async fn on_start<C: Context>(&self, ctx: &C) -> ActorResult<()> {
72 /// tracing::info!("Actor {} started", ctx.self_id());
73 /// // Initialize resources
74 /// Ok(())
75 /// }
76 /// ```
77 async fn on_start<C: Context>(&self, _ctx: &C) -> ActorResult<()> {
78 Ok(())
79 }
80
81 /// Lifecycle hook: Called when Actor stops
82 ///
83 /// # Default Implementation
84 ///
85 /// Default is a no-op, users can optionally override to perform cleanup logic.
86 ///
87 /// # Example
88 ///
89 /// ```rust,ignore
90 /// async fn on_stop<C: Context>(&self, ctx: &C) -> ActorResult<()> {
91 /// tracing::info!("Actor {} stopping", ctx.self_id());
92 /// // Cleanup resources
93 /// Ok(())
94 /// }
95 /// ```
96 async fn on_stop<C: Context>(&self, _ctx: &C) -> ActorResult<()> {
97 Ok(())
98 }
99}