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_string_ref(&self) -> Option<&str> {
75 match self {
76 Value::String(s) => Some(s),
77 _ => None,
78 }
79 }
80
81 pub fn as_integer(&self) -> Option<i64> {
83 match self {
84 Value::Integer(i) => Some(*i),
85 _ => None,
86 }
87 }
88
89 pub fn as_boolean(&self) -> Option<bool> {
91 match self {
92 Value::Boolean(b) => Some(*b),
93 _ => None,
94 }
95 }
96
97 pub fn as_number(&self) -> Option<f64> {
99 match self {
100 Value::Number(n) => Some(*n),
101 _ => None,
102 }
103 }
104
105 pub fn to_bool(&self) -> bool {
107 match self {
108 Value::Boolean(b) => *b,
109 Value::String(s) => !s.is_empty(),
110 Value::Number(n) => *n != 0.0,
111 Value::Integer(i) => *i != 0,
112 Value::Array(arr) => !arr.is_empty(),
113 Value::Object(obj) => !obj.is_empty(),
114 Value::Null => false,
115 Value::Expression(_) => false, }
117 }
118
119 pub fn call_method(&mut self, method: &str, args: Vec<Value>) -> Result<Value, String> {
121 match self {
122 Value::Object(ref mut obj) => match method {
123 "setSpeed" => {
124 if let Some(Value::Number(speed)) = args.first() {
125 obj.insert("Speed".to_string(), Value::Number(*speed));
126 Ok(Value::Null)
127 } else if let Some(Value::Integer(speed)) = args.first() {
128 obj.insert("Speed".to_string(), Value::Integer(*speed));
129 Ok(Value::Null)
130 } else {
131 Err("setSpeed requires a number argument".to_string())
132 }
133 }
134 "setTotalDistance" => {
135 if let Some(Value::Number(distance)) = args.first() {
136 obj.insert("TotalDistance".to_string(), Value::Number(*distance));
137 Ok(Value::Null)
138 } else if let Some(Value::Integer(distance)) = args.first() {
139 obj.insert("TotalDistance".to_string(), Value::Integer(*distance));
140 Ok(Value::Null)
141 } else {
142 Err("setTotalDistance requires a number argument".to_string())
143 }
144 }
145 "getTotalDistance" => Ok(obj
146 .get("TotalDistance")
147 .cloned()
148 .unwrap_or(Value::Number(0.0))),
149 "getSpeed" => Ok(obj.get("Speed").cloned().unwrap_or(Value::Number(0.0))),
150 _ => Err(format!("Method {} not found", method)),
151 },
152 _ => Err("Cannot call method on non-object value".to_string()),
153 }
154 }
155
156 pub fn get_property(&self, property: &str) -> Option<Value> {
158 match self {
159 Value::Object(obj) => obj.get(property).cloned(),
160 _ => None,
161 }
162 }
163
164 pub fn set_property(&mut self, property: &str, value: Value) -> Result<(), String> {
166 match self {
167 Value::Object(ref mut obj) => {
168 obj.insert(property.to_string(), value);
169 Ok(())
170 }
171 _ => Err("Cannot set property on non-object value".to_string()),
172 }
173 }
174}
175
176impl From<String> for Value {
177 fn from(s: String) -> Self {
178 Value::String(s)
179 }
180}
181
182impl From<&str> for Value {
183 fn from(s: &str) -> Self {
184 Value::String(s.to_string())
185 }
186}
187
188impl From<f64> for Value {
189 fn from(n: f64) -> Self {
190 Value::Number(n)
191 }
192}
193
194impl From<i64> for Value {
195 fn from(i: i64) -> Self {
196 Value::Integer(i)
197 }
198}
199
200impl From<bool> for Value {
201 fn from(b: bool) -> Self {
202 Value::Boolean(b)
203 }
204}
205
206impl From<serde_json::Value> for Value {
207 fn from(json_value: serde_json::Value) -> Self {
208 match json_value {
209 serde_json::Value::String(s) => Value::String(s),
210 serde_json::Value::Number(n) => {
211 if let Some(i) = n.as_i64() {
212 Value::Integer(i)
213 } else if let Some(f) = n.as_f64() {
214 Value::Number(f)
215 } else {
216 Value::Null
217 }
218 }
219 serde_json::Value::Bool(b) => Value::Boolean(b),
220 serde_json::Value::Array(arr) => {
221 Value::Array(arr.into_iter().map(Value::from).collect())
222 }
223 serde_json::Value::Object(obj) => {
224 let mut map = HashMap::new();
225 for (k, v) in obj {
226 map.insert(k, Value::from(v));
227 }
228 Value::Object(map)
229 }
230 serde_json::Value::Null => Value::Null,
231 }
232 }
233}
234
235#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
237pub enum Operator {
238 Equal,
240 NotEqual,
242 GreaterThan,
244 GreaterThanOrEqual,
246 LessThan,
248 LessThanOrEqual,
250 Contains,
252 NotContains,
254 StartsWith,
256 EndsWith,
258 Matches,
260 In,
262}
263
264impl Operator {
265 #[allow(clippy::should_implement_trait)]
267 pub fn from_str(s: &str) -> Option<Self> {
268 match s {
269 "==" | "eq" => Some(Operator::Equal),
270 "!=" | "ne" => Some(Operator::NotEqual),
271 ">" | "gt" => Some(Operator::GreaterThan),
272 ">=" | "gte" => Some(Operator::GreaterThanOrEqual),
273 "<" | "lt" => Some(Operator::LessThan),
274 "<=" | "lte" => Some(Operator::LessThanOrEqual),
275 "contains" => Some(Operator::Contains),
276 "not_contains" => Some(Operator::NotContains),
277 "starts_with" | "startsWith" => Some(Operator::StartsWith),
278 "ends_with" | "endsWith" => Some(Operator::EndsWith),
279 "matches" => Some(Operator::Matches),
280 "in" => Some(Operator::In),
281 _ => None,
282 }
283 }
284
285 pub fn evaluate(&self, left: &Value, right: &Value) -> bool {
287 match self {
288 Operator::Equal => {
289 if matches!(left, Value::Null) || matches!(right, Value::Null) {
292 let left_is_null = matches!(left, Value::Null)
294 || (matches!(left, Value::String(s) if s == "null"));
295 let right_is_null = matches!(right, Value::Null)
296 || (matches!(right, Value::String(s) if s == "null"));
297
298 left_is_null == right_is_null
299 } else {
300 left == right
301 }
302 }
303 Operator::NotEqual => {
304 if matches!(left, Value::Null) || matches!(right, Value::Null) {
306 let left_is_null = matches!(left, Value::Null)
307 || (matches!(left, Value::String(s) if s == "null"));
308 let right_is_null = matches!(right, Value::Null)
309 || (matches!(right, Value::String(s) if s == "null"));
310
311 left_is_null != right_is_null
312 } else {
313 left != right
314 }
315 }
316 Operator::GreaterThan => {
317 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
318 l > r
319 } else {
320 false
321 }
322 }
323 Operator::GreaterThanOrEqual => {
324 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
325 l >= r
326 } else {
327 false
328 }
329 }
330 Operator::LessThan => {
331 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
332 l < r
333 } else {
334 false
335 }
336 }
337 Operator::LessThanOrEqual => {
338 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
339 l <= r
340 } else {
341 false
342 }
343 }
344 Operator::Contains => {
345 if let (Some(l), Some(r)) = (left.as_string_ref(), right.as_string_ref()) {
346 l.contains(r)
347 } else {
348 false
349 }
350 }
351 Operator::NotContains => {
352 if let (Some(l), Some(r)) = (left.as_string_ref(), right.as_string_ref()) {
353 !l.contains(r)
354 } else {
355 false
356 }
357 }
358 Operator::StartsWith => {
359 if let (Some(l), Some(r)) = (left.as_string_ref(), right.as_string_ref()) {
360 l.starts_with(r)
361 } else {
362 false
363 }
364 }
365 Operator::EndsWith => {
366 if let (Some(l), Some(r)) = (left.as_string_ref(), right.as_string_ref()) {
367 l.ends_with(r)
368 } else {
369 false
370 }
371 }
372 Operator::Matches => {
373 if let (Some(l), Some(r)) = (left.as_string_ref(), right.as_string_ref()) {
375 l.contains(r)
377 } else {
378 false
379 }
380 }
381 Operator::In => {
382 match right {
384 Value::Array(arr) => arr.contains(left),
385 _ => false,
386 }
387 }
388 }
389 }
390}
391
392#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
394pub enum LogicalOperator {
395 And,
397 Or,
399 Not,
401}
402
403impl LogicalOperator {
404 #[allow(clippy::should_implement_trait)]
406 pub fn from_str(s: &str) -> Option<Self> {
407 match s.to_lowercase().as_str() {
408 "and" | "&&" => Some(LogicalOperator::And),
409 "or" | "||" => Some(LogicalOperator::Or),
410 "not" | "!" => Some(LogicalOperator::Not),
411 _ => None,
412 }
413 }
414}
415
416pub type Context = HashMap<String, Value>;
418
419#[derive(Debug, Clone, PartialEq)]
421pub enum ActionType {
422 Set {
424 field: String,
426 value: Value,
428 },
429 Log {
431 message: String,
433 },
434 MethodCall {
436 object: String,
438 method: String,
440 args: Vec<Value>,
442 },
443 Retract {
445 object: String,
447 },
448 Custom {
450 action_type: String,
452 params: HashMap<String, Value>,
454 },
455 ActivateAgendaGroup {
457 group: String,
459 },
460 ScheduleRule {
462 rule_name: String,
464 delay_ms: u64,
466 },
467 CompleteWorkflow {
469 workflow_name: String,
471 },
472 SetWorkflowData {
474 key: String,
476 value: Value,
478 },
479 Append {
481 field: String,
483 value: Value,
485 },
486}
487
488impl std::fmt::Display for Value {
490 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
491 match self {
492 Value::String(s) => write!(f, "{}", s),
493 Value::Number(n) => write!(f, "{}", n),
494 Value::Integer(i) => write!(f, "{}", i),
495 Value::Boolean(b) => write!(f, "{}", b),
496 Value::Array(_) => write!(f, "[Array]"),
497 Value::Object(_) => write!(f, "[Object]"),
498 Value::Null => write!(f, "null"),
499 Value::Expression(expr) => write!(f, "[Expr: {}]", expr),
500 }
501 }
502}