1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6pub enum Value {
7 String(String),
9 Number(f64),
11 Integer(i64),
13 Boolean(bool),
15 Array(Vec<Value>),
17 Object(HashMap<String, Value>),
19 Null,
21 Expression(String),
23}
24
25impl Value {
26 #[allow(clippy::inherent_to_string_shadow_display)]
28 pub fn to_string(&self) -> String {
29 match self {
30 Value::String(s) => s.clone(), Value::Number(n) => n.to_string(),
32 Value::Integer(i) => i.to_string(),
33 Value::Boolean(b) => b.to_string(),
34 Value::Array(_) => "[Array]".to_string(),
35 Value::Object(_) => "[Object]".to_string(),
36 Value::Null => "null".to_string(),
37 Value::Expression(expr) => format!("[Expr: {}]", expr),
38 }
39 }
40
41 pub fn as_str(&self) -> std::borrow::Cow<'_, str> {
43 match self {
44 Value::String(s) => std::borrow::Cow::Borrowed(s),
45 Value::Number(n) => std::borrow::Cow::Owned(n.to_string()),
46 Value::Integer(i) => std::borrow::Cow::Owned(i.to_string()),
47 Value::Boolean(b) => std::borrow::Cow::Borrowed(if *b { "true" } else { "false" }),
48 Value::Array(_) => std::borrow::Cow::Borrowed("[Array]"),
49 Value::Object(_) => std::borrow::Cow::Borrowed("[Object]"),
50 Value::Null => std::borrow::Cow::Borrowed("null"),
51 Value::Expression(expr) => std::borrow::Cow::Owned(format!("[Expr: {}]", expr)),
52 }
53 }
54
55 pub fn to_number(&self) -> Option<f64> {
57 match self {
58 Value::Number(n) => Some(*n),
59 Value::Integer(i) => Some(*i as f64),
60 Value::String(s) => s.parse::<f64>().ok(),
61 _ => None,
62 }
63 }
64
65 pub fn as_string(&self) -> Option<String> {
67 match self {
68 Value::String(s) => Some(s.clone()),
69 _ => None,
70 }
71 }
72
73 pub fn as_integer(&self) -> Option<i64> {
75 match self {
76 Value::Integer(i) => Some(*i),
77 _ => None,
78 }
79 }
80
81 pub fn as_boolean(&self) -> Option<bool> {
83 match self {
84 Value::Boolean(b) => Some(*b),
85 _ => None,
86 }
87 }
88
89 pub fn as_number(&self) -> Option<f64> {
91 match self {
92 Value::Number(n) => Some(*n),
93 _ => None,
94 }
95 }
96
97 pub fn to_bool(&self) -> bool {
99 match self {
100 Value::Boolean(b) => *b,
101 Value::String(s) => !s.is_empty(),
102 Value::Number(n) => *n != 0.0,
103 Value::Integer(i) => *i != 0,
104 Value::Array(arr) => !arr.is_empty(),
105 Value::Object(obj) => !obj.is_empty(),
106 Value::Null => false,
107 Value::Expression(_) => false, }
109 }
110
111 pub fn call_method(&mut self, method: &str, args: Vec<Value>) -> Result<Value, String> {
113 match self {
114 Value::Object(ref mut obj) => match method {
115 "setSpeed" => {
116 if let Some(Value::Number(speed)) = args.first() {
117 obj.insert("Speed".to_string(), Value::Number(*speed));
118 Ok(Value::Null)
119 } else if let Some(Value::Integer(speed)) = args.first() {
120 obj.insert("Speed".to_string(), Value::Integer(*speed));
121 Ok(Value::Null)
122 } else {
123 Err("setSpeed requires a number argument".to_string())
124 }
125 }
126 "setTotalDistance" => {
127 if let Some(Value::Number(distance)) = args.first() {
128 obj.insert("TotalDistance".to_string(), Value::Number(*distance));
129 Ok(Value::Null)
130 } else if let Some(Value::Integer(distance)) = args.first() {
131 obj.insert("TotalDistance".to_string(), Value::Integer(*distance));
132 Ok(Value::Null)
133 } else {
134 Err("setTotalDistance requires a number argument".to_string())
135 }
136 }
137 "getTotalDistance" => Ok(obj
138 .get("TotalDistance")
139 .cloned()
140 .unwrap_or(Value::Number(0.0))),
141 "getSpeed" => Ok(obj.get("Speed").cloned().unwrap_or(Value::Number(0.0))),
142 _ => Err(format!("Method {} not found", method)),
143 },
144 _ => Err("Cannot call method on non-object value".to_string()),
145 }
146 }
147
148 pub fn get_property(&self, property: &str) -> Option<Value> {
150 match self {
151 Value::Object(obj) => obj.get(property).cloned(),
152 _ => None,
153 }
154 }
155
156 pub fn set_property(&mut self, property: &str, value: Value) -> Result<(), String> {
158 match self {
159 Value::Object(ref mut obj) => {
160 obj.insert(property.to_string(), value);
161 Ok(())
162 }
163 _ => Err("Cannot set property on non-object value".to_string()),
164 }
165 }
166}
167
168impl From<String> for Value {
169 fn from(s: String) -> Self {
170 Value::String(s)
171 }
172}
173
174impl From<&str> for Value {
175 fn from(s: &str) -> Self {
176 Value::String(s.to_string())
177 }
178}
179
180impl From<f64> for Value {
181 fn from(n: f64) -> Self {
182 Value::Number(n)
183 }
184}
185
186impl From<i64> for Value {
187 fn from(i: i64) -> Self {
188 Value::Integer(i)
189 }
190}
191
192impl From<bool> for Value {
193 fn from(b: bool) -> Self {
194 Value::Boolean(b)
195 }
196}
197
198impl From<serde_json::Value> for Value {
199 fn from(json_value: serde_json::Value) -> Self {
200 match json_value {
201 serde_json::Value::String(s) => Value::String(s),
202 serde_json::Value::Number(n) => {
203 if let Some(i) = n.as_i64() {
204 Value::Integer(i)
205 } else if let Some(f) = n.as_f64() {
206 Value::Number(f)
207 } else {
208 Value::Null
209 }
210 }
211 serde_json::Value::Bool(b) => Value::Boolean(b),
212 serde_json::Value::Array(arr) => {
213 Value::Array(arr.into_iter().map(Value::from).collect())
214 }
215 serde_json::Value::Object(obj) => {
216 let mut map = HashMap::new();
217 for (k, v) in obj {
218 map.insert(k, Value::from(v));
219 }
220 Value::Object(map)
221 }
222 serde_json::Value::Null => Value::Null,
223 }
224 }
225}
226
227#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
229pub enum Operator {
230 Equal,
232 NotEqual,
234 GreaterThan,
236 GreaterThanOrEqual,
238 LessThan,
240 LessThanOrEqual,
242 Contains,
244 NotContains,
246 StartsWith,
248 EndsWith,
250 Matches,
252 In,
254}
255
256impl Operator {
257 #[allow(clippy::should_implement_trait)]
259 pub fn from_str(s: &str) -> Option<Self> {
260 match s {
261 "==" | "eq" => Some(Operator::Equal),
262 "!=" | "ne" => Some(Operator::NotEqual),
263 ">" | "gt" => Some(Operator::GreaterThan),
264 ">=" | "gte" => Some(Operator::GreaterThanOrEqual),
265 "<" | "lt" => Some(Operator::LessThan),
266 "<=" | "lte" => Some(Operator::LessThanOrEqual),
267 "contains" => Some(Operator::Contains),
268 "not_contains" => Some(Operator::NotContains),
269 "starts_with" | "startsWith" => Some(Operator::StartsWith),
270 "ends_with" | "endsWith" => Some(Operator::EndsWith),
271 "matches" => Some(Operator::Matches),
272 "in" => Some(Operator::In),
273 _ => None,
274 }
275 }
276
277 pub fn evaluate(&self, left: &Value, right: &Value) -> bool {
279 match self {
280 Operator::Equal => {
281 if matches!(left, Value::Null) || matches!(right, Value::Null) {
284 let left_is_null = matches!(left, Value::Null)
286 || (matches!(left, Value::String(s) if s == "null"));
287 let right_is_null = matches!(right, Value::Null)
288 || (matches!(right, Value::String(s) if s == "null"));
289
290 left_is_null == right_is_null
291 } else {
292 left == right
293 }
294 }
295 Operator::NotEqual => {
296 if matches!(left, Value::Null) || matches!(right, Value::Null) {
298 let left_is_null = matches!(left, Value::Null)
299 || (matches!(left, Value::String(s) if s == "null"));
300 let right_is_null = matches!(right, Value::Null)
301 || (matches!(right, Value::String(s) if s == "null"));
302
303 left_is_null != right_is_null
304 } else {
305 left != right
306 }
307 }
308 Operator::GreaterThan => {
309 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
310 l > r
311 } else {
312 false
313 }
314 }
315 Operator::GreaterThanOrEqual => {
316 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
317 l >= r
318 } else {
319 false
320 }
321 }
322 Operator::LessThan => {
323 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
324 l < r
325 } else {
326 false
327 }
328 }
329 Operator::LessThanOrEqual => {
330 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
331 l <= r
332 } else {
333 false
334 }
335 }
336 Operator::Contains => {
337 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
338 l.contains(&r)
339 } else {
340 false
341 }
342 }
343 Operator::NotContains => {
344 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
345 !l.contains(&r)
346 } else {
347 false
348 }
349 }
350 Operator::StartsWith => {
351 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
352 l.starts_with(&r)
353 } else {
354 false
355 }
356 }
357 Operator::EndsWith => {
358 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
359 l.ends_with(&r)
360 } else {
361 false
362 }
363 }
364 Operator::Matches => {
365 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
367 l.contains(&r)
369 } else {
370 false
371 }
372 }
373 Operator::In => {
374 match right {
376 Value::Array(arr) => arr.contains(left),
377 _ => false,
378 }
379 }
380 }
381 }
382}
383
384#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
386pub enum LogicalOperator {
387 And,
389 Or,
391 Not,
393}
394
395impl LogicalOperator {
396 #[allow(clippy::should_implement_trait)]
398 pub fn from_str(s: &str) -> Option<Self> {
399 match s.to_lowercase().as_str() {
400 "and" | "&&" => Some(LogicalOperator::And),
401 "or" | "||" => Some(LogicalOperator::Or),
402 "not" | "!" => Some(LogicalOperator::Not),
403 _ => None,
404 }
405 }
406}
407
408pub type Context = HashMap<String, Value>;
410
411#[derive(Debug, Clone, PartialEq)]
413pub enum ActionType {
414 Set {
416 field: String,
418 value: Value,
420 },
421 Log {
423 message: String,
425 },
426 MethodCall {
428 object: String,
430 method: String,
432 args: Vec<Value>,
434 },
435 Retract {
437 object: String,
439 },
440 Custom {
442 action_type: String,
444 params: HashMap<String, Value>,
446 },
447 ActivateAgendaGroup {
449 group: String,
451 },
452 ScheduleRule {
454 rule_name: String,
456 delay_ms: u64,
458 },
459 CompleteWorkflow {
461 workflow_name: String,
463 },
464 SetWorkflowData {
466 key: String,
468 value: Value,
470 },
471 Append {
473 field: String,
475 value: Value,
477 },
478}
479
480impl std::fmt::Display for Value {
482 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
483 match self {
484 Value::String(s) => write!(f, "{}", s),
485 Value::Number(n) => write!(f, "{}", n),
486 Value::Integer(i) => write!(f, "{}", i),
487 Value::Boolean(b) => write!(f, "{}", b),
488 Value::Array(_) => write!(f, "[Array]"),
489 Value::Object(_) => write!(f, "[Object]"),
490 Value::Null => write!(f, "null"),
491 Value::Expression(expr) => write!(f, "[Expr: {}]", expr),
492 }
493 }
494}