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)]
28 pub fn to_string(&self) -> String {
29 match self {
30 Value::String(s) => s.clone(),
31 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 to_number(&self) -> Option<f64> {
43 match self {
44 Value::Number(n) => Some(*n),
45 Value::Integer(i) => Some(*i as f64),
46 Value::String(s) => s.parse::<f64>().ok(),
47 _ => None,
48 }
49 }
50
51 pub fn as_string(&self) -> Option<String> {
53 match self {
54 Value::String(s) => Some(s.clone()),
55 _ => None,
56 }
57 }
58
59 pub fn as_integer(&self) -> Option<i64> {
61 match self {
62 Value::Integer(i) => Some(*i),
63 _ => None,
64 }
65 }
66
67 pub fn as_boolean(&self) -> Option<bool> {
69 match self {
70 Value::Boolean(b) => Some(*b),
71 _ => None,
72 }
73 }
74
75 pub fn as_number(&self) -> Option<f64> {
77 match self {
78 Value::Number(n) => Some(*n),
79 _ => None,
80 }
81 }
82
83 pub fn to_bool(&self) -> bool {
85 match self {
86 Value::Boolean(b) => *b,
87 Value::String(s) => !s.is_empty(),
88 Value::Number(n) => *n != 0.0,
89 Value::Integer(i) => *i != 0,
90 Value::Array(arr) => !arr.is_empty(),
91 Value::Object(obj) => !obj.is_empty(),
92 Value::Null => false,
93 Value::Expression(_) => false, }
95 }
96
97 pub fn call_method(&mut self, method: &str, args: Vec<Value>) -> Result<Value, String> {
99 match self {
100 Value::Object(ref mut obj) => match method {
101 "setSpeed" => {
102 if let Some(Value::Number(speed)) = args.first() {
103 obj.insert("Speed".to_string(), Value::Number(*speed));
104 Ok(Value::Null)
105 } else if let Some(Value::Integer(speed)) = args.first() {
106 obj.insert("Speed".to_string(), Value::Integer(*speed));
107 Ok(Value::Null)
108 } else {
109 Err("setSpeed requires a number argument".to_string())
110 }
111 }
112 "setTotalDistance" => {
113 if let Some(Value::Number(distance)) = args.first() {
114 obj.insert("TotalDistance".to_string(), Value::Number(*distance));
115 Ok(Value::Null)
116 } else if let Some(Value::Integer(distance)) = args.first() {
117 obj.insert("TotalDistance".to_string(), Value::Integer(*distance));
118 Ok(Value::Null)
119 } else {
120 Err("setTotalDistance requires a number argument".to_string())
121 }
122 }
123 "getTotalDistance" => Ok(obj
124 .get("TotalDistance")
125 .cloned()
126 .unwrap_or(Value::Number(0.0))),
127 "getSpeed" => Ok(obj.get("Speed").cloned().unwrap_or(Value::Number(0.0))),
128 _ => Err(format!("Method {} not found", method)),
129 },
130 _ => Err("Cannot call method on non-object value".to_string()),
131 }
132 }
133
134 pub fn get_property(&self, property: &str) -> Option<Value> {
136 match self {
137 Value::Object(obj) => obj.get(property).cloned(),
138 _ => None,
139 }
140 }
141
142 pub fn set_property(&mut self, property: &str, value: Value) -> Result<(), String> {
144 match self {
145 Value::Object(ref mut obj) => {
146 obj.insert(property.to_string(), value);
147 Ok(())
148 }
149 _ => Err("Cannot set property on non-object value".to_string()),
150 }
151 }
152}
153
154impl From<String> for Value {
155 fn from(s: String) -> Self {
156 Value::String(s)
157 }
158}
159
160impl From<&str> for Value {
161 fn from(s: &str) -> Self {
162 Value::String(s.to_string())
163 }
164}
165
166impl From<f64> for Value {
167 fn from(n: f64) -> Self {
168 Value::Number(n)
169 }
170}
171
172impl From<i64> for Value {
173 fn from(i: i64) -> Self {
174 Value::Integer(i)
175 }
176}
177
178impl From<bool> for Value {
179 fn from(b: bool) -> Self {
180 Value::Boolean(b)
181 }
182}
183
184impl From<serde_json::Value> for Value {
185 fn from(json_value: serde_json::Value) -> Self {
186 match json_value {
187 serde_json::Value::String(s) => Value::String(s),
188 serde_json::Value::Number(n) => {
189 if let Some(i) = n.as_i64() {
190 Value::Integer(i)
191 } else if let Some(f) = n.as_f64() {
192 Value::Number(f)
193 } else {
194 Value::Null
195 }
196 }
197 serde_json::Value::Bool(b) => Value::Boolean(b),
198 serde_json::Value::Array(arr) => {
199 Value::Array(arr.into_iter().map(Value::from).collect())
200 }
201 serde_json::Value::Object(obj) => {
202 let mut map = HashMap::new();
203 for (k, v) in obj {
204 map.insert(k, Value::from(v));
205 }
206 Value::Object(map)
207 }
208 serde_json::Value::Null => Value::Null,
209 }
210 }
211}
212
213#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
215pub enum Operator {
216 Equal,
218 NotEqual,
220 GreaterThan,
222 GreaterThanOrEqual,
224 LessThan,
226 LessThanOrEqual,
228 Contains,
230 NotContains,
232 StartsWith,
234 EndsWith,
236 Matches,
238}
239
240impl Operator {
241 #[allow(clippy::should_implement_trait)]
243 pub fn from_str(s: &str) -> Option<Self> {
244 match s {
245 "==" | "eq" => Some(Operator::Equal),
246 "!=" | "ne" => Some(Operator::NotEqual),
247 ">" | "gt" => Some(Operator::GreaterThan),
248 ">=" | "gte" => Some(Operator::GreaterThanOrEqual),
249 "<" | "lt" => Some(Operator::LessThan),
250 "<=" | "lte" => Some(Operator::LessThanOrEqual),
251 "contains" => Some(Operator::Contains),
252 "not_contains" => Some(Operator::NotContains),
253 "starts_with" => Some(Operator::StartsWith),
254 "ends_with" => Some(Operator::EndsWith),
255 "matches" => Some(Operator::Matches),
256 _ => None,
257 }
258 }
259
260 pub fn evaluate(&self, left: &Value, right: &Value) -> bool {
262 match self {
263 Operator::Equal => {
264 if matches!(left, Value::Null) || matches!(right, Value::Null) {
267 let left_is_null = matches!(left, Value::Null)
269 || (matches!(left, Value::String(s) if s == "null"));
270 let right_is_null = matches!(right, Value::Null)
271 || (matches!(right, Value::String(s) if s == "null"));
272
273 left_is_null == right_is_null
274 } else {
275 left == right
276 }
277 }
278 Operator::NotEqual => {
279 if matches!(left, Value::Null) || matches!(right, Value::Null) {
281 let left_is_null = matches!(left, Value::Null)
282 || (matches!(left, Value::String(s) if s == "null"));
283 let right_is_null = matches!(right, Value::Null)
284 || (matches!(right, Value::String(s) if s == "null"));
285
286 left_is_null != right_is_null
287 } else {
288 left != right
289 }
290 }
291 Operator::GreaterThan => {
292 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
293 l > r
294 } else {
295 false
296 }
297 }
298 Operator::GreaterThanOrEqual => {
299 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
300 l >= r
301 } else {
302 false
303 }
304 }
305 Operator::LessThan => {
306 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
307 l < r
308 } else {
309 false
310 }
311 }
312 Operator::LessThanOrEqual => {
313 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
314 l <= r
315 } else {
316 false
317 }
318 }
319 Operator::Contains => {
320 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
321 l.contains(&r)
322 } else {
323 false
324 }
325 }
326 Operator::NotContains => {
327 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
328 !l.contains(&r)
329 } else {
330 false
331 }
332 }
333 Operator::StartsWith => {
334 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
335 l.starts_with(&r)
336 } else {
337 false
338 }
339 }
340 Operator::EndsWith => {
341 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
342 l.ends_with(&r)
343 } else {
344 false
345 }
346 }
347 Operator::Matches => {
348 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
350 l.contains(&r)
352 } else {
353 false
354 }
355 }
356 }
357 }
358}
359
360#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
362pub enum LogicalOperator {
363 And,
365 Or,
367 Not,
369}
370
371impl LogicalOperator {
372 #[allow(clippy::should_implement_trait)]
374 pub fn from_str(s: &str) -> Option<Self> {
375 match s.to_lowercase().as_str() {
376 "and" | "&&" => Some(LogicalOperator::And),
377 "or" | "||" => Some(LogicalOperator::Or),
378 "not" | "!" => Some(LogicalOperator::Not),
379 _ => None,
380 }
381 }
382}
383
384pub type Context = HashMap<String, Value>;
386
387#[derive(Debug, Clone, PartialEq)]
389pub enum ActionType {
390 Set {
392 field: String,
394 value: Value,
396 },
397 Log {
399 message: String,
401 },
402 MethodCall {
404 object: String,
406 method: String,
408 args: Vec<Value>,
410 },
411 Retract {
413 object: String,
415 },
416 Custom {
418 action_type: String,
420 params: HashMap<String, Value>,
422 },
423 ActivateAgendaGroup {
425 group: String,
427 },
428 ScheduleRule {
430 rule_name: String,
432 delay_ms: u64,
434 },
435 CompleteWorkflow {
437 workflow_name: String,
439 },
440 SetWorkflowData {
442 key: String,
444 value: Value,
446 },
447}