Skip to main content

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}