swf_runtime/handler.rs
1use crate::error::WorkflowResult;
2use serde_json::Value;
3use std::sync::Arc;
4
5/// Handler for call task types that require custom implementations.
6///
7/// Implement this trait to provide support for call types like gRPC, OpenAPI,
8/// AsyncAPI, and A2A. Register handlers with `WorkflowRunner::with_call_handler()`.
9///
10/// # Example
11///
12/// ```no_run
13/// use async_trait::async_trait;
14/// use serde_json::Value;
15/// use swf_runtime::{CallHandler, WorkflowResult};
16///
17/// struct GrpcCallHandler;
18///
19/// #[async_trait]
20/// impl CallHandler for GrpcCallHandler {
21/// fn call_type(&self) -> &str { "grpc" }
22///
23/// async fn handle(
24/// &self,
25/// task_name: &str,
26/// call_config: &Value,
27/// input: &Value,
28/// ) -> WorkflowResult<Value> {
29/// // Implement gRPC call logic here
30/// Ok(serde_json::json!({ "result": "grpc response" }))
31/// }
32/// }
33/// ```
34#[async_trait::async_trait]
35pub trait CallHandler: Send + Sync {
36 /// Returns the call type this handler supports (e.g., "grpc", "openapi", "asyncapi", "a2a")
37 fn call_type(&self) -> &str;
38
39 /// Executes the call with the given configuration and input.
40 async fn handle(
41 &self,
42 task_name: &str,
43 call_config: &Value,
44 input: &Value,
45 ) -> WorkflowResult<Value>;
46}
47
48/// Handler for run task types that require custom implementations.
49///
50/// Implement this trait to provide support for run types like container and script.
51/// Register handlers with `WorkflowRunner::with_run_handler()`.
52///
53/// # Example
54///
55/// ```no_run
56/// use async_trait::async_trait;
57/// use serde_json::Value;
58/// use swf_runtime::{RunHandler, WorkflowResult};
59///
60/// struct ContainerRunHandler;
61///
62/// #[async_trait]
63/// impl RunHandler for ContainerRunHandler {
64/// fn run_type(&self) -> &str { "container" }
65///
66/// async fn handle(
67/// &self,
68/// task_name: &str,
69/// run_config: &Value,
70/// input: &Value,
71/// ) -> WorkflowResult<Value> {
72/// // Implement container run logic here
73/// Ok(serde_json::json!({ "exitCode": 0 }))
74/// }
75/// }
76/// ```
77#[async_trait::async_trait]
78pub trait RunHandler: Send + Sync {
79 /// Returns the run type this handler supports (e.g., "container", "script")
80 fn run_type(&self) -> &str;
81
82 /// Executes the run with the given configuration and input.
83 async fn handle(
84 &self,
85 task_name: &str,
86 run_config: &Value,
87 input: &Value,
88 ) -> WorkflowResult<Value>;
89}
90
91/// Handler for custom/extension task types.
92///
93/// Implement this trait to provide support for custom task types that are
94/// not part of the built-in Serverless Workflow specification.
95/// Register handlers with `WorkflowRunner::with_custom_task_handler()`.
96///
97/// # Example
98///
99/// ```no_run
100/// use async_trait::async_trait;
101/// use serde_json::Value;
102/// use swf_runtime::{CustomTaskHandler, WorkflowResult};
103///
104/// struct UppercaseHandler;
105///
106/// #[async_trait]
107/// impl CustomTaskHandler for UppercaseHandler {
108/// fn task_type(&self) -> &str { "uppercase" }
109///
110/// async fn handle(
111/// &self,
112/// task_name: &str,
113/// task_type: &str,
114/// task_config: &Value,
115/// input: &Value,
116/// ) -> WorkflowResult<Value> {
117/// let text = input["text"].as_str().unwrap_or("");
118/// Ok(serde_json::json!({ "result": text.to_uppercase() }))
119/// }
120/// }
121/// ```
122#[async_trait::async_trait]
123pub trait CustomTaskHandler: Send + Sync {
124 /// Returns the custom task type this handler supports (e.g., "myCustomTask")
125 fn task_type(&self) -> &str;
126
127 /// Executes the custom task with the given configuration and input.
128 async fn handle(
129 &self,
130 task_name: &str,
131 task_type: &str,
132 task_config: &Value,
133 input: &Value,
134 ) -> WorkflowResult<Value>;
135}
136
137/// Registry of call, run, and custom task handlers.
138/// Uses Arc for cheap cloning — handlers are shared across workflow context propagation.
139#[derive(Default, Clone)]
140pub struct HandlerRegistry {
141 call_handlers:
142 std::sync::Arc<std::collections::HashMap<String, std::sync::Arc<dyn CallHandler>>>,
143 run_handlers: std::sync::Arc<std::collections::HashMap<String, std::sync::Arc<dyn RunHandler>>>,
144 custom_task_handlers:
145 std::sync::Arc<std::collections::HashMap<String, std::sync::Arc<dyn CustomTaskHandler>>>,
146}
147
148impl HandlerRegistry {
149 /// Creates a new empty handler registry
150 pub fn new() -> Self {
151 Self::default()
152 }
153
154 /// Registers a call handler
155 pub fn register_call_handler(&mut self, handler: Box<dyn CallHandler>) {
156 let key = handler.call_type().to_string();
157 Arc::make_mut(&mut self.call_handlers).insert(key, std::sync::Arc::from(handler));
158 }
159
160 /// Registers a run handler
161 pub fn register_run_handler(&mut self, handler: Box<dyn RunHandler>) {
162 let key = handler.run_type().to_string();
163 Arc::make_mut(&mut self.run_handlers).insert(key, std::sync::Arc::from(handler));
164 }
165
166 /// Registers a custom task handler
167 pub fn register_custom_task_handler(&mut self, handler: Box<dyn CustomTaskHandler>) {
168 let key = handler.task_type().to_string();
169 Arc::make_mut(&mut self.custom_task_handlers).insert(key, std::sync::Arc::from(handler));
170 }
171
172 /// Looks up a call handler by type
173 pub fn get_call_handler(&self, call_type: &str) -> Option<std::sync::Arc<dyn CallHandler>> {
174 self.call_handlers.get(call_type).cloned()
175 }
176
177 /// Looks up a run handler by type
178 pub fn get_run_handler(&self, run_type: &str) -> Option<std::sync::Arc<dyn RunHandler>> {
179 self.run_handlers.get(run_type).cloned()
180 }
181
182 /// Looks up a custom task handler by task type
183 pub fn get_custom_task_handler(
184 &self,
185 task_type: &str,
186 ) -> Option<std::sync::Arc<dyn CustomTaskHandler>> {
187 self.custom_task_handlers.get(task_type).cloned()
188 }
189}