swf_runtime/handler.rs
1use crate::error::WorkflowResult;
2use serde_json::Value;
3use std::sync::Arc;
4
5/// Read-only snapshot of workflow context variables available to task handlers.
6///
7/// Provides access to `$context`, `$secret`, `$workflow`, and other runtime variables
8/// that were previously inaccessible from custom handlers.
9///
10/// # Example
11///
12/// ```no_run
13/// use async_trait::async_trait;
14/// use serde_json::Value;
15/// use swf_runtime::{CustomTaskHandler, HandlerContext, WorkflowResult};
16///
17/// struct SmartHandler;
18///
19/// #[async_trait]
20/// impl CustomTaskHandler for SmartHandler {
21/// fn task_type(&self) -> &str { "smart" }
22///
23/// async fn handle(
24/// &self,
25/// task_name: &str,
26/// task_type: &str,
27/// task_config: &Value,
28/// input: &Value,
29/// context: &HandlerContext,
30/// ) -> WorkflowResult<Value> {
31/// // Access $context to read workflow state
32/// let preferred = context.context().get("provider").and_then(|v| v.as_str());
33/// // Access $secret for credentials
34/// let api_key = context.secret().and_then(|s| s.get("API_KEY")).and_then(|v| v.as_str());
35/// Ok(input.clone())
36/// }
37/// }
38/// ```
39#[derive(Debug, Clone)]
40pub struct HandlerContext {
41 context: Value,
42 secret: Option<Value>,
43 workflow: Value,
44 authorization: Option<Value>,
45}
46
47impl HandlerContext {
48 /// Creates a new HandlerContext from the current workflow context variables
49 pub(crate) fn from_vars(vars: &std::collections::HashMap<String, Value>) -> Self {
50 Self {
51 context: vars
52 .get(crate::context::vars::CONTEXT)
53 .cloned()
54 .unwrap_or(Value::Null),
55 secret: vars.get(crate::context::vars::SECRET).cloned(),
56 workflow: vars
57 .get(crate::context::vars::WORKFLOW)
58 .cloned()
59 .unwrap_or(Value::Null),
60 authorization: vars.get(crate::context::vars::AUTHORIZATION).cloned(),
61 }
62 }
63
64 /// Returns the `$context` value (workflow instance state set by `export.as`)
65 pub fn context(&self) -> &Value {
66 &self.context
67 }
68
69 /// Returns the `$secret` value (all resolved secrets), if a secret manager is configured
70 pub fn secret(&self) -> Option<&Value> {
71 self.secret.as_ref()
72 }
73
74 /// Returns the `$workflow` descriptor (workflow metadata)
75 pub fn workflow(&self) -> &Value {
76 &self.workflow
77 }
78
79 /// Returns the `$authorization` value (set after HTTP authentication), if any
80 pub fn authorization(&self) -> Option<&Value> {
81 self.authorization.as_ref()
82 }
83}
84
85/// Handler for call task types that require custom implementations.
86///
87/// Implement this trait to provide support for call types like gRPC, OpenAPI,
88/// AsyncAPI, and A2A. Register handlers with `WorkflowRunner::with_call_handler()`.
89///
90/// # Example
91///
92/// ```no_run
93/// use async_trait::async_trait;
94/// use serde_json::Value;
95/// use swf_runtime::{CallHandler, HandlerContext, WorkflowResult};
96///
97/// struct GrpcCallHandler;
98///
99/// #[async_trait]
100/// impl CallHandler for GrpcCallHandler {
101/// fn call_type(&self) -> &str { "grpc" }
102///
103/// async fn handle(
104/// &self,
105/// task_name: &str,
106/// call_config: &Value,
107/// input: &Value,
108/// context: &HandlerContext,
109/// ) -> WorkflowResult<Value> {
110/// // Implement gRPC call logic here
111/// Ok(serde_json::json!({ "result": "grpc response" }))
112/// }
113/// }
114/// ```
115#[async_trait::async_trait]
116pub trait CallHandler: Send + Sync {
117 /// Returns the call type this handler supports (e.g., "grpc", "openapi", "asyncapi", "a2a")
118 fn call_type(&self) -> &str;
119
120 /// Executes the call with the given configuration, input, and workflow context.
121 async fn handle(
122 &self,
123 task_name: &str,
124 call_config: &Value,
125 input: &Value,
126 context: &HandlerContext,
127 ) -> WorkflowResult<Value>;
128}
129
130/// Handler for run task types that require custom implementations.
131///
132/// Implement this trait to provide support for run types like container and script.
133/// Register handlers with `WorkflowRunner::with_run_handler()`.
134///
135/// # Example
136///
137/// ```no_run
138/// use async_trait::async_trait;
139/// use serde_json::Value;
140/// use swf_runtime::{RunHandler, HandlerContext, WorkflowResult};
141///
142/// struct ContainerRunHandler;
143///
144/// #[async_trait]
145/// impl RunHandler for ContainerRunHandler {
146/// fn run_type(&self) -> &str { "container" }
147///
148/// async fn handle(
149/// &self,
150/// task_name: &str,
151/// run_config: &Value,
152/// input: &Value,
153/// context: &HandlerContext,
154/// ) -> WorkflowResult<Value> {
155/// // Implement container run logic here
156/// Ok(serde_json::json!({ "exitCode": 0 }))
157/// }
158/// }
159/// ```
160#[async_trait::async_trait]
161pub trait RunHandler: Send + Sync {
162 /// Returns the run type this handler supports (e.g., "container", "script")
163 fn run_type(&self) -> &str;
164
165 /// Executes the run with the given configuration, input, and workflow context.
166 async fn handle(
167 &self,
168 task_name: &str,
169 run_config: &Value,
170 input: &Value,
171 context: &HandlerContext,
172 ) -> WorkflowResult<Value>;
173}
174
175/// Handler for custom/extension task types.
176///
177/// Implement this trait to provide support for custom task types that are
178/// not part of the built-in Serverless Workflow specification.
179/// Register handlers with `WorkflowRunner::with_custom_task_handler()`.
180///
181/// # Example
182///
183/// ```no_run
184/// use async_trait::async_trait;
185/// use serde_json::Value;
186/// use swf_runtime::{CustomTaskHandler, HandlerContext, WorkflowResult};
187///
188/// struct UppercaseHandler;
189///
190/// #[async_trait]
191/// impl CustomTaskHandler for UppercaseHandler {
192/// fn task_type(&self) -> &str { "uppercase" }
193///
194/// async fn handle(
195/// &self,
196/// task_name: &str,
197/// task_type: &str,
198/// task_config: &Value,
199/// input: &Value,
200/// context: &HandlerContext,
201/// ) -> WorkflowResult<Value> {
202/// let text = input["text"].as_str().unwrap_or("");
203/// Ok(serde_json::json!({ "result": text.to_uppercase() }))
204/// }
205/// }
206/// ```
207#[async_trait::async_trait]
208pub trait CustomTaskHandler: Send + Sync {
209 /// Returns the custom task type this handler supports (e.g., "myCustomTask")
210 fn task_type(&self) -> &str;
211
212 /// Executes the custom task with the given configuration, input, and workflow context.
213 async fn handle(
214 &self,
215 task_name: &str,
216 task_type: &str,
217 task_config: &Value,
218 input: &Value,
219 context: &HandlerContext,
220 ) -> WorkflowResult<Value>;
221}
222
223/// Registry of call, run, and custom task handlers.
224/// Uses Arc for cheap cloning — handlers are shared across workflow context propagation.
225#[derive(Default, Clone)]
226pub struct HandlerRegistry {
227 call_handlers:
228 std::sync::Arc<std::collections::HashMap<String, std::sync::Arc<dyn CallHandler>>>,
229 run_handlers: std::sync::Arc<std::collections::HashMap<String, std::sync::Arc<dyn RunHandler>>>,
230 custom_task_handlers:
231 std::sync::Arc<std::collections::HashMap<String, std::sync::Arc<dyn CustomTaskHandler>>>,
232}
233
234impl HandlerRegistry {
235 /// Creates a new empty handler registry
236 pub fn new() -> Self {
237 Self::default()
238 }
239
240 /// Registers a call handler
241 pub fn register_call_handler(&mut self, handler: Box<dyn CallHandler>) {
242 let key = handler.call_type().to_string();
243 Arc::make_mut(&mut self.call_handlers).insert(key, std::sync::Arc::from(handler));
244 }
245
246 /// Registers a run handler
247 pub fn register_run_handler(&mut self, handler: Box<dyn RunHandler>) {
248 let key = handler.run_type().to_string();
249 Arc::make_mut(&mut self.run_handlers).insert(key, std::sync::Arc::from(handler));
250 }
251
252 /// Registers a custom task handler
253 pub fn register_custom_task_handler(&mut self, handler: Box<dyn CustomTaskHandler>) {
254 let key = handler.task_type().to_string();
255 Arc::make_mut(&mut self.custom_task_handlers).insert(key, std::sync::Arc::from(handler));
256 }
257
258 /// Looks up a call handler by type
259 pub fn get_call_handler(&self, call_type: &str) -> Option<std::sync::Arc<dyn CallHandler>> {
260 self.call_handlers.get(call_type).cloned()
261 }
262
263 /// Looks up a run handler by type
264 pub fn get_run_handler(&self, run_type: &str) -> Option<std::sync::Arc<dyn RunHandler>> {
265 self.run_handlers.get(run_type).cloned()
266 }
267
268 /// Looks up a custom task handler by task type
269 pub fn get_custom_task_handler(
270 &self,
271 task_type: &str,
272 ) -> Option<std::sync::Arc<dyn CustomTaskHandler>> {
273 self.custom_task_handlers.get(task_type).cloned()
274 }
275}