helix/dna/ops/
fundamental.rs

1
2//! Fundamental Language Operators - @ prefixed operators
3//!
4//! This module implements the core @ prefixed operators that provide basic language functionality:
5//! - @var: Global variable references (persisted across executions)
6//! - @env: Environment variable access
7//! - @request: HTTP request data access
8//! - @session: Session management (mutable)
9//! - @cookie: Cookie operations
10//! - @header: HTTP header access
11//! - @param: Route parameter extraction
12//! - @query: URL query parameter access
13//! - @timezone: Timezone conversions with chrono-tz
14//! - @regex: Regular expression operations
15//! - @json: JSON parsing and manipulation
16//! - @base64: Base64 encoding/decoding
17//! - @url: URL encoding/decoding
18//! - @hash: Hashing operations (SHA256, MD5)
19//! - @uuid: UUID generation (v4)
20//! - @if: Conditional expressions
21//! - @switch: Switch statements
22//! - @case: Case matching
23//! - @default: Default values
24//! - @and: Logical AND
25//! - @or: Logical OR
26//! - @not: Logical NOT
27//! - @math: Mathematical operations
28//! - @calc: Complex calculations with meval
29//! - @min: Minimum value from array
30//! - @max: Maximum value from array
31
32use 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/// Execution context containing request data, session, cookies, etc.
51#[derive(Debug, Clone)]
52pub struct ExecutionContext {
53    /// HTTP request data (optional, may be None for non-web contexts)
54    pub request: Option<RequestData>,
55    /// Session data (mutable, shared)
56    pub session: Arc<RwLock<HashMap<String, Value>>>,
57    /// HTTP cookies
58    pub cookies: HashMap<String, String>,
59    /// Route parameters
60    pub params: HashMap<String, String>,
61    /// URL query parameters
62    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/// HTTP request data structure
78#[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
86/// Fundamental operators implementation supporting @ prefixed syntax
87pub struct FundamentalOperators {
88    /// Global variables (persisted across executions)
89    variables: Arc<RwLock<HashMap<String, Value>>>,
90    /// Execution context – carries request, session, cookies, etc.
91    context: Arc<ExecutionContext>,
92}
93
94impl FundamentalOperators {
95    /// Construct a new instance **with** an execution context.
96    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    /// Legacy constructor for backward compatibility (creates empty context)
104    pub async fn new() -> Result<Self, HlxError> {
105        let context = ExecutionContext::default();
106        Self::new_with_context(context).await
107    }
108
109    /// Get a variable value directly from the global variables store
110    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    /// Set a variable value in the global variables store
117    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    /// @var – Get or set a global variable.
127    /// Parameters:
128    ///   * `name` (String) – variable name (required)
129    ///   * `value` (any Value) – if present the variable is set, otherwise it is read.
130    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            // ---- SET ----
146            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            // ---- GET ----
163            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    /// @env – Read an OS environment variable, optionally providing a default.
182    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    /// @request – Pull data from the current request.
206    /// Parameters:
207    ///   * `field` (String, optional) – one of `method`, `url`, `headers`, `body`, `all`.
208    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        // The request lives inside the shared ExecutionContext
216        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    /// @session – Read or write a value from the session map.
253    /// Parameters:
254    ///   * `action` (String, optional) – `get` (default) or `set`.
255    ///   * `key` (String, required for both actions).
256    ///   * `value` (any Value, required for `set`).
257    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    /// @cookie – Read a cookie from the shared context.
323    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    /// @header – Retrieve a request header.
346    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    /// @param – Pull a route‑parameter from the context.
368    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    /// @query – Pull a URL‑query parameter from the shared context.
391    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    // Date and Time Operations
414    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            // Try to parse as ISO string
478            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        // For now, just return the datetime in the requested timezone name
516        // Real implementation would use chrono-tz for proper conversion
517        let result = format!("{} ({})", datetime.to_rfc3339(), tz);
518        Ok(Value::String(result))
519    }
520
521    // String and Data Processing
522    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    /// @if - Conditional evaluation.
716    /// Parameters: condition (Bool) - The condition to evaluate; then (Value) - Value if true; else (Value, optional) - Value if false.
717    /// Example: @if condition=true then="yes" else="no"
718    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    /// @switch - Switch statement evaluation.
735    /// Parameters: value (Value) - The value to match against; cases (Array) - Array of objects with 'match' and 'result' fields.
736    /// Example: @switch value="a" cases=[{"match":"a","result":"apple"}]
737    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        // If no case matches, return the value itself or null
758        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    // Math and Calculation Operations
873    /// @math - Basic mathematical operations.
874    /// Parameters: operation (String) - "add", "sub", "mul", "div", "mod", "pow"; a (Number); b (Number).
875    /// Example: @math operation="add" a=5 b=3
876    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    /// @calc - Complex mathematical expression evaluation.
915    /// Parameters: expression (String) - Mathematical expression to evaluate.
916    /// Example: @calc expression="2 * (3 + 4) / 2"
917    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    // Array and Collection Operations
1049    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    /// @exec – Execute shell commands and external programs
1302    /// Parameters:
1303    ///   * `command` (String, required) – The shell command to execute
1304    ///   * `timeout` (Number, optional) – Timeout in seconds (default: 30)
1305    ///   * `working_dir` (String, optional) – Working directory (default: current)
1306    /// Example: @exec command="git rev-parse HEAD" timeout=10
1307    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        // Parse command into parts (simple shell parsing)
1329        let parts: Vec<&str> = if command.contains(' ') {
1330            // For complex commands, use shell
1331            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        // Execute with timeout
1346        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        // Support both @ prefixed and non-prefixed operators
1385        let clean_operator = operator.strip_prefix('@').unwrap_or(operator);
1386
1387        match clean_operator {
1388            // Variable and Environment Access
1389            "var" => self.var_operator(params).await,
1390            "env" => self.env_operator(params).await,
1391
1392            // HTTP and Request Data
1393            "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 and Time
1401            "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 and Data Processing
1409            "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            // Conditional and Logic
1418            "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 and Calculations
1427            "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 and Collections
1436            "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            // System Execution
1445            "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
1452/// Clean registry API for fundamental operators
1453pub struct OperatorRegistry {
1454    core: Arc<FundamentalOperators>,
1455}
1456
1457impl OperatorRegistry {
1458    /// Create a new registry with an empty context (default values)
1459    pub async fn new() -> Result<Self, HlxError> {
1460        let context = ExecutionContext::default();
1461        Self::new_with_context(context).await
1462    }
1463
1464    /// Create a new registry with the provided execution context
1465    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    /// Execute an operator with the given parameters
1473    pub async fn execute(&self, op: &str, params: &str) -> Result<Value, HlxError> {
1474        self.core.execute(op, params).await
1475    }
1476
1477    /// Get access to the underlying context for inspection/modification
1478    pub fn context(&self) -> &Arc<ExecutionContext> {
1479        &self.core.context
1480    }
1481
1482    /// Get a variable value by name
1483    pub fn get_variable(&self, name: &str) -> Result<Value, HlxError> {
1484        self.core.get_variable(name)
1485    }
1486}