helix/dna/ops/
string_processing.rs

1//! String & Data Processing - 8 operators
2//! 
3//! This module implements operators for string manipulation and data processing:
4//! - @string: String manipulation
5//! - @regex: Regular expressions
6//! - @hash: Hashing functions
7//! - @base64: Base64 encoding
8//! - @xml: XML parsing
9//! - @yaml: YAML parsing
10//! - @csv: CSV processing
11//! - @template: Template engine
12
13use crate::dna::hel::error::HlxError;
14use crate::ops::utils;
15use crate::dna::atp::value::Value;
16use crate::ops::OperatorTrait;
17use async_trait::async_trait;
18use base64::{engine::general_purpose, Engine};
19use md5;
20use sha2::{Digest, Sha256};
21use std::collections::HashMap;
22
23/// String processing operators implementation
24pub struct StringOperators;
25
26impl StringOperators {
27    pub async fn new() -> Result<Self, HlxError> {
28        Ok(Self)
29    }
30
31    pub async fn execute(&self, operator: &str, params: &str) -> Result<Value, HlxError> {
32        self.execute_impl(operator, params).await
33    }
34
35    async fn execute_impl(&self, operator: &str, params: &str) -> Result<Value, HlxError> {
36        let params_map = crate::ops::utils::parse_params(params)?;
37
38        match operator {
39            "concat" => self.concat_operator(&params_map).await,
40            "split" => self.split_operator(&params_map).await,
41            "replace" => self.replace_operator(&params_map).await,
42            "trim" => self.trim_operator(&params_map).await,
43            "upper" => self.upper_operator(&params_map).await,
44            "lower" => self.lower_operator(&params_map).await,
45            "hash" => self.hash_operator(&params_map).await,
46            "format" => self.format_operator(&params_map).await,
47            _ => Err(HlxError::invalid_parameters(operator, "Unknown string operator")),
48        }
49    }
50}
51
52#[async_trait]
53impl crate::ops::OperatorTrait for StringOperators {
54    async fn execute(&self, operator: &str, params: &str) -> Result<Value, HlxError> {
55        self.execute_impl(operator, params).await
56    }
57}
58
59impl StringOperators {
60    async fn concat_operator(&self, params: &HashMap<String, Value>) -> Result<Value, HlxError> {
61        let strings = params.get("strings")
62            .and_then(|v| v.as_array())
63            .ok_or_else(|| HlxError::validation_error("Missing 'strings' parameter", "Check the strings parameter"))?;
64
65        let separator = params.get("separator")
66            .and_then(|v| v.as_string())
67            .unwrap_or("");
68
69        let concatenated = strings.iter()
70            .map(|v| v.to_string())
71            .collect::<Vec<String>>()
72            .join(separator);
73
74        Ok(Value::Object({
75            let mut map = HashMap::new();
76            map.insert("result".to_string(), Value::String(concatenated.to_string()));
77            map.insert("count".to_string(), Value::Number(strings.len() as f64));
78            map.insert("separator".to_string(), Value::String(separator.to_string()));
79            map
80        }))
81    }
82
83    async fn split_operator(&self, params: &HashMap<String, Value>) -> Result<Value, HlxError> {
84        let input = params.get("input")
85            .and_then(|v| v.as_string())
86            .ok_or_else(|| HlxError::validation_error("Missing 'input' parameter", "Check the input parameter"))?;
87
88        let delimiter = params.get("delimiter")
89            .and_then(|v| v.as_string())
90            .unwrap_or(" ");
91
92        let parts: Vec<Value> = input.split(delimiter)
93            .map(|s| Value::String(s.to_string()))
94            .collect();
95
96        Ok(Value::Object({
97            let mut map = HashMap::new();
98            map.insert("parts".to_string(), Value::Array(parts.clone()));
99            map.insert("count".to_string(), Value::Number(parts.len() as f64));
100            map.insert("delimiter".to_string(), Value::String(delimiter.to_string()));
101            map
102        }))
103    }
104
105    async fn replace_operator(&self, params: &HashMap<String, Value>) -> Result<Value, HlxError> {
106        let input = params.get("input")
107            .and_then(|v| v.as_string())
108            .ok_or_else(|| HlxError::validation_error("Missing 'input' parameter", "Check the input parameter"))?;
109
110        let from = params.get("from")
111            .and_then(|v| v.as_string())
112            .ok_or_else(|| HlxError::validation_error("Missing 'from' parameter", "Check the from parameter"))?;
113
114        let to = params.get("to")
115            .and_then(|v| v.as_string())
116            .unwrap_or("");
117
118        let replaced = input.replace(from, &to);
119
120        Ok(Value::Object({
121            let mut map = HashMap::new();
122            map.insert("result".to_string(), Value::String(replaced.to_string()));
123            map.insert("original".to_string(), Value::String(input.to_string()));
124            map.insert("from".to_string(), Value::String(from.to_string()));
125            map.insert("to".to_string(), Value::String(to.to_string()));
126            map
127        }))
128    }
129
130    async fn trim_operator(&self, params: &HashMap<String, Value>) -> Result<Value, HlxError> {
131        let input = params.get("input")
132            .and_then(|v| v.as_string())
133            .ok_or_else(|| HlxError::validation_error("Missing 'input' parameter", "Check the input parameter"))?;
134
135        let mode = params.get("mode")
136            .and_then(|v| v.as_string())
137            .unwrap_or("both");
138
139        let trimmed = match mode {
140            "left" => input.trim_start(),
141            "right" => input.trim_end(),
142            "both" => input.trim(),
143            _ => input.trim(),
144        };
145
146        Ok(Value::Object({
147            let mut map = HashMap::new();
148            map.insert("result".to_string(), Value::String(trimmed.to_string()));
149            map.insert("original".to_string(), Value::String(input.to_string()));
150            map.insert("mode".to_string(), Value::String(mode.to_string()));
151            map
152        }))
153    }
154
155    async fn upper_operator(&self, params: &HashMap<String, Value>) -> Result<Value, HlxError> {
156        let input = params.get("input")
157            .and_then(|v| v.as_string())
158            .ok_or_else(|| HlxError::validation_error("Missing 'input' parameter", "Check the input parameter"))?;
159
160        let uppercased = input.to_uppercase();
161
162        Ok(Value::Object({
163            let mut map = HashMap::new();
164            map.insert("result".to_string(), Value::String(uppercased.to_string()));
165            map.insert("original".to_string(), Value::String(input.to_string()));
166            map.insert("operation".to_string(), Value::String("uppercase".to_string()));
167            map
168        }))
169    }
170
171    async fn lower_operator(&self, params: &HashMap<String, Value>) -> Result<Value, HlxError> {
172        let input = params.get("input")
173            .and_then(|v| v.as_string())
174            .ok_or_else(|| HlxError::validation_error("Missing 'input' parameter", "Check the input parameter"))?;
175
176        let lowercased = input.to_lowercase();
177
178        Ok(Value::Object({
179            let mut map = HashMap::new();
180            map.insert("result".to_string(), Value::String(lowercased.to_string()));
181            map.insert("original".to_string(), Value::String(input.to_string()));
182            map.insert("operation".to_string(), Value::String("lowercase".to_string()));
183            map
184        }))
185    }
186
187    async fn hash_operator(&self, params: &HashMap<String, Value>) -> Result<Value, HlxError> {
188        let input = params.get("input")
189            .and_then(|v| v.as_string())
190            .ok_or_else(|| HlxError::validation_error("Missing 'input' parameter", "Check the input parameter"))?;
191
192        let algorithm = params.get("algorithm")
193            .and_then(|v| v.as_string())
194            .unwrap_or("sha256");
195
196        let hash = match algorithm {
197            "sha256" => {
198                let mut hasher = Sha256::new();
199                hasher.update(input.as_bytes());
200                general_purpose::STANDARD.encode(hasher.finalize())
201            },
202            "md5" => {
203                let digest = md5::compute(input.as_bytes());
204                general_purpose::STANDARD.encode(&digest[..])
205            },
206            _ => return Err(HlxError::hash_error(format!("Unsupported algorithm: {}", algorithm), "Check the hash algorithm parameter")),
207        };
208
209        Ok(Value::Object({
210            let mut map = HashMap::new();
211            map.insert("hash".to_string(), Value::String(hash.to_string()));
212            map.insert("algorithm".to_string(), Value::String(algorithm.to_string()));
213            map.insert("input".to_string(), Value::String(input.to_string()));
214            map
215        }))
216    }
217
218    async fn format_operator(&self, params: &HashMap<String, Value>) -> Result<Value, HlxError> {
219        let template = params.get("template")
220            .and_then(|v| v.as_string())
221            .ok_or_else(|| HlxError::validation_error("Missing 'template' parameter", "Check the template parameter"))?;
222
223        let variables = params.get("variables")
224            .and_then(|v| v.as_object())
225            .cloned()
226            .unwrap_or_default();
227
228        let mut result = template.to_string();
229        for (key, value) in &variables {
230            let placeholder = format!("${{{}}}", key);
231            let value_str = value.to_string();
232            result = result.replace(&placeholder, &value_str);
233        }
234
235        Ok(Value::Object({
236            let mut map = HashMap::new();
237            map.insert("result".to_string(), Value::String(result.to_string()));
238            map.insert("template".to_string(), Value::String(template.to_string()));
239            map.insert("variables_used".to_string(), Value::Number(variables.len() as f64));
240            map
241        }))
242    }
243}