mofa_kernel/workflow/
command.rs1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9use super::StateUpdate;
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub enum ControlFlow {
16 Goto(String),
18
19 Continue,
21
22 Return,
24
25 Send(Vec<SendCommand>),
27}
28
29impl Default for ControlFlow {
30 fn default() -> Self {
31 Self::Continue
32 }
33}
34
35#[derive(Debug, Clone, Default, Serialize, Deserialize)]
66pub struct Command {
67 pub updates: Vec<StateUpdate>,
69 pub control: ControlFlow,
71}
72
73impl Command {
74 pub fn new() -> Self {
76 Self::default()
77 }
78
79 pub fn update(mut self, key: impl Into<String>, value: Value) -> Self {
81 self.updates.push(StateUpdate::new(key, value));
82 self
83 }
84
85 pub fn updates(mut self, updates: Vec<StateUpdate>) -> Self {
87 self.updates.extend(updates);
88 self
89 }
90
91 pub fn continue_(mut self) -> Self {
93 self.control = ControlFlow::Continue;
94 self
95 }
96
97 pub fn goto(mut self, node: impl Into<String>) -> Self {
99 self.control = ControlFlow::Goto(node.into());
100 self
101 }
102
103 pub fn return_(mut self) -> Self {
105 self.control = ControlFlow::Return;
106 self
107 }
108
109 pub fn send(targets: Vec<SendCommand>) -> Self {
111 Self {
112 updates: Vec::new(),
113 control: ControlFlow::Send(targets),
114 }
115 }
116
117 pub fn just_update(key: impl Into<String>, value: Value) -> Self {
119 Self::new().update(key, value)
120 }
121
122 pub fn just_goto(node: impl Into<String>) -> Self {
124 Self::new().goto(node)
125 }
126
127 pub fn just_return() -> Self {
129 Self::new().return_()
130 }
131
132 pub fn is_return(&self) -> bool {
134 matches!(self.control, ControlFlow::Return)
135 }
136
137 pub fn is_send(&self) -> bool {
139 matches!(self.control, ControlFlow::Send(_))
140 }
141
142 pub fn goto_target(&self) -> Option<&str> {
144 match &self.control {
145 ControlFlow::Goto(target) => Some(target),
146 _ => None,
147 }
148 }
149}
150
151#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
156pub struct SendCommand {
157 pub target: String,
159 pub input: Value,
161 pub branch_id: Option<String>,
163}
164
165impl SendCommand {
166 pub fn new(target: impl Into<String>, input: Value) -> Self {
168 Self {
169 target: target.into(),
170 input,
171 branch_id: None,
172 }
173 }
174
175 pub fn with_branch(
177 target: impl Into<String>,
178 input: Value,
179 branch_id: impl Into<String>,
180 ) -> Self {
181 Self {
182 target: target.into(),
183 input,
184 branch_id: Some(branch_id.into()),
185 }
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use serde_json::json;
193
194 #[test]
195 fn test_command_builder() {
196 let cmd = Command::new()
197 .update("key1", json!("value1"))
198 .update("key2", json!(42))
199 .goto("next_node");
200
201 assert_eq!(cmd.updates.len(), 2);
202 assert_eq!(cmd.updates[0].key, "key1");
203 assert_eq!(cmd.goto_target(), Some("next_node"));
204 }
205
206 #[test]
207 fn test_command_continue() {
208 let cmd = Command::new().update("result", json!("done")).continue_();
209
210 assert_eq!(cmd.control, ControlFlow::Continue);
211 assert!(!cmd.is_return());
212 }
213
214 #[test]
215 fn test_command_return() {
216 let cmd = Command::new().update("final", json!("result")).return_();
217
218 assert!(cmd.is_return());
219 }
220
221 #[test]
222 fn test_command_send() {
223 let cmd = Command::send(vec![
224 SendCommand::new("worker", json!({"task": 1})),
225 SendCommand::new("worker", json!({"task": 2})),
226 ]);
227
228 assert!(cmd.is_send());
229 if let ControlFlow::Send(targets) = &cmd.control {
230 assert_eq!(targets.len(), 2);
231 } else {
232 panic!("Expected Send control flow");
233 }
234 }
235
236 #[test]
237 fn test_send_command() {
238 let send = SendCommand::new("process", json!({"data": "test"}));
239 assert_eq!(send.target, "process");
240 assert!(send.branch_id.is_none());
241
242 let send_with_branch =
243 SendCommand::with_branch("process", json!({"data": "test"}), "branch-1");
244 assert_eq!(send_with_branch.branch_id, Some("branch-1".to_string()));
245 }
246
247 #[test]
248 fn test_just_helpers() {
249 let cmd = Command::just_update("key", json!("value"));
250 assert_eq!(cmd.updates.len(), 1);
251 assert_eq!(cmd.control, ControlFlow::Continue);
252
253 let cmd = Command::just_goto("target");
254 assert!(cmd.updates.is_empty());
255 assert_eq!(cmd.goto_target(), Some("target"));
256
257 let cmd = Command::just_return();
258 assert!(cmd.is_return());
259 }
260}