kotoba_workflow/
spec.rs

1//! Serverless Workflow Specification Implementation
2//!
3//! This module implements the Serverless Workflow specification (https://serverlessworkflow.io/)
4//! providing JSON/YAML-based workflow definitions compliant with the SW DSL.
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9
10/// Serverless Workflow Document
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct WorkflowDocument {
13    /// DSL version (e.g., "1.0.0")
14    pub dsl: String,
15    /// Workflow namespace
16    pub namespace: String,
17    /// Workflow name
18    pub name: String,
19    /// Workflow version
20    pub version: String,
21    /// Optional title
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub title: Option<String>,
24    /// Optional summary
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub summary: Option<String>,
27    /// Optional description
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub description: Option<String>,
30}
31
32/// Serverless Workflow definition
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct ServerlessWorkflow {
35    /// Workflow metadata
36    pub document: WorkflowDocument,
37    /// Input schema (optional)
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub input: Option<WorkflowInput>,
40    /// Workflow steps
41    #[serde(rename = "do")]
42    pub r#do: Vec<WorkflowStep>,
43    /// Timeouts (optional)
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub timeouts: Option<WorkflowTimeouts>,
46    /// Events (optional)
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub events: Option<Vec<EventDefinition>>,
49    /// Functions (optional)
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub functions: Option<Vec<FunctionDefinition>>,
52}
53
54/// Workflow input schema
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct WorkflowInput {
57    /// Input schema definition
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub schema: Option<Value>,
60}
61
62/// Workflow timeouts
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct WorkflowTimeouts {
65    /// Workflow execution timeout
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub workflow: Option<TimeoutDefinition>,
68    /// State execution timeout
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub state: Option<TimeoutDefinition>,
71    /// Action execution timeout
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub action: Option<TimeoutDefinition>,
74}
75
76/// Timeout definition
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct TimeoutDefinition {
79    /// Duration in seconds
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub seconds: Option<u64>,
82    /// ISO 8601 duration string
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub duration: Option<String>,
85}
86
87/// Event definition
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct EventDefinition {
90    /// Event name
91    pub name: String,
92    /// Event source
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub source: Option<String>,
95    /// Event type
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub r#type: Option<String>,
98    /// Event data schema
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub data: Option<Value>,
101}
102
103/// Function definition
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct FunctionDefinition {
106    /// Function name
107    pub name: String,
108    /// Function type
109    #[serde(rename = "type")]
110    pub function_type: String,
111    /// Function operation
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub operation: Option<String>,
114}
115
116/// Workflow step definition
117#[derive(Debug, Clone, Serialize, Deserialize)]
118#[serde(untagged)]
119pub enum WorkflowStep {
120    /// Call step (HTTP, gRPC, OpenAPI, etc.)
121    Call {
122        /// Step name
123        #[serde(skip)]
124        name: String,
125        /// Call definition
126        call: CallDefinition,
127        /// Optional output mapping
128        #[serde(skip_serializing_if = "Option::is_none")]
129        output: Option<String>,
130    },
131    /// Emit event step
132    Emit {
133        /// Step name
134        #[serde(skip)]
135        name: String,
136        /// Emit definition
137        emit: EmitDefinition,
138    },
139    /// Listen for events step
140    Listen {
141        /// Step name
142        #[serde(skip)]
143        name: String,
144        /// Listen definition
145        listen: ListenDefinition,
146        /// Optional output mapping
147        #[serde(skip_serializing_if = "Option::is_none")]
148        output: Option<String>,
149    },
150    /// Wait step
151    Wait {
152        /// Step name
153        #[serde(skip)]
154        name: String,
155        /// Wait definition
156        wait: WaitDefinition,
157    },
158    /// Run container/script step
159    Run {
160        /// Step name
161        #[serde(skip)]
162        name: String,
163        /// Run definition
164        run: RunDefinition,
165        /// Optional output mapping
166        #[serde(skip_serializing_if = "Option::is_none")]
167        output: Option<String>,
168    },
169    /// Switch/decision step
170    Switch {
171        /// Step name
172        #[serde(skip)]
173        name: String,
174        /// Switch definition
175        switch: Vec<SwitchCase>,
176    },
177    /// For loop step
178    For {
179        /// Step name
180        #[serde(skip)]
181        name: String,
182        /// For definition
183        r#for: ForDefinition,
184    },
185    /// Fork parallel execution step
186    Fork {
187        /// Step name
188        #[serde(skip)]
189        name: String,
190        /// Fork definition
191        fork: ForkDefinition,
192    },
193    /// Try-catch error handling step
194    Try {
195        /// Step name
196        #[serde(skip)]
197        name: String,
198        /// Try definition
199        r#try: TryDefinition,
200    },
201    /// Raise error step
202    Raise {
203        /// Step name
204        #[serde(skip)]
205        name: String,
206        /// Raise definition
207        raise: RaiseDefinition,
208    },
209    /// Set variable step
210    Set {
211        /// Step name
212        #[serde(skip)]
213        name: String,
214        /// Set definition
215        set: HashMap<String, Value>,
216    },
217}
218
219/// Call step definition
220#[derive(Debug, Clone, Serialize, Deserialize)]
221#[serde(untagged)]
222pub enum CallDefinition {
223    /// HTTP call
224    Http {
225        /// HTTP method
226        method: String,
227        /// Endpoint URL
228        endpoint: String,
229        /// Optional headers
230        #[serde(skip_serializing_if = "Option::is_none")]
231        headers: Option<HashMap<String, String>>,
232        /// Optional body
233        #[serde(skip_serializing_if = "Option::is_none")]
234        body: Option<Value>,
235        /// Optional authentication
236        #[serde(skip_serializing_if = "Option::is_none")]
237        auth: Option<Authentication>,
238    },
239    /// gRPC call
240    Grpc {
241        /// Proto file
242        proto: ProtoDefinition,
243        /// Service definition
244        service: ServiceDefinition,
245        /// Method name
246        method: String,
247        /// Method arguments
248        arguments: HashMap<String, Value>,
249        /// Optional authentication
250        #[serde(skip_serializing_if = "Option::is_none")]
251        auth: Option<Authentication>,
252    },
253    /// OpenAPI call
254    OpenApi {
255        /// OpenAPI document
256        document: ApiDocument,
257        /// Operation ID
258        operation_id: String,
259        /// Parameters
260        #[serde(skip_serializing_if = "Option::is_none")]
261        parameters: Option<HashMap<String, Value>>,
262        /// Request body
263        #[serde(skip_serializing_if = "Option::is_none")]
264        body: Option<Value>,
265        /// Optional authentication
266        #[serde(skip_serializing_if = "Option::is_none")]
267        auth: Option<Authentication>,
268    },
269    /// AsyncAPI call
270    AsyncApi {
271        /// AsyncAPI document
272        document: ApiDocument,
273        /// Operation reference
274        operation_ref: String,
275        /// Server name
276        server: String,
277        /// Message definition
278        message: MessageDefinition,
279        /// Optional authentication
280        #[serde(skip_serializing_if = "Option::is_none")]
281        auth: Option<Authentication>,
282    },
283}
284
285/// Authentication definition
286#[derive(Debug, Clone, Serialize, Deserialize)]
287#[serde(untagged)]
288pub enum Authentication {
289    /// Basic authentication
290    Basic {
291        /// Username
292        username: String,
293        /// Password
294        password: String,
295    },
296    /// Bearer token authentication
297    Bearer {
298        /// Token
299        token: String,
300    },
301    /// OAuth2 authentication
302    OAuth2 {
303        /// Authority URL
304        authority: String,
305        /// Grant type
306        grant_type: String,
307        /// Client ID
308        client_id: String,
309        /// Client secret
310        client_secret: String,
311        /// Scopes
312        #[serde(skip_serializing_if = "Option::is_none")]
313        scopes: Option<Vec<String>>,
314    },
315}
316
317/// Proto definition for gRPC
318#[derive(Debug, Clone, Serialize, Deserialize)]
319pub struct ProtoDefinition {
320    /// Proto file endpoint or inline content
321    pub endpoint: String,
322}
323
324/// Service definition for gRPC
325#[derive(Debug, Clone, Serialize, Deserialize)]
326pub struct ServiceDefinition {
327    /// Service name
328    pub name: String,
329    /// Service host
330    pub host: String,
331    /// Service port
332    pub port: u16,
333}
334
335/// API document definition
336#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct ApiDocument {
338    /// Document endpoint
339    pub endpoint: String,
340}
341
342/// Message definition for AsyncAPI
343#[derive(Debug, Clone, Serialize, Deserialize)]
344pub struct MessageDefinition {
345    /// Message payload
346    #[serde(skip_serializing_if = "Option::is_none")]
347    pub payload: Option<Value>,
348}
349
350/// Emit step definition
351#[derive(Debug, Clone, Serialize, Deserialize)]
352pub struct EmitDefinition {
353    /// Event definition
354    pub event: EventInstance,
355}
356
357/// Event instance
358#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct EventInstance {
360    /// Event data
361    #[serde(skip_serializing_if = "Option::is_none")]
362    pub with: Option<EventData>,
363}
364
365/// Event data
366#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct EventData {
368    /// Event source
369    #[serde(skip_serializing_if = "Option::is_none")]
370    pub source: Option<String>,
371    /// Event type
372    #[serde(skip_serializing_if = "Option::is_none")]
373    pub r#type: Option<String>,
374    /// Event data payload
375    #[serde(skip_serializing_if = "Option::is_none")]
376    pub data: Option<Value>,
377}
378
379/// Listen step definition
380#[derive(Debug, Clone, Serialize, Deserialize)]
381pub struct ListenDefinition {
382    /// Events to listen for
383    pub to: ListenTarget,
384}
385
386/// Listen target
387#[derive(Debug, Clone, Serialize, Deserialize)]
388#[serde(untagged)]
389pub enum ListenTarget {
390    /// Listen for one event
391    One {
392        /// Event filter
393        with: EventFilter,
394    },
395    /// Listen for all matching events
396    All {
397        /// Event filters
398        with: Vec<EventFilter>,
399    },
400}
401
402/// Event filter
403#[derive(Debug, Clone, Serialize, Deserialize)]
404pub struct EventFilter {
405    /// Event type
406    #[serde(skip_serializing_if = "Option::is_none")]
407    pub r#type: Option<String>,
408    /// Event source
409    #[serde(skip_serializing_if = "Option::is_none")]
410    pub source: Option<String>,
411    /// Event data filter (expression)
412    #[serde(skip_serializing_if = "Option::is_none")]
413    pub data: Option<String>,
414}
415
416/// Wait step definition
417#[derive(Debug, Clone, Serialize, Deserialize)]
418#[serde(untagged)]
419pub enum WaitDefinition {
420    /// Wait for duration
421    Duration {
422        /// Seconds to wait
423        seconds: u64,
424    },
425    /// Wait until specific time
426    Until {
427        /// Timestamp to wait until
428        timestamp: String,
429    },
430    /// Wait for event
431    Event {
432        /// Event to wait for
433        event: EventFilter,
434    },
435}
436
437/// Run step definition
438#[derive(Debug, Clone, Serialize, Deserialize)]
439#[serde(untagged)]
440pub enum RunDefinition {
441    /// Run container
442    Container {
443        /// Container definition
444        container: ContainerDefinition,
445    },
446    /// Run script
447    Script {
448        /// Script definition
449        script: ScriptDefinition,
450    },
451    /// Run workflow
452    Workflow {
453        /// Workflow definition
454        workflow: SubWorkflowDefinition,
455    },
456}
457
458/// Container definition
459#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct ContainerDefinition {
461    /// Container image
462    pub image: String,
463    /// Command to run
464    #[serde(skip_serializing_if = "Option::is_none")]
465    pub command: Option<Vec<String>>,
466    /// Environment variables
467    #[serde(skip_serializing_if = "Option::is_none")]
468    pub env: Option<HashMap<String, String>>,
469    /// Volumes
470    #[serde(skip_serializing_if = "Option::is_none")]
471    pub volumes: Option<Vec<VolumeDefinition>>,
472}
473
474/// Script definition
475#[derive(Debug, Clone, Serialize, Deserialize)]
476pub struct ScriptDefinition {
477    /// Script language
478    #[serde(skip_serializing_if = "Option::is_none")]
479    pub language: Option<String>,
480    /// Script code
481    #[serde(skip_serializing_if = "Option::is_none")]
482    pub code: Option<String>,
483    /// Script arguments
484    #[serde(skip_serializing_if = "Option::is_none")]
485    pub arguments: Option<HashMap<String, Value>>,
486}
487
488/// Sub-workflow definition
489#[derive(Debug, Clone, Serialize, Deserialize)]
490pub struct SubWorkflowDefinition {
491    /// Sub-workflow namespace
492    #[serde(skip_serializing_if = "Option::is_none")]
493    pub namespace: Option<String>,
494    /// Sub-workflow name
495    pub name: String,
496    /// Sub-workflow version
497    #[serde(skip_serializing_if = "Option::is_none")]
498    pub version: Option<String>,
499    /// Input data for sub-workflow
500    #[serde(skip_serializing_if = "Option::is_none")]
501    pub input: Option<Value>,
502}
503
504/// Volume definition
505#[derive(Debug, Clone, Serialize, Deserialize)]
506pub struct VolumeDefinition {
507    /// Host path
508    pub host_path: String,
509    /// Container path
510    pub container_path: String,
511}
512
513/// Switch case definition
514#[derive(Debug, Clone, Serialize, Deserialize)]
515pub struct SwitchCase {
516    /// Case name
517    #[serde(skip_serializing_if = "Option::is_none")]
518    pub name: Option<String>,
519    /// Condition expression
520    #[serde(skip_serializing_if = "Option::is_none")]
521    pub when: Option<String>,
522    /// Steps to execute
523    #[serde(skip_serializing_if = "Option::is_none")]
524    pub then: Option<String>,
525    /// Default case (no condition)
526    #[serde(skip_serializing_if = "Option::is_none")]
527    pub default: Option<bool>,
528}
529
530/// For loop definition
531#[derive(Debug, Clone, Serialize, Deserialize)]
532pub struct ForDefinition {
533    /// Loop variable name
534    #[serde(skip_serializing_if = "Option::is_none")]
535    pub each: Option<String>,
536    /// Collection to iterate over (expression)
537    #[serde(skip_serializing_if = "Option::is_none")]
538    pub in_expr: Option<String>,
539    /// Loop index variable name
540    #[serde(skip_serializing_if = "Option::is_none")]
541    pub at: Option<String>,
542    /// Loop condition (while)
543    #[serde(skip_serializing_if = "Option::is_none")]
544    pub while_expr: Option<String>,
545    /// Steps to execute in loop
546    pub do_steps: Vec<WorkflowStep>,
547}
548
549/// Fork definition for parallel execution
550#[derive(Debug, Clone, Serialize, Deserialize)]
551pub struct ForkDefinition {
552    /// Fork branches
553    pub branches: Vec<ForkBranch>,
554    /// Whether to compete (first to complete wins)
555    #[serde(skip_serializing_if = "Option::is_none")]
556    pub compete: Option<bool>,
557}
558
559/// Fork branch
560#[derive(Debug, Clone, Serialize, Deserialize)]
561pub struct ForkBranch {
562    /// Branch name
563    #[serde(skip_serializing_if = "Option::is_none")]
564    pub name: Option<String>,
565    /// Steps to execute in this branch
566    pub do_steps: Vec<WorkflowStep>,
567}
568
569/// Try-catch definition
570#[derive(Debug, Clone, Serialize, Deserialize)]
571pub struct TryDefinition {
572    /// Steps to try
573    pub do_steps: Vec<WorkflowStep>,
574    /// Catch definitions
575    #[serde(skip_serializing_if = "Option::is_none")]
576    pub catch: Option<Vec<CatchDefinition>>,
577}
578
579/// Catch definition
580#[derive(Debug, Clone, Serialize, Deserialize)]
581pub struct CatchDefinition {
582    /// Error filter
583    #[serde(skip_serializing_if = "Option::is_none")]
584    pub errors: Option<ErrorFilter>,
585    /// Error variable name
586    #[serde(skip_serializing_if = "Option::is_none")]
587    pub as_var: Option<String>,
588    /// Steps to execute on error
589    pub do_steps: Vec<WorkflowStep>,
590}
591
592/// Error filter
593#[derive(Debug, Clone, Serialize, Deserialize)]
594pub struct ErrorFilter {
595    /// Error type filter
596    #[serde(skip_serializing_if = "Option::is_none")]
597    pub r#type: Option<String>,
598    /// Error status filter
599    #[serde(skip_serializing_if = "Option::is_none")]
600    pub status: Option<String>,
601}
602
603/// Raise error definition
604#[derive(Debug, Clone, Serialize, Deserialize)]
605pub struct RaiseDefinition {
606    /// Error definition
607    pub error: ErrorDefinition,
608}
609
610/// Error definition
611#[derive(Debug, Clone, Serialize, Deserialize)]
612pub struct ErrorDefinition {
613    /// Error type
614    #[serde(skip_serializing_if = "Option::is_none")]
615    pub r#type: Option<String>,
616    /// HTTP status code
617    #[serde(skip_serializing_if = "Option::is_none")]
618    pub status: Option<u16>,
619    /// Error title
620    #[serde(skip_serializing_if = "Option::is_none")]
621    pub title: Option<String>,
622    /// Error detail
623    #[serde(skip_serializing_if = "Option::is_none")]
624    pub detail: Option<String>,
625    /// Error instance
626    #[serde(skip_serializing_if = "Option::is_none")]
627    pub instance: Option<String>,
628}