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(target: impl Into<String>, input: Value, branch_id: impl Into<String>) -> Self {
177 Self {
178 target: target.into(),
179 input,
180 branch_id: Some(branch_id.into()),
181 }
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188 use serde_json::json;
189
190 #[test]
191 fn test_command_builder() {
192 let cmd = Command::new()
193 .update("key1", json!("value1"))
194 .update("key2", json!(42))
195 .goto("next_node");
196
197 assert_eq!(cmd.updates.len(), 2);
198 assert_eq!(cmd.updates[0].key, "key1");
199 assert_eq!(cmd.goto_target(), Some("next_node"));
200 }
201
202 #[test]
203 fn test_command_continue() {
204 let cmd = Command::new()
205 .update("result", json!("done"))
206 .continue_();
207
208 assert_eq!(cmd.control, ControlFlow::Continue);
209 assert!(!cmd.is_return());
210 }
211
212 #[test]
213 fn test_command_return() {
214 let cmd = Command::new()
215 .update("final", json!("result"))
216 .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 = SendCommand::with_branch(
243 "process",
244 json!({"data": "test"}),
245 "branch-1",
246 );
247 assert_eq!(send_with_branch.branch_id, Some("branch-1".to_string()));
248 }
249
250 #[test]
251 fn test_just_helpers() {
252 let cmd = Command::just_update("key", json!("value"));
253 assert_eq!(cmd.updates.len(), 1);
254 assert_eq!(cmd.control, ControlFlow::Continue);
255
256 let cmd = Command::just_goto("target");
257 assert!(cmd.updates.is_empty());
258 assert_eq!(cmd.goto_target(), Some("target"));
259
260 let cmd = Command::just_return();
261 assert!(cmd.is_return());
262 }
263}