json_eval_rs/rlogic/evaluator/
string_ops.rs1use super::Evaluator;
2use serde_json::Value;
3use super::super::compiled::CompiledLogic;
4use super::helpers;
5
6impl Evaluator {
7 #[inline]
9 pub(super) fn concat_strings(&self, items: &[CompiledLogic], user_data: &Value, internal_context: &Value, depth: usize) -> Result<Value, String> {
10 let mut result = String::new();
11 for item in items {
12 let val = self.evaluate_with_context(item, user_data, internal_context, depth + 1)?;
13 result.push_str(&helpers::to_string(&val));
14 }
15 Ok(Value::String(result))
16 }
17
18 pub(super) fn extract_text_side(&self, text_expr: &CompiledLogic, num_expr: Option<&CompiledLogic>, is_left: bool, user_data: &Value, internal_context: &Value, depth: usize) -> Result<Value, String> {
20 let text_val = self.evaluate_with_context(text_expr, user_data, internal_context, depth + 1)?;
21 let text = helpers::to_string(&text_val);
22 let num_chars = if let Some(n_expr) = num_expr {
23 let n_val = self.evaluate_with_context(n_expr, user_data, internal_context, depth + 1)?;
24 helpers::to_number(&n_val) as usize
25 } else { 1 };
26
27 if is_left {
28 Ok(Value::String(text.chars().take(num_chars).collect()))
29 } else {
30 let chars: Vec<char> = text.chars().collect();
31 let start = chars.len().saturating_sub(num_chars);
32 Ok(Value::String(chars[start..].iter().collect()))
33 }
34 }
35
36 pub(super) fn eval_substr(&self, string_expr: &CompiledLogic, start_expr: &CompiledLogic, length_expr: &Option<Box<CompiledLogic>>, user_data: &Value, internal_context: &Value, depth: usize) -> Result<Value, String> {
38 let string_val = self.evaluate_with_context(string_expr, user_data, internal_context, depth + 1)?;
39 let start_val = self.evaluate_with_context(start_expr, user_data, internal_context, depth + 1)?;
40
41 let s = helpers::to_string(&string_val);
42 let start = helpers::to_number(&start_val) as i32;
43
44 let start_idx = if start < 0 {
45 (s.len() as i32 + start).max(0) as usize
46 } else {
47 start.min(s.len() as i32) as usize
48 };
49
50 if let Some(len_expr) = length_expr {
51 let length_val = self.evaluate_with_context(len_expr, user_data, internal_context, depth + 1)?;
52 let length = helpers::to_number(&length_val) as usize;
53 let end_idx = (start_idx + length).min(s.len());
54 Ok(Value::String(s[start_idx..end_idx].to_string()))
55 } else {
56 Ok(Value::String(s[start_idx..].to_string()))
57 }
58 }
59
60 pub(super) fn eval_search(&self, find_expr: &CompiledLogic, within_expr: &CompiledLogic, start_expr: &Option<Box<CompiledLogic>>, user_data: &Value, internal_context: &Value, depth: usize) -> Result<Value, String> {
62 let find_val = self.evaluate_with_context(find_expr, user_data, internal_context, depth + 1)?;
63 let within_val = self.evaluate_with_context(within_expr, user_data, internal_context, depth + 1)?;
64
65 if let (Value::String(find), Value::String(within)) = (&find_val, &within_val) {
66 let start = if let Some(start_e) = start_expr {
67 let start_val = self.evaluate_with_context(start_e, user_data, internal_context, depth + 1)?;
68 (helpers::to_number(&start_val) as usize).saturating_sub(1)
69 } else {
70 0
71 };
72
73 if let Some(pos) = within.to_lowercase()[start..].find(&find.to_lowercase()) {
74 Ok(self.f64_to_json((pos + start + 1) as f64))
75 } else {
76 Ok(Value::Null)
77 }
78 } else {
79 Ok(Value::Null)
80 }
81 }
82
83 pub(super) fn eval_mid(&self, text_expr: &CompiledLogic, start_expr: &CompiledLogic, num_expr: &CompiledLogic, user_data: &Value, internal_context: &Value, depth: usize) -> Result<Value, String> {
85 let text_val = self.evaluate_with_context(text_expr, user_data, internal_context, depth + 1)?;
86 let start_val = self.evaluate_with_context(start_expr, user_data, internal_context, depth + 1)?;
87 let num_val = self.evaluate_with_context(num_expr, user_data, internal_context, depth + 1)?;
88
89 let text = helpers::to_string(&text_val);
90 let start = (helpers::to_number(&start_val) as usize).saturating_sub(1);
91 let num_chars = helpers::to_number(&num_val) as usize;
92
93 let chars: Vec<char> = text.chars().collect();
94 let end = (start + num_chars).min(chars.len());
95 Ok(Value::String(chars[start..end].iter().collect()))
96 }
97
98 pub(super) fn eval_split_text(&self, value_expr: &CompiledLogic, sep_expr: &CompiledLogic, index_expr: &Option<Box<CompiledLogic>>, user_data: &Value, internal_context: &Value, depth: usize) -> Result<Value, String> {
100 let value_val = self.evaluate_with_context(value_expr, user_data, internal_context, depth + 1)?;
101 let sep_val = self.evaluate_with_context(sep_expr, user_data, internal_context, depth + 1)?;
102
103 let text = helpers::to_string(&value_val);
104 let separator = helpers::to_string(&sep_val);
105 let index = if let Some(idx_expr) = index_expr {
106 let idx_val = self.evaluate_with_context(idx_expr, user_data, internal_context, depth + 1)?;
107 helpers::to_number(&idx_val) as usize
108 } else {
109 0
110 };
111
112 let parts: Vec<&str> = text.split(&separator).collect();
113 Ok(Value::String(parts.get(index).unwrap_or(&"").to_string()))
114 }
115
116 pub(super) fn eval_string_format(
118 &self,
119 value_expr: &CompiledLogic,
120 decimals_expr: &Option<Box<CompiledLogic>>,
121 prefix_expr: &Option<Box<CompiledLogic>>,
122 suffix_expr: &Option<Box<CompiledLogic>>,
123 thousands_sep_expr: &Option<Box<CompiledLogic>>,
124 user_data: &Value,
125 internal_context: &Value,
126 depth: usize,
127 ) -> Result<Value, String> {
128 let value_val = self.evaluate_with_context(value_expr, user_data, internal_context, depth + 1)?;
129 let num = helpers::to_f64(&value_val);
130
131 let decimals = if let Some(dec_expr) = decimals_expr {
133 let dec_val = self.evaluate_with_context(dec_expr, user_data, internal_context, depth + 1)?;
134 helpers::to_number(&dec_val) as usize
135 } else {
136 0
137 };
138
139 let prefix = if let Some(pre_expr) = prefix_expr {
141 let pre_val = self.evaluate_with_context(pre_expr, user_data, internal_context, depth + 1)?;
142 helpers::to_string(&pre_val)
143 } else {
144 String::new()
145 };
146
147 let suffix = if let Some(suf_expr) = suffix_expr {
149 let suf_val = self.evaluate_with_context(suf_expr, user_data, internal_context, depth + 1)?;
150 helpers::to_string(&suf_val)
151 } else {
152 String::new()
153 };
154
155 let thousands_sep = if let Some(sep_expr) = thousands_sep_expr {
157 let sep_val = self.evaluate_with_context(sep_expr, user_data, internal_context, depth + 1)?;
158 helpers::to_string(&sep_val)
159 } else {
160 ",".to_string()
161 };
162
163 let formatted = if decimals == 0 {
165 let rounded = num.round() as i64;
166 format_with_thousands(rounded.to_string(), &thousands_sep)
167 } else {
168 let formatted_num = format!("{:.prec$}", num, prec = decimals);
169 if let Some(dot_idx) = formatted_num.find('.') {
171 let integer_part = &formatted_num[..dot_idx];
172 let decimal_part = &formatted_num[dot_idx..];
173 format_with_thousands(integer_part.to_string(), &thousands_sep) + decimal_part
174 } else {
175 format_with_thousands(formatted_num, &thousands_sep)
176 }
177 };
178
179 Ok(Value::String(format!("{}{}{}", prefix, formatted, suffix)))
180 }
181
182 pub(super) fn eval_split_value(&self, string_expr: &CompiledLogic, sep_expr: &CompiledLogic, user_data: &Value, internal_context: &Value, depth: usize) -> Result<Value, String> {
184 let string_val = self.evaluate_with_context(string_expr, user_data, internal_context, depth + 1)?;
185 let sep_val = self.evaluate_with_context(sep_expr, user_data, internal_context, depth + 1)?;
186
187 let text = helpers::to_string(&string_val);
188 let separator = helpers::to_string(&sep_val);
189 let parts: Vec<Value> = text.split(&separator)
190 .map(|s| Value::String(s.to_string()))
191 .collect();
192 Ok(Value::Array(parts))
193 }
194
195 pub(super) fn eval_length(&self, expr: &CompiledLogic, user_data: &Value, internal_context: &Value, depth: usize) -> Result<Value, String> {
197 let val = self.evaluate_with_context(expr, user_data, internal_context, depth + 1)?;
198 let len = match &val {
199 Value::String(s) => s.len(),
200 Value::Array(arr) => arr.len(),
201 Value::Object(obj) => obj.len(),
202 _ => 0,
203 };
204 Ok(self.f64_to_json(len as f64))
205 }
206
207 pub(super) fn eval_len(&self, expr: &CompiledLogic, user_data: &Value, internal_context: &Value, depth: usize) -> Result<Value, String> {
209 let val = self.evaluate_with_context(expr, user_data, internal_context, depth + 1)?;
210 let s = helpers::to_string(&val);
211 Ok(self.f64_to_json(s.len() as f64))
212 }
213}
214
215fn format_with_thousands(num_str: String, separator: &str) -> String {
217 if separator.is_empty() {
218 return num_str;
219 }
220
221 let chars: Vec<char> = num_str.chars().collect();
222 let mut result = String::new();
223 let len = chars.len();
224
225 for (i, ch) in chars.iter().enumerate() {
226 if *ch == '-' || *ch == '+' {
227 result.push(*ch);
228 continue;
229 }
230
231 result.push(*ch);
232
233 let remaining = len - i - 1;
235 if remaining > 0 && remaining % 3 == 0 {
236 result.push_str(separator);
237 }
238 }
239
240 result
241}