1
2use crate::dna::hel::error::HlxError;
33use crate::ops::utils::{json_to_value, value_to_json};
34use crate::ops::OperatorTrait;
35use crate::dna::atp::value::Value;
36use async_trait::async_trait;
37use std::collections::HashMap;
38use std::sync::{Arc, RwLock};
39
40use chrono::{Local, Utc, TimeZone, Datelike, Timelike, DateTime};
41use chrono_tz::Tz;
42use regex::Regex;
43use base64::{engine::general_purpose, Engine as _};
44use urlencoding;
45use sha2::{Sha256, Digest};
46use md5;
47use uuid::Uuid;
48use meval;
49
50#[derive(Debug, Clone)]
52pub struct ExecutionContext {
53 pub request: Option<RequestData>,
55 pub session: Arc<RwLock<HashMap<String, Value>>>,
57 pub cookies: HashMap<String, String>,
59 pub params: HashMap<String, String>,
61 pub query: HashMap<String, String>,
63}
64
65impl Default for ExecutionContext {
66 fn default() -> Self {
67 Self {
68 request: None,
69 session: Arc::new(RwLock::new(HashMap::new())),
70 cookies: HashMap::new(),
71 params: HashMap::new(),
72 query: HashMap::new(),
73 }
74 }
75}
76
77#[derive(Debug, Clone)]
79pub struct RequestData {
80 pub method: String,
81 pub url: String,
82 pub headers: HashMap<String, String>,
83 pub body: String,
84}
85
86pub struct FundamentalOperators {
88 variables: Arc<RwLock<HashMap<String, Value>>>,
90 context: Arc<ExecutionContext>,
92}
93
94impl FundamentalOperators {
95 pub async fn new_with_context(context: ExecutionContext) -> Result<Self, HlxError> {
97 Ok(Self {
98 variables: Arc::new(RwLock::new(HashMap::new())),
99 context: Arc::new(context),
100 })
101 }
102
103 pub async fn new() -> Result<Self, HlxError> {
105 let context = ExecutionContext::default();
106 Self::new_with_context(context).await
107 }
108
109 pub fn get_variable(&self, name: &str) -> Result<Value, HlxError> {
111 let vars = self.variables.read()
112 .map_err(|_| HlxError::validation_error("RwLock poisoned", "Check concurrency"))?;
113 Ok(vars.get(name).cloned().unwrap_or(Value::Null))
114 }
115
116 pub fn set_variable(&self, name: String, value: Value) -> Result<(), HlxError> {
118 let mut vars = self.variables.write()
119 .map_err(|_| HlxError::validation_error("RwLock poisoned", "Check concurrency"))?;
120 vars.insert(name, value);
121 Ok(())
122 }
123}
124
125impl FundamentalOperators {
126 async fn var_operator(&self, params: &str) -> Result<Value, HlxError> {
131 let params_map = crate::dna::ops::utils::parse_params(params)?;
132
133 let name = params_map
134 .get("name")
135 .and_then(|v| v.as_string())
136 .ok_or_else(|| {
137 HlxError::invalid_parameters(
138 "var",
139 "Missing required parameter `name`",
140 )
141 })?
142 .to_string();
143
144 if let Some(val) = params_map.get("value") {
145 let mut vars = self
147 .variables
148 .write()
149 .map_err(|_| HlxError::validation_error("RwLock poisoned", "Check concurrency"))?;
150 vars.insert(name.clone(), val.clone());
151 Ok(Value::Object({
152 let mut map = HashMap::new();
153 map.insert(
154 "operation".to_string(),
155 Value::String("set".to_string()),
156 );
157 map.insert("name".to_string(), Value::String(name));
158 map.insert("value".to_string(), val.clone());
159 map
160 }))
161 } else {
162 let vars = self
164 .variables
165 .read()
166 .map_err(|_| HlxError::validation_error("RwLock poisoned", "Check concurrency"))?;
167 let stored = vars.get(&name).cloned().unwrap_or(Value::Null);
168 Ok(Value::Object({
169 let mut map = HashMap::new();
170 map.insert(
171 "operation".to_string(),
172 Value::String("get".to_string()),
173 );
174 map.insert("name".to_string(), Value::String(name));
175 map.insert("value".to_string(), stored);
176 map
177 }))
178 }
179 }
180
181 async fn env_operator(&self, params: &str) -> Result<Value, HlxError> {
183 let params_map = crate::dna::ops::utils::parse_params(params)?;
184
185 let var = params_map
186 .get("var")
187 .and_then(|v| v.as_string())
188 .ok_or_else(|| HlxError::invalid_parameters("env", "Missing required `var`"))?;
189
190 match std::env::var(var) {
191 Ok(v) => Ok(Value::String(v)),
192 Err(_) => {
193 if let Some(default) = params_map.get("default").and_then(|v| v.as_string()) {
194 Ok(Value::String(default.to_string()))
195 } else {
196 Err(HlxError::invalid_parameters(
197 "env",
198 &format!("Variable `{}` not set and no `default` supplied", var),
199 ))
200 }
201 }
202 }
203 }
204
205 async fn request_operator(&self, params: &str) -> Result<Value, HlxError> {
209 let params_map = crate::dna::ops::utils::parse_params(params)?;
210 let field = params_map
211 .get("field")
212 .and_then(|v| v.as_string())
213 .unwrap_or("all");
214
215 let req_opt = &self.context.request;
217
218 let req = req_opt
219 .as_ref()
220 .ok_or_else(|| HlxError::invalid_parameters("request", "No request data in context"))?;
221
222 match field {
223 "method" => Ok(Value::String(req.method.clone())),
224 "url" => Ok(Value::String(req.url.clone())),
225 "headers" => {
226 let mut map = HashMap::new();
227 for (k, v) in &req.headers {
228 map.insert(k.clone(), Value::String(v.clone()));
229 }
230 Ok(Value::Object(map))
231 }
232 "body" => Ok(Value::String(req.body.clone())),
233 "all" => {
234 let mut map = HashMap::new();
235 map.insert("method".to_string(), Value::String(req.method.clone()));
236 map.insert("url".to_string(), Value::String(req.url.clone()));
237 let mut hdrs = HashMap::new();
238 for (k, v) in &req.headers {
239 hdrs.insert(k.clone(), Value::String(v.clone()));
240 }
241 map.insert("headers".to_string(), Value::Object(hdrs));
242 map.insert("body".to_string(), Value::String(req.body.clone()));
243 Ok(Value::Object(map))
244 }
245 _ => Err(HlxError::invalid_parameters(
246 "request",
247 "Invalid `field`; allowed: method, url, headers, body, all",
248 )),
249 }
250 }
251
252 async fn session_operator(&self, params: &str) -> Result<Value, HlxError> {
258 let params_map = crate::dna::ops::utils::parse_params(params)?;
259 let action = params_map
260 .get("action")
261 .and_then(|v| v.as_string())
262 .unwrap_or("get");
263
264 match action {
265 "get" => {
266 let key = params_map
267 .get("key")
268 .and_then(|v| v.as_string())
269 .ok_or_else(|| {
270 HlxError::invalid_parameters("session", "`key` required for get")
271 })?;
272
273 let sess = self
274 .context
275 .session
276 .read()
277 .map_err(|_| HlxError::validation_error("RwLock poisoned", ""))?;
278 let value = sess.get(key).cloned().unwrap_or(Value::Null);
279 Ok(Value::Object({
280 let mut map = HashMap::new();
281 map.insert("key".to_string(), Value::String(key.to_string()));
282 map.insert("value".to_string(), value);
283 map
284 }))
285 }
286 "set" => {
287 let key = params_map
288 .get("key")
289 .and_then(|v| v.as_string())
290 .ok_or_else(|| {
291 HlxError::invalid_parameters("session", "`key` required for set")
292 })?;
293 let value = params_map
294 .get("value")
295 .ok_or_else(|| {
296 HlxError::invalid_parameters("session", "`value` required for set")
297 })?;
298
299 let mut sess = self
300 .context
301 .session
302 .write()
303 .map_err(|_| HlxError::validation_error("RwLock poisoned", ""))?;
304 sess.insert(key.to_string(), value.clone());
305
306 Ok(Value::Object({
307 let mut map = HashMap::new();
308 map.insert("operation".to_string(), Value::String("set".to_string()));
309 map.insert("key".to_string(), Value::String(key.to_string()));
310 map.insert("value".to_string(), value.clone());
311 map.insert("success".to_string(), Value::Bool(true));
312 map
313 }))
314 }
315 _ => Err(HlxError::invalid_parameters(
316 "session",
317 "`action` must be either `get` or `set`",
318 )),
319 }
320 }
321
322 async fn cookie_operator(&self, params: &str) -> Result<Value, HlxError> {
324 let params_map = crate::dna::ops::utils::parse_params(params)?;
325 let name = params_map
326 .get("name")
327 .and_then(|v| v.as_string())
328 .unwrap_or("session_id");
329
330 let cookie_val = self
331 .context
332 .cookies
333 .get(name)
334 .cloned()
335 .unwrap_or_else(|| "".to_string());
336
337 Ok(Value::Object({
338 let mut map = HashMap::new();
339 map.insert("name".to_string(), Value::String(name.to_string()));
340 map.insert("value".to_string(), Value::String(cookie_val));
341 map
342 }))
343 }
344
345 async fn header_operator(&self, params: &str) -> Result<Value, HlxError> {
347 let params_map = crate::dna::ops::utils::parse_params(params)?;
348 let name = params_map
349 .get("name")
350 .and_then(|v| v.as_string())
351 .unwrap_or("User-Agent");
352
353 let req_opt = &self.context.request;
354 let req = req_opt
355 .as_ref()
356 .ok_or_else(|| HlxError::invalid_parameters("header", "No request in context"))?;
357
358 let val = req.headers.get(name).cloned().unwrap_or_else(|| "".to_string());
359 Ok(Value::Object({
360 let mut map = HashMap::new();
361 map.insert("name".to_string(), Value::String(name.to_string()));
362 map.insert("value".to_string(), Value::String(val));
363 map
364 }))
365 }
366
367 async fn param_operator(&self, params: &str) -> Result<Value, HlxError> {
369 let params_map = crate::dna::ops::utils::parse_params(params)?;
370 let name = params_map
371 .get("name")
372 .and_then(|v| v.as_string())
373 .ok_or_else(|| HlxError::invalid_parameters("param", "`name` required"))?;
374
375 let val = self
376 .context
377 .params
378 .get(name)
379 .cloned()
380 .unwrap_or_else(|| "".to_string());
381
382 Ok(Value::Object({
383 let mut map = HashMap::new();
384 map.insert("name".to_string(), Value::String(name.to_string()));
385 map.insert("value".to_string(), Value::String(val));
386 map
387 }))
388 }
389
390 async fn query_operator(&self, params: &str) -> Result<Value, HlxError> {
392 let params_map = crate::dna::ops::utils::parse_params(params)?;
393 let name = params_map
394 .get("name")
395 .and_then(|v| v.as_string())
396 .ok_or_else(|| HlxError::invalid_parameters("query", "`name` required"))?;
397
398 let val = self
399 .context
400 .query
401 .get(name)
402 .cloned()
403 .unwrap_or_else(|| "".to_string());
404
405 Ok(Value::Object({
406 let mut map = HashMap::new();
407 map.insert("name".to_string(), Value::String(name.to_string()));
408 map.insert("value".to_string(), Value::String(val));
409 map
410 }))
411 }
412
413 async fn date_operator(&self, params: &str) -> Result<Value, HlxError> {
415 let params_map = crate::dna::ops::utils::parse_params(params)?;
416
417 let format = params_map.get("format")
418 .and_then(|v| v.as_string())
419 .unwrap_or("%Y-%m-%d");
420
421 use chrono::{Local, Datelike, Timelike};
422
423 let now = Local::now();
424 let formatted = now.format(format).to_string();
425
426 Ok(Value::String(formatted))
427 }
428
429 async fn time_operator(&self, params: &str) -> Result<Value, HlxError> {
430 let params_map = crate::dna::ops::utils::parse_params(params)?;
431
432 let format = params_map.get("format")
433 .and_then(|v| v.as_string())
434 .unwrap_or("%H:%M:%S");
435
436 use chrono::Local;
437
438 let now = Local::now();
439 let formatted = now.format(format).to_string();
440
441 Ok(Value::String(formatted))
442 }
443
444 async fn timestamp_operator(&self, _params: &str) -> Result<Value, HlxError> {
445 use chrono::Utc;
446 let timestamp = Utc::now().timestamp();
447 Ok(Value::Number(timestamp as f64))
448 }
449
450 async fn now_operator(&self, _params: &str) -> Result<Value, HlxError> {
451 use chrono::{Utc, Local};
452 let now = Local::now().to_rfc3339();
453 Ok(Value::String(now))
454 }
455
456 async fn format_operator(&self, params: &str) -> Result<Value, HlxError> {
457 let params_map = crate::dna::ops::utils::parse_params(params)?;
458
459 let input = params_map.get("input")
460 .and_then(|v| v.as_string())
461 .unwrap_or("now");
462
463 let format = params_map.get("format")
464 .and_then(|v| v.as_string())
465 .unwrap_or("%Y-%m-%d %H:%M:%S");
466
467 use chrono::{Local, Utc, TimeZone};
468
469 let datetime = if input == "now" {
470 Local::now()
471 } else if let Ok(ts) = input.parse::<i64>() {
472 match Local.timestamp_opt(ts, 0) {
473 chrono::LocalResult::Single(dt) => dt,
474 _ => Local::now(),
475 }
476 } else {
477 match chrono::DateTime::parse_from_rfc3339(input) {
479 Ok(dt) => dt.with_timezone(&Local),
480 Err(_) => Local::now(),
481 }
482 };
483
484 let result = datetime.format(format).to_string();
485 Ok(Value::String(result))
486 }
487
488 async fn timezone_operator(&self, params: &str) -> Result<Value, HlxError> {
489 let params_map = crate::dna::ops::utils::parse_params(params)?;
490
491 let tz = params_map.get("tz")
492 .and_then(|v| v.as_string())
493 .unwrap_or("UTC");
494
495 let input = params_map.get("input")
496 .and_then(|v| v.as_string())
497 .unwrap_or("now");
498
499 use chrono::{Local, Utc, TimeZone};
500
501 let datetime = if input == "now" {
502 Utc::now()
503 } else if let Ok(ts) = input.parse::<i64>() {
504 match Utc.timestamp_opt(ts, 0) {
505 chrono::LocalResult::Single(dt) => dt,
506 _ => Utc::now(),
507 }
508 } else {
509 match chrono::DateTime::parse_from_rfc3339(input) {
510 Ok(dt) => dt.with_timezone(&Utc),
511 Err(_) => Utc::now(),
512 }
513 };
514
515 let result = format!("{} ({})", datetime.to_rfc3339(), tz);
518 Ok(Value::String(result))
519 }
520
521 async fn string_operator(&self, params: &str) -> Result<Value, HlxError> {
523 let params_map = crate::dna::ops::utils::parse_params(params)?;
524
525 let input = params_map.get("input")
526 .and_then(|v| v.as_string())
527 .ok_or_else(|| HlxError::invalid_parameters("string", "Missing 'input' parameter"))?;
528
529 let operation = params_map.get("operation")
530 .and_then(|v| v.as_string())
531 .ok_or_else(|| HlxError::invalid_parameters("string", "Missing 'operation' parameter"))?;
532
533 match operation {
534 "upper" => Ok(Value::String(input.to_uppercase())),
535 "lower" => Ok(Value::String(input.to_lowercase())),
536 "capitalize" => {
537 let mut chars = input.chars();
538 match chars.next() {
539 None => Ok(Value::String(String::new())),
540 Some(first) => Ok(Value::String(first.to_uppercase().collect::<String>() + chars.as_str())),
541 }
542 },
543 "reverse" => Ok(Value::String(input.chars().rev().collect())),
544 "length" => Ok(Value::Number(input.len() as f64)),
545 "trim" => Ok(Value::String(input.trim().to_string())),
546 "substring" => {
547 let start = params_map.get("start").and_then(|v| v.as_number()).unwrap_or(0.0) as usize;
548 let len = params_map.get("len").and_then(|v| v.as_number()).unwrap_or((input.len() - start) as f64) as usize;
549 let substr: String = input.chars().skip(start).take(len).collect();
550 Ok(Value::String(substr))
551 }
552 _ => Err(HlxError::invalid_parameters("string", "Invalid 'operation'")),
553 }
554 }
555
556 async fn regex_operator(&self, params: &str) -> Result<Value, HlxError> {
557 let params_map = crate::dna::ops::utils::parse_params(params)?;
558
559 let input = params_map.get("input")
560 .and_then(|v| v.as_string())
561 .ok_or_else(|| HlxError::invalid_parameters("regex", "Missing 'input' parameter"))?;
562
563 let pattern = params_map.get("pattern")
564 .and_then(|v| v.as_string())
565 .ok_or_else(|| HlxError::invalid_parameters("regex", "Missing 'pattern' parameter"))?;
566
567 let operation = params_map.get("operation")
568 .and_then(|v| v.as_string())
569 .ok_or_else(|| HlxError::invalid_parameters("regex", "Missing 'operation' parameter"))?;
570
571 let re = regex::Regex::new(pattern)
572 .map_err(|e| HlxError::validation_error(format!("Invalid regex pattern: {}", e), "Check regex syntax"))?;
573
574 match operation {
575 "match" => Ok(Value::Bool(re.is_match(input))),
576 "find" => {
577 Ok(Value::String(re.find(input).map(|m| m.as_str()).unwrap_or("").to_string()))
578 },
579 "replace" => {
580 let replacement = params_map.get("replacement")
581 .and_then(|v| v.as_string())
582 .unwrap_or("");
583 Ok(Value::String(re.replace_all(input, replacement).to_string()))
584 },
585 "captures" => {
586 let captures: Vec<Value> = re.captures_iter(input)
587 .map(|cap| Value::Array(cap.iter().skip(1).map(|m| Value::String(m.unwrap().as_str().to_string())).collect()))
588 .collect();
589 Ok(Value::Array(captures))
590 },
591 _ => Err(HlxError::invalid_parameters("regex", "Invalid 'operation'")),
592 }
593 }
594
595 async fn json_operator(&self, params: &str) -> Result<Value, HlxError> {
596 let params_map = crate::dna::ops::utils::parse_params(params)?;
597
598 let operation = params_map.get("operation")
599 .and_then(|v| v.as_string())
600 .ok_or_else(|| HlxError::invalid_parameters("json", "Missing 'operation' parameter"))?;
601
602 match operation {
603 "parse" => {
604 let input = params_map.get("input")
605 .and_then(|v| v.as_string())
606 .ok_or_else(|| HlxError::invalid_parameters("json", "Missing 'input' parameter for parse"))?;
607
608 let parsed: serde_json::Value = serde_json::from_str(input)
609 .map_err(|e| HlxError::json_error(format!("JSON parse error: {}", e), "Provide valid JSON"))?;
610
611 Ok(json_to_value(&parsed))
612 },
613 "stringify" => {
614 let input = params_map.get("input")
615 .ok_or_else(|| HlxError::invalid_parameters("json", "Missing 'input' parameter for stringify"))?;
616
617 let json_value = value_to_json(input);
618 let json_str = serde_json::to_string(&json_value)
619 .map_err(|e| HlxError::json_error(format!("JSON stringify error: {}", e), "Check input value"))?;
620
621 Ok(Value::String(json_str))
622 },
623 _ => Err(HlxError::invalid_parameters("json", "Invalid 'operation'")),
624 }
625 }
626
627 async fn base64_operator(&self, params: &str) -> Result<Value, HlxError> {
628 let params_map = crate::dna::ops::utils::parse_params(params)?;
629
630 let input = params_map.get("input")
631 .and_then(|v| v.as_string())
632 .ok_or_else(|| HlxError::invalid_parameters("base64", "Missing 'input' parameter"))?;
633
634 let operation = params_map.get("operation")
635 .and_then(|v| v.as_string())
636 .ok_or_else(|| HlxError::invalid_parameters("base64", "Missing 'operation' parameter"))?;
637
638 match operation {
639 "encode" => {
640 use base64::{Engine as _, engine::general_purpose};
641 Ok(Value::String(general_purpose::STANDARD.encode(input)))
642 },
643 "decode" => {
644 use base64::{Engine as _, engine::general_purpose};
645 let decoded = general_purpose::STANDARD.decode(input)
646 .map_err(|e| HlxError::base64_error(e.to_string(), "Provide valid base64 string"))?;
647 String::from_utf8(decoded)
648 .map(Value::String)
649 .map_err(|_| HlxError::base64_error("Decoded bytes are not valid UTF-8", "Ensure input is valid base64-encoded UTF-8"))
650 },
651 _ => Err(HlxError::invalid_parameters("base64", "Invalid 'operation'")),
652 }
653 }
654
655 async fn url_operator(&self, params: &str) -> Result<Value, HlxError> {
656 let params_map = crate::dna::ops::utils::parse_params(params)?;
657
658 let input = params_map.get("input")
659 .and_then(|v| v.as_string())
660 .ok_or_else(|| HlxError::invalid_parameters("url", "Missing 'input' parameter"))?;
661
662 let operation = params_map.get("operation")
663 .and_then(|v| v.as_string())
664 .ok_or_else(|| HlxError::invalid_parameters("url", "Missing 'operation' parameter"))?;
665
666 match operation {
667 "encode" => Ok(Value::String(urlencoding::encode(input).to_string())),
668 "decode" => {
669 urlencoding::decode(input)
670 .map(|s| Value::String(s.to_string()))
671 .map_err(|e| HlxError::validation_error(format!("URL decode error: {}", e), "Provide valid URL-encoded string"))
672 },
673 _ => Err(HlxError::invalid_parameters("url", "Invalid 'operation'")),
674 }
675 }
676
677 async fn hash_operator(&self, params: &str) -> Result<Value, HlxError> {
678 let params_map = crate::dna::ops::utils::parse_params(params)?;
679
680 let input = params_map.get("input")
681 .and_then(|v| v.as_string())
682 .ok_or_else(|| HlxError::invalid_parameters("hash", "Missing 'input' parameter"))?;
683
684 let algorithm = params_map.get("algorithm")
685 .and_then(|v| v.as_string())
686 .ok_or_else(|| HlxError::invalid_parameters("hash", "Missing 'algorithm' parameter"))?;
687
688 match algorithm {
689 "sha256" => {
690 use sha2::{Sha256, Digest};
691 let mut hasher = Sha256::new();
692 hasher.update(input);
693 Ok(Value::String(format!("{:x}", hasher.finalize())))
694 },
695 "md5" => {
696 Ok(Value::String(format!("{:x}", md5::compute(input))))
697 },
698 _ => Err(HlxError::invalid_parameters("hash", "Invalid 'algorithm' - supported: sha256, md5")),
699 }
700 }
701
702 async fn uuid_operator(&self, params: &str) -> Result<Value, HlxError> {
703 let params_map = crate::dna::ops::utils::parse_params(params)?;
704
705 let version = params_map.get("version")
706 .and_then(|v| v.as_string())
707 .unwrap_or("v4");
708
709 match version {
710 "v4" => Ok(Value::String(uuid::Uuid::new_v4().to_string())),
711 _ => Err(HlxError::invalid_parameters("uuid", "Only v4 is supported")),
712 }
713 }
714
715 async fn if_operator(&self, params: &str) -> Result<Value, HlxError> {
719 let params_map = crate::dna::ops::utils::parse_params(params)?;
720
721 let condition = params_map.get("condition")
722 .and_then(|v| v.as_boolean())
723 .ok_or_else(|| HlxError::invalid_parameters("if", "Missing 'condition' parameter"))?;
724
725 let then_value = params_map.get("then")
726 .cloned()
727 .ok_or_else(|| HlxError::invalid_parameters("if", "Missing 'then' parameter"))?;
728
729 let else_value = params_map.get("else").cloned().unwrap_or(Value::Null);
730
731 Ok(if condition { then_value } else { else_value })
732 }
733
734 async fn switch_operator(&self, params: &str) -> Result<Value, HlxError> {
738 let params_map = crate::dna::ops::utils::parse_params(params)?;
739
740 let value = params_map.get("value")
741 .ok_or_else(|| HlxError::invalid_parameters("switch", "Missing 'value' parameter"))?;
742
743 let cases = params_map.get("cases")
744 .and_then(|v| v.as_array())
745 .ok_or_else(|| HlxError::invalid_parameters("switch", "Missing 'cases' parameter"))?;
746
747 for case in cases {
748 if let Value::Object(case_obj) = case {
749 if let (Some(match_val), Some(result_val)) = (case_obj.get("match"), case_obj.get("result")) {
750 if match_val == value {
751 return Ok(result_val.clone());
752 }
753 }
754 }
755 }
756
757 Ok(Value::Null)
759 }
760
761 async fn case_operator(&self, params: &str) -> Result<Value, HlxError> {
762 let params_map = crate::dna::ops::utils::parse_params(params)?;
763
764 let value = params_map.get("value")
765 .and_then(|v| v.as_string())
766 .unwrap_or("case1")
767 .to_string();
768
769 let match_value = params_map.get("match")
770 .and_then(|v| v.as_string())
771 .unwrap_or("case1")
772 .to_string();
773
774 let result = value == match_value;
775
776 Ok(Value::Object({
777 let mut map = HashMap::new();
778 map.insert("value".to_string(), Value::String(value.to_string()));
779 map.insert("match".to_string(), Value::String(match_value.to_string()));
780 map.insert("result".to_string(), Value::Bool(result));
781 map
782 }))
783 }
784
785 async fn default_operator(&self, params: &str) -> Result<Value, HlxError> {
786 let params_map = crate::dna::ops::utils::parse_params(params)?;
787
788 let value = params_map.get("value")
789 .and_then(|v| v.as_string())
790 .unwrap_or("");
791
792 let default = params_map.get("default")
793 .and_then(|v| v.as_string())
794 .unwrap_or("default");
795
796 let result = if value.is_empty() {
797 Value::String(default.to_string())
798 } else {
799 Value::String(value.to_string())
800 };
801
802 Ok(Value::Object({
803 let mut map = HashMap::new();
804 map.insert("value".to_string(), Value::String(value.to_string()));
805 map.insert("default".to_string(), Value::String(default.to_string()));
806 map.insert("result".to_string(), result);
807 map
808 }))
809 }
810
811 async fn and_operator(&self, params: &str) -> Result<Value, HlxError> {
812 let params_map = crate::dna::ops::utils::parse_params(params)?;
813
814 let a = params_map.get("a")
815 .and_then(|v| v.as_boolean())
816 .unwrap_or(false);
817
818 let b = params_map.get("b")
819 .and_then(|v| v.as_boolean())
820 .unwrap_or(false);
821
822 let result = a && b;
823
824 Ok(Value::Object({
825 let mut map = HashMap::new();
826 map.insert("a".to_string(), Value::Bool(a));
827 map.insert("b".to_string(), Value::Bool(b));
828 map.insert("result".to_string(), Value::Bool(result));
829 map
830 }))
831 }
832
833 async fn or_operator(&self, params: &str) -> Result<Value, HlxError> {
834 let params_map = crate::dna::ops::utils::parse_params(params)?;
835
836 let a = params_map.get("a")
837 .and_then(|v| v.as_boolean())
838 .unwrap_or(false);
839
840 let b = params_map.get("b")
841 .and_then(|v| v.as_boolean())
842 .unwrap_or(false);
843
844 let result = a || b;
845
846 Ok(Value::Object({
847 let mut map = HashMap::new();
848 map.insert("a".to_string(), Value::Bool(a));
849 map.insert("b".to_string(), Value::Bool(b));
850 map.insert("result".to_string(), Value::Bool(result));
851 map
852 }))
853 }
854
855 async fn not_operator(&self, params: &str) -> Result<Value, HlxError> {
856 let params_map = crate::dna::ops::utils::parse_params(params)?;
857
858 let value = params_map.get("value")
859 .and_then(|v| v.as_boolean())
860 .unwrap_or(false);
861
862 let result = !value;
863
864 Ok(Value::Object({
865 let mut map = HashMap::new();
866 map.insert("value".to_string(), Value::Bool(value));
867 map.insert("result".to_string(), Value::Bool(result));
868 map
869 }))
870 }
871
872 async fn math_operator(&self, params: &str) -> Result<Value, HlxError> {
877 let params_map = crate::dna::ops::utils::parse_params(params)?;
878
879 let operation = params_map.get("operation")
880 .and_then(|v| v.as_string())
881 .ok_or_else(|| HlxError::invalid_parameters("math", "Missing 'operation' parameter"))?;
882
883 let a = params_map.get("a")
884 .and_then(|v| v.as_number())
885 .ok_or_else(|| HlxError::invalid_parameters("math", "Missing 'a' parameter"))?;
886
887 let b = params_map.get("b")
888 .and_then(|v| v.as_number())
889 .ok_or_else(|| HlxError::invalid_parameters("math", "Missing 'b' parameter"))?;
890
891 match operation {
892 "add" => Ok(Value::Number(a + b)),
893 "sub" => Ok(Value::Number(a - b)),
894 "mul" => Ok(Value::Number(a * b)),
895 "div" => {
896 if b == 0.0 {
897 Err(HlxError::validation_error("Division by zero", "Provide non-zero 'b' parameter"))
898 } else {
899 Ok(Value::Number(a / b))
900 }
901 },
902 "mod" => {
903 if b == 0.0 {
904 Err(HlxError::validation_error("Modulo by zero", "Provide non-zero 'b' parameter"))
905 } else {
906 Ok(Value::Number(a % b))
907 }
908 },
909 "pow" => Ok(Value::Number(a.powf(b))),
910 _ => Err(HlxError::invalid_parameters("math", "Invalid operation - supported: add, sub, mul, div, mod, pow")),
911 }
912 }
913
914 async fn calc_operator(&self, params: &str) -> Result<Value, HlxError> {
918 let params_map = crate::dna::ops::utils::parse_params(params)?;
919
920 let expression = params_map.get("expression")
921 .and_then(|v| v.as_string())
922 .ok_or_else(|| HlxError::invalid_parameters("calc", "Missing 'expression' parameter"))?;
923
924 let result = meval::eval_str(expression)
925 .map_err(|e| HlxError::validation_error(
926 format!("Expression evaluation error: {}", e),
927 "Provide a valid mathematical expression"
928 ))?;
929
930 Ok(Value::Number(result))
931 }
932
933 async fn min_operator(&self, params: &str) -> Result<Value, HlxError> {
934 let params_map = crate::dna::ops::utils::parse_params(params)?;
935
936 let empty_vec: Vec<Value> = vec![];
937 let values = params_map.get("values")
938 .and_then(|v| v.as_array())
939 .unwrap_or(&empty_vec);
940
941 let numbers: Vec<f64> = values.iter()
942 .filter_map(|v| v.as_number())
943 .collect();
944
945 let result = numbers.iter().fold(f64::INFINITY, |a, &b| a.min(b));
946
947 Ok(Value::Object({
948 let mut map = HashMap::new();
949 map.insert("values".to_string(), Value::Array(values.to_vec()));
950 map.insert("min".to_string(), Value::Number(if result == f64::INFINITY { 0.0 } else { result }));
951 map
952 }))
953 }
954
955 async fn max_operator(&self, params: &str) -> Result<Value, HlxError> {
956 let params_map = crate::dna::ops::utils::parse_params(params)?;
957
958 let empty_vec: Vec<Value> = vec![];
959 let values = params_map.get("values")
960 .and_then(|v| v.as_array())
961 .unwrap_or(&empty_vec);
962
963 let numbers: Vec<f64> = values.iter()
964 .filter_map(|v| v.as_number())
965 .collect();
966
967 let result = numbers.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
968
969 Ok(Value::Object({
970 let mut map = HashMap::new();
971 map.insert("values".to_string(), Value::Array(values.to_vec()));
972 map.insert("max".to_string(), Value::Number(if result == f64::NEG_INFINITY { 0.0 } else { result }));
973 map
974 }))
975 }
976
977 async fn avg_operator(&self, params: &str) -> Result<Value, HlxError> {
978 let params_map = crate::dna::ops::utils::parse_params(params)?;
979
980 let empty_vec: Vec<Value> = vec![];
981 let values = params_map.get("values")
982 .and_then(|v| v.as_array())
983 .unwrap_or(&empty_vec);
984
985 let numbers: Vec<f64> = values.iter()
986 .filter_map(|v| v.as_number())
987 .collect();
988
989 let result = if numbers.is_empty() {
990 0.0
991 } else {
992 numbers.iter().sum::<f64>() / numbers.len() as f64
993 };
994
995 Ok(Value::Object({
996 let mut map = HashMap::new();
997 map.insert("values".to_string(), Value::Array(values.to_vec()));
998 map.insert("average".to_string(), Value::Number(result));
999 map
1000 }))
1001 }
1002
1003 async fn sum_operator(&self, params: &str) -> Result<Value, HlxError> {
1004 let params_map = crate::dna::ops::utils::parse_params(params)?;
1005
1006 let empty_vec: Vec<Value> = vec![];
1007 let values = params_map.get("values")
1008 .and_then(|v| v.as_array())
1009 .unwrap_or(&empty_vec);
1010
1011 let numbers: Vec<f64> = values.iter()
1012 .filter_map(|v| v.as_number())
1013 .collect();
1014
1015 let result = numbers.iter().sum::<f64>();
1016
1017 Ok(Value::Object({
1018 let mut map = HashMap::new();
1019 map.insert("values".to_string(), Value::Array(values.to_vec()));
1020 map.insert("sum".to_string(), Value::Number(result));
1021 map
1022 }))
1023 }
1024
1025 async fn round_operator(&self, params: &str) -> Result<Value, HlxError> {
1026 let params_map = crate::dna::ops::utils::parse_params(params)?;
1027
1028 let value = params_map.get("value")
1029 .and_then(|v| v.as_number())
1030 .unwrap_or(3.14159);
1031
1032 let decimals = params_map.get("decimals")
1033 .and_then(|v| v.as_number())
1034 .unwrap_or(2.0) as i32;
1035
1036 let multiplier = 10.0_f64.powi(decimals);
1037 let result = (value * multiplier).round() / multiplier;
1038
1039 Ok(Value::Object({
1040 let mut map = HashMap::new();
1041 map.insert("value".to_string(), Value::Number(value));
1042 map.insert("decimals".to_string(), Value::Number(decimals as f64));
1043 map.insert("rounded".to_string(), Value::Number(result));
1044 map
1045 }))
1046 }
1047
1048 async fn array_operator(&self, params: &str) -> Result<Value, HlxError> {
1050 let params_map = crate::dna::ops::utils::parse_params(params)?;
1051
1052 let operation = params_map.get("operation")
1053 .and_then(|v| v.as_string())
1054 .unwrap_or("create")
1055 .to_string();
1056
1057 let empty_vec: Vec<Value> = vec![];
1058 let items = params_map.get("items")
1059 .and_then(|v| v.as_array())
1060 .unwrap_or(&empty_vec);
1061
1062 match operation.as_str() {
1063 "create" => Ok(Value::Array(items.to_vec())),
1064 "push" => {
1065 let item = params_map.get("item").cloned().unwrap_or(Value::String("new_item".to_string()));
1066 let mut new_array = items.to_vec();
1067 new_array.push(item);
1068 Ok(Value::Array(new_array))
1069 },
1070 "pop" => {
1071 let mut new_array = items.to_vec();
1072 let popped = new_array.pop().unwrap_or(Value::String("".to_string()));
1073 Ok(Value::Object({
1074 let mut map = HashMap::new();
1075 map.insert("array".to_string(), Value::Array(new_array));
1076 map.insert("popped".to_string(), popped);
1077 map
1078 }))
1079 },
1080 _ => Ok(Value::Array(items.to_vec())),
1081 }
1082 }
1083
1084 async fn map_operator(&self, params: &str) -> Result<Value, HlxError> {
1085 let params_map = crate::dna::ops::utils::parse_params(params)?;
1086
1087 let empty_vec: Vec<Value> = vec![];
1088 let array = params_map.get("array")
1089 .and_then(|v| v.as_array())
1090 .unwrap_or(&empty_vec);
1091
1092 let transform = params_map.get("transform")
1093 .and_then(|v| v.as_string())
1094 .unwrap_or("upper")
1095 .to_string();
1096
1097 let result: Vec<Value> = array.iter().map(|item| {
1098 match transform.as_str() {
1099 "upper" => {
1100 if let Some(s) = item.as_string() {
1101 Value::String(s.to_uppercase())
1102 } else {
1103 item.clone()
1104 }
1105 },
1106 "lower" => {
1107 if let Some(s) = item.as_string() {
1108 Value::String(s.to_lowercase())
1109 } else {
1110 item.clone()
1111 }
1112 },
1113 "double" => {
1114 if let Some(n) = item.as_number() {
1115 Value::Number(n * 2.0)
1116 } else {
1117 item.clone()
1118 }
1119 },
1120 _ => item.clone(),
1121 }
1122 }).collect();
1123
1124 Ok(Value::Object({
1125 let mut map = HashMap::new();
1126 map.insert("original".to_string(), Value::Array(array.to_vec()));
1127 map.insert("transform".to_string(), Value::String(transform.to_string()));
1128 map.insert("result".to_string(), Value::Array(result));
1129 map
1130 }))
1131 }
1132
1133 async fn filter_operator(&self, params: &str) -> Result<Value, HlxError> {
1134 let params_map = crate::dna::ops::utils::parse_params(params)?;
1135
1136 let empty_vec: Vec<Value> = vec![];
1137 let array = params_map.get("array")
1138 .and_then(|v| v.as_array())
1139 .unwrap_or(&empty_vec);
1140
1141 let condition = params_map.get("condition")
1142 .and_then(|v| v.as_string())
1143 .unwrap_or("not_empty")
1144 .to_string();
1145
1146 let result: Vec<Value> = array.iter().filter(|item| {
1147 match condition.as_str() {
1148 "not_empty" => {
1149 if let Some(s) = item.as_string() {
1150 !s.is_empty()
1151 } else {
1152 true
1153 }
1154 },
1155 "positive" => {
1156 if let Some(n) = item.as_number() {
1157 n > 0.0
1158 } else {
1159 false
1160 }
1161 },
1162 "negative" => {
1163 if let Some(n) = item.as_number() {
1164 n < 0.0
1165 } else {
1166 false
1167 }
1168 },
1169 _ => true,
1170 }
1171 }).cloned().collect();
1172
1173 Ok(Value::Object({
1174 let mut map = HashMap::new();
1175 map.insert("original".to_string(), Value::Array(array.to_vec()));
1176 map.insert("condition".to_string(), Value::String(condition.to_string()));
1177 map.insert("result".to_string(), Value::Array(result));
1178 map
1179 }))
1180 }
1181
1182 async fn sort_operator(&self, params: &str) -> Result<Value, HlxError> {
1183 let params_map = crate::dna::ops::utils::parse_params(params)?;
1184
1185 let empty_vec: Vec<Value> = vec![];
1186 let array = params_map.get("array")
1187 .and_then(|v| v.as_array())
1188 .unwrap_or(&empty_vec);
1189
1190 let order = params_map.get("order")
1191 .and_then(|v| v.as_string())
1192 .unwrap_or("asc")
1193 .to_string();
1194
1195 let mut result = array.to_vec();
1196 result.sort_by(|a, b| {
1197 match order.as_str() {
1198 "asc" => {
1199 let a_str = a.as_string().unwrap_or("");
1200 let b_str = b.as_string().unwrap_or("");
1201 a_str.partial_cmp(b_str).unwrap_or(std::cmp::Ordering::Equal)
1202 },
1203 "desc" => {
1204 let a_str = a.as_string().unwrap_or("");
1205 let b_str = b.as_string().unwrap_or("");
1206 b_str.partial_cmp(a_str).unwrap_or(std::cmp::Ordering::Equal)
1207 },
1208 _ => {
1209 let a_str = a.as_string().unwrap_or("");
1210 let b_str = b.as_string().unwrap_or("");
1211 a_str.partial_cmp(b_str).unwrap_or(std::cmp::Ordering::Equal)
1212 },
1213 }
1214 });
1215
1216 Ok(Value::Object({
1217 let mut map = HashMap::new();
1218 map.insert("original".to_string(), Value::Array(array.to_vec()));
1219 map.insert("order".to_string(), Value::String(order.to_string()));
1220 map.insert("result".to_string(), Value::Array(result));
1221 map
1222 }))
1223 }
1224
1225 async fn join_operator(&self, params: &str) -> Result<Value, HlxError> {
1226 let params_map = crate::dna::ops::utils::parse_params(params)?;
1227
1228 let empty_vec: Vec<Value> = vec![];
1229 let array = params_map.get("array")
1230 .and_then(|v| v.as_array())
1231 .unwrap_or(&empty_vec);
1232
1233 let separator = params_map.get("separator")
1234 .and_then(|v| v.as_string())
1235 .unwrap_or(",")
1236 .to_string();
1237
1238 let strings: Vec<String> = array.iter()
1239 .filter_map(|v| v.as_string().map(|s| s.to_string()))
1240 .collect();
1241
1242 let result = strings.join(&separator);
1243
1244 Ok(Value::Object({
1245 let mut map = HashMap::new();
1246 map.insert("array".to_string(), Value::Array(array.to_vec()));
1247 map.insert("separator".to_string(), Value::String(separator.to_string()));
1248 map.insert("result".to_string(), Value::String(result));
1249 map
1250 }))
1251 }
1252
1253 async fn split_operator(&self, params: &str) -> Result<Value, HlxError> {
1254 let params_map = crate::dna::ops::utils::parse_params(params)?;
1255
1256 let input = params_map.get("input")
1257 .and_then(|v| v.as_string())
1258 .unwrap_or("a,b,c,d")
1259 .to_string();
1260
1261 let separator = params_map.get("separator")
1262 .and_then(|v| v.as_string())
1263 .unwrap_or(",")
1264 .to_string();
1265
1266 let result: Vec<Value> = input.split(&separator)
1267 .map(|s| Value::String(s.to_string()))
1268 .collect();
1269
1270 Ok(Value::Object({
1271 let mut map = HashMap::new();
1272 map.insert("input".to_string(), Value::String(input.to_string()));
1273 map.insert("separator".to_string(), Value::String(separator.to_string()));
1274 map.insert("result".to_string(), Value::Array(result));
1275 map
1276 }))
1277 }
1278
1279 async fn length_operator(&self, params: &str) -> Result<Value, HlxError> {
1280 let params_map = crate::dna::ops::utils::parse_params(params)?;
1281
1282 let input = params_map.get("input")
1283 .cloned()
1284 .unwrap_or(Value::String("hello".to_string()));
1285
1286 let length = match &input {
1287 Value::String(s) => s.len(),
1288 Value::Array(a) => a.len(),
1289 Value::Object(o) => o.len(),
1290 _ => 0,
1291 };
1292
1293 Ok(Value::Object({
1294 let mut map = HashMap::new();
1295 map.insert("input".to_string(), input.clone());
1296 map.insert("length".to_string(), Value::Number(length as f64));
1297 map
1298 }))
1299 }
1300
1301 async fn exec_operator(&self, params: &str) -> Result<Value, HlxError> {
1308 use std::process::{Command, Output};
1309 use std::time::Duration;
1310 use tokio::time::timeout;
1311
1312 let params_map = crate::dna::ops::utils::parse_params(params)?;
1313
1314 let command = params_map
1315 .get("command")
1316 .and_then(|v| v.as_string())
1317 .ok_or_else(|| HlxError::invalid_parameters("exec", "Missing 'command' parameter"))?;
1318
1319 let timeout_secs = params_map
1320 .get("timeout")
1321 .and_then(|v| v.as_number())
1322 .unwrap_or(30.0) as u64;
1323
1324 let working_dir = params_map
1325 .get("working_dir")
1326 .and_then(|v| v.as_string());
1327
1328 let parts: Vec<&str> = if command.contains(' ') {
1330 vec!["sh", "-c", &command]
1332 } else {
1333 command.split_whitespace().collect()
1334 };
1335
1336 let mut cmd = Command::new(parts[0]);
1337 if parts.len() > 1 {
1338 cmd.args(&parts[1..]);
1339 }
1340
1341 if let Some(dir) = working_dir {
1342 cmd.current_dir(dir);
1343 }
1344
1345 let result = tokio::task::spawn_blocking(move || {
1347 cmd.output()
1348 }).await;
1349
1350 match result {
1351 Ok(Ok(output)) => {
1352 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
1353 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
1354 let exit_code = output.status.code().unwrap_or(-1);
1355
1356 Ok(Value::Object({
1357 let mut map = HashMap::new();
1358 map.insert("stdout".to_string(), Value::String(stdout.trim().to_string()));
1359 map.insert("stderr".to_string(), Value::String(stderr.trim().to_string()));
1360 map.insert("exit_code".to_string(), Value::Number(exit_code as f64));
1361 map.insert("success".to_string(), Value::Bool(exit_code == 0));
1362 map
1363 }))
1364 }
1365 Ok(Err(e)) => {
1366 Err(HlxError::execution_error(
1367 format!("Command execution failed: {}", e),
1368 "Check command syntax and permissions"
1369 ))
1370 }
1371 Err(e) => {
1372 Err(HlxError::execution_error(
1373 format!("Task execution failed: {}", e),
1374 "Internal execution error"
1375 ))
1376 }
1377 }
1378 }
1379}
1380
1381#[async_trait]
1382impl OperatorTrait for FundamentalOperators {
1383 async fn execute(&self, operator: &str, params: &str) -> Result<Value, HlxError> {
1384 let clean_operator = operator.strip_prefix('@').unwrap_or(operator);
1386
1387 match clean_operator {
1388 "var" => self.var_operator(params).await,
1390 "env" => self.env_operator(params).await,
1391
1392 "request" => self.request_operator(params).await,
1394 "session" => self.session_operator(params).await,
1395 "cookie" => self.cookie_operator(params).await,
1396 "header" => self.header_operator(params).await,
1397 "param" => self.param_operator(params).await,
1398 "query" => self.query_operator(params).await,
1399
1400 "date" => self.date_operator(params).await,
1402 "time" => self.time_operator(params).await,
1403 "timestamp" => self.timestamp_operator(params).await,
1404 "now" => self.now_operator(params).await,
1405 "format" => self.format_operator(params).await,
1406 "timezone" => self.timezone_operator(params).await,
1407
1408 "string" => self.string_operator(params).await,
1410 "regex" => self.regex_operator(params).await,
1411 "json" => self.json_operator(params).await,
1412 "base64" => self.base64_operator(params).await,
1413 "url" => self.url_operator(params).await,
1414 "hash" => self.hash_operator(params).await,
1415 "uuid" => self.uuid_operator(params).await,
1416
1417 "if" => self.if_operator(params).await,
1419 "switch" => self.switch_operator(params).await,
1420 "case" => self.case_operator(params).await,
1421 "default" => self.default_operator(params).await,
1422 "and" => self.and_operator(params).await,
1423 "or" => self.or_operator(params).await,
1424 "not" => self.not_operator(params).await,
1425
1426 "math" => self.math_operator(params).await,
1428 "calc" => self.calc_operator(params).await,
1429 "min" => self.min_operator(params).await,
1430 "max" => self.max_operator(params).await,
1431 "avg" => self.avg_operator(params).await,
1432 "sum" => self.sum_operator(params).await,
1433 "round" => self.round_operator(params).await,
1434
1435 "array" => self.array_operator(params).await,
1437 "map" => self.map_operator(params).await,
1438 "filter" => self.filter_operator(params).await,
1439 "sort" => self.sort_operator(params).await,
1440 "join" => self.join_operator(params).await,
1441 "split" => self.split_operator(params).await,
1442 "length" => self.length_operator(params).await,
1443
1444 "exec" => self.exec_operator(params).await,
1446
1447 _ => Err(HlxError::unknown_error(format!("Unknown fundamental operator: @{}", clean_operator), "Check the operator name")),
1448 }
1449 }
1450}
1451
1452pub struct OperatorRegistry {
1454 core: Arc<FundamentalOperators>,
1455}
1456
1457impl OperatorRegistry {
1458 pub async fn new() -> Result<Self, HlxError> {
1460 let context = ExecutionContext::default();
1461 Self::new_with_context(context).await
1462 }
1463
1464 pub async fn new_with_context(context: ExecutionContext) -> Result<Self, HlxError> {
1466 let core = FundamentalOperators::new_with_context(context).await?;
1467 Ok(Self {
1468 core: Arc::new(core),
1469 })
1470 }
1471
1472 pub async fn execute(&self, op: &str, params: &str) -> Result<Value, HlxError> {
1474 self.core.execute(op, params).await
1475 }
1476
1477 pub fn context(&self) -> &Arc<ExecutionContext> {
1479 &self.core.context
1480 }
1481
1482 pub fn get_variable(&self, name: &str) -> Result<Value, HlxError> {
1484 self.core.get_variable(name)
1485 }
1486}