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 => left == right,
264 Operator::NotEqual => left != right,
265 Operator::GreaterThan => {
266 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
267 l > r
268 } else {
269 false
270 }
271 }
272 Operator::GreaterThanOrEqual => {
273 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
274 l >= r
275 } else {
276 false
277 }
278 }
279 Operator::LessThan => {
280 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
281 l < r
282 } else {
283 false
284 }
285 }
286 Operator::LessThanOrEqual => {
287 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
288 l <= r
289 } else {
290 false
291 }
292 }
293 Operator::Contains => {
294 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
295 l.contains(&r)
296 } else {
297 false
298 }
299 }
300 Operator::NotContains => {
301 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
302 !l.contains(&r)
303 } else {
304 false
305 }
306 }
307 Operator::StartsWith => {
308 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
309 l.starts_with(&r)
310 } else {
311 false
312 }
313 }
314 Operator::EndsWith => {
315 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
316 l.ends_with(&r)
317 } else {
318 false
319 }
320 }
321 Operator::Matches => {
322 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
324 l.contains(&r)
326 } else {
327 false
328 }
329 }
330 }
331 }
332}
333
334#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
336pub enum LogicalOperator {
337 And,
339 Or,
341 Not,
343}
344
345impl LogicalOperator {
346 #[allow(clippy::should_implement_trait)]
348 pub fn from_str(s: &str) -> Option<Self> {
349 match s.to_lowercase().as_str() {
350 "and" | "&&" => Some(LogicalOperator::And),
351 "or" | "||" => Some(LogicalOperator::Or),
352 "not" | "!" => Some(LogicalOperator::Not),
353 _ => None,
354 }
355 }
356}
357
358pub type Context = HashMap<String, Value>;
360
361#[derive(Debug, Clone, PartialEq)]
363pub enum ActionType {
364 Set {
366 field: String,
368 value: Value,
370 },
371 Log {
373 message: String,
375 },
376 Call {
378 function: String,
380 args: Vec<Value>,
382 },
383 MethodCall {
385 object: String,
387 method: String,
389 args: Vec<Value>,
391 },
392 Update {
394 object: String,
396 },
397 Retract {
399 object: String,
401 },
402 Custom {
404 action_type: String,
406 params: HashMap<String, Value>,
408 },
409 ActivateAgendaGroup {
411 group: String,
413 },
414 ScheduleRule {
416 rule_name: String,
418 delay_ms: u64,
420 },
421 CompleteWorkflow {
423 workflow_name: String,
425 },
426 SetWorkflowData {
428 key: String,
430 value: Value,
432 },
433}