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}
22
23impl Value {
24 #[allow(clippy::inherent_to_string)]
26 pub fn to_string(&self) -> String {
27 match self {
28 Value::String(s) => s.clone(),
29 Value::Number(n) => n.to_string(),
30 Value::Integer(i) => i.to_string(),
31 Value::Boolean(b) => b.to_string(),
32 Value::Array(_) => "[Array]".to_string(),
33 Value::Object(_) => "[Object]".to_string(),
34 Value::Null => "null".to_string(),
35 }
36 }
37
38 pub fn to_number(&self) -> Option<f64> {
40 match self {
41 Value::Number(n) => Some(*n),
42 Value::Integer(i) => Some(*i as f64),
43 Value::String(s) => s.parse::<f64>().ok(),
44 _ => None,
45 }
46 }
47
48 pub fn as_string(&self) -> Option<String> {
50 match self {
51 Value::String(s) => Some(s.clone()),
52 _ => None,
53 }
54 }
55
56 pub fn as_integer(&self) -> Option<i64> {
58 match self {
59 Value::Integer(i) => Some(*i),
60 _ => None,
61 }
62 }
63
64 pub fn as_boolean(&self) -> Option<bool> {
66 match self {
67 Value::Boolean(b) => Some(*b),
68 _ => None,
69 }
70 }
71
72 pub fn as_number(&self) -> Option<f64> {
74 match self {
75 Value::Number(n) => Some(*n),
76 _ => None,
77 }
78 }
79
80 pub fn to_bool(&self) -> bool {
82 match self {
83 Value::Boolean(b) => *b,
84 Value::String(s) => !s.is_empty(),
85 Value::Number(n) => *n != 0.0,
86 Value::Integer(i) => *i != 0,
87 Value::Array(arr) => !arr.is_empty(),
88 Value::Object(obj) => !obj.is_empty(),
89 Value::Null => false,
90 }
91 }
92
93 pub fn call_method(&mut self, method: &str, args: Vec<Value>) -> Result<Value, String> {
95 match self {
96 Value::Object(ref mut obj) => match method {
97 "setSpeed" => {
98 if let Some(Value::Number(speed)) = args.first() {
99 obj.insert("Speed".to_string(), Value::Number(*speed));
100 Ok(Value::Null)
101 } else if let Some(Value::Integer(speed)) = args.first() {
102 obj.insert("Speed".to_string(), Value::Integer(*speed));
103 Ok(Value::Null)
104 } else {
105 Err("setSpeed requires a number argument".to_string())
106 }
107 }
108 "setTotalDistance" => {
109 if let Some(Value::Number(distance)) = args.first() {
110 obj.insert("TotalDistance".to_string(), Value::Number(*distance));
111 Ok(Value::Null)
112 } else if let Some(Value::Integer(distance)) = args.first() {
113 obj.insert("TotalDistance".to_string(), Value::Integer(*distance));
114 Ok(Value::Null)
115 } else {
116 Err("setTotalDistance requires a number argument".to_string())
117 }
118 }
119 "getTotalDistance" => Ok(obj
120 .get("TotalDistance")
121 .cloned()
122 .unwrap_or(Value::Number(0.0))),
123 "getSpeed" => Ok(obj.get("Speed").cloned().unwrap_or(Value::Number(0.0))),
124 _ => Err(format!("Method {} not found", method)),
125 },
126 _ => Err("Cannot call method on non-object value".to_string()),
127 }
128 }
129
130 pub fn get_property(&self, property: &str) -> Option<Value> {
132 match self {
133 Value::Object(obj) => obj.get(property).cloned(),
134 _ => None,
135 }
136 }
137
138 pub fn set_property(&mut self, property: &str, value: Value) -> Result<(), String> {
140 match self {
141 Value::Object(ref mut obj) => {
142 obj.insert(property.to_string(), value);
143 Ok(())
144 }
145 _ => Err("Cannot set property on non-object value".to_string()),
146 }
147 }
148}
149
150impl From<String> for Value {
151 fn from(s: String) -> Self {
152 Value::String(s)
153 }
154}
155
156impl From<&str> for Value {
157 fn from(s: &str) -> Self {
158 Value::String(s.to_string())
159 }
160}
161
162impl From<f64> for Value {
163 fn from(n: f64) -> Self {
164 Value::Number(n)
165 }
166}
167
168impl From<i64> for Value {
169 fn from(i: i64) -> Self {
170 Value::Integer(i)
171 }
172}
173
174impl From<bool> for Value {
175 fn from(b: bool) -> Self {
176 Value::Boolean(b)
177 }
178}
179
180impl From<serde_json::Value> for Value {
181 fn from(json_value: serde_json::Value) -> Self {
182 match json_value {
183 serde_json::Value::String(s) => Value::String(s),
184 serde_json::Value::Number(n) => {
185 if let Some(i) = n.as_i64() {
186 Value::Integer(i)
187 } else if let Some(f) = n.as_f64() {
188 Value::Number(f)
189 } else {
190 Value::Null
191 }
192 }
193 serde_json::Value::Bool(b) => Value::Boolean(b),
194 serde_json::Value::Array(arr) => {
195 Value::Array(arr.into_iter().map(Value::from).collect())
196 }
197 serde_json::Value::Object(obj) => {
198 let mut map = HashMap::new();
199 for (k, v) in obj {
200 map.insert(k, Value::from(v));
201 }
202 Value::Object(map)
203 }
204 serde_json::Value::Null => Value::Null,
205 }
206 }
207}
208
209#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
211pub enum Operator {
212 Equal,
214 NotEqual,
216 GreaterThan,
218 GreaterThanOrEqual,
220 LessThan,
222 LessThanOrEqual,
224 Contains,
226 NotContains,
228 StartsWith,
230 EndsWith,
232 Matches,
234}
235
236impl Operator {
237 #[allow(clippy::should_implement_trait)]
239 pub fn from_str(s: &str) -> Option<Self> {
240 match s {
241 "==" | "eq" => Some(Operator::Equal),
242 "!=" | "ne" => Some(Operator::NotEqual),
243 ">" | "gt" => Some(Operator::GreaterThan),
244 ">=" | "gte" => Some(Operator::GreaterThanOrEqual),
245 "<" | "lt" => Some(Operator::LessThan),
246 "<=" | "lte" => Some(Operator::LessThanOrEqual),
247 "contains" => Some(Operator::Contains),
248 "not_contains" => Some(Operator::NotContains),
249 "starts_with" => Some(Operator::StartsWith),
250 "ends_with" => Some(Operator::EndsWith),
251 "matches" => Some(Operator::Matches),
252 _ => None,
253 }
254 }
255
256 pub fn evaluate(&self, left: &Value, right: &Value) -> bool {
258 match self {
259 Operator::Equal => left == right,
260 Operator::NotEqual => left != right,
261 Operator::GreaterThan => {
262 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
263 l > r
264 } else {
265 false
266 }
267 }
268 Operator::GreaterThanOrEqual => {
269 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
270 l >= r
271 } else {
272 false
273 }
274 }
275 Operator::LessThan => {
276 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
277 l < r
278 } else {
279 false
280 }
281 }
282 Operator::LessThanOrEqual => {
283 if let (Some(l), Some(r)) = (left.to_number(), right.to_number()) {
284 l <= r
285 } else {
286 false
287 }
288 }
289 Operator::Contains => {
290 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
291 l.contains(&r)
292 } else {
293 false
294 }
295 }
296 Operator::NotContains => {
297 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
298 !l.contains(&r)
299 } else {
300 false
301 }
302 }
303 Operator::StartsWith => {
304 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
305 l.starts_with(&r)
306 } else {
307 false
308 }
309 }
310 Operator::EndsWith => {
311 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
312 l.ends_with(&r)
313 } else {
314 false
315 }
316 }
317 Operator::Matches => {
318 if let (Some(l), Some(r)) = (left.as_string(), right.as_string()) {
320 l.contains(&r)
322 } else {
323 false
324 }
325 }
326 }
327 }
328}
329
330#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
332pub enum LogicalOperator {
333 And,
335 Or,
337 Not,
339}
340
341impl LogicalOperator {
342 #[allow(clippy::should_implement_trait)]
344 pub fn from_str(s: &str) -> Option<Self> {
345 match s.to_lowercase().as_str() {
346 "and" | "&&" => Some(LogicalOperator::And),
347 "or" | "||" => Some(LogicalOperator::Or),
348 "not" | "!" => Some(LogicalOperator::Not),
349 _ => None,
350 }
351 }
352}
353
354pub type Context = HashMap<String, Value>;
356
357#[derive(Debug, Clone, PartialEq)]
359pub enum ActionType {
360 Set {
362 field: String,
364 value: Value,
366 },
367 Log {
369 message: String,
371 },
372 Call {
374 function: String,
376 args: Vec<Value>,
378 },
379 MethodCall {
381 object: String,
383 method: String,
385 args: Vec<Value>,
387 },
388 Update {
390 object: String,
392 },
393 Custom {
395 action_type: String,
397 params: HashMap<String, Value>,
399 },
400}