Skip to main content

atom_engine/filters/
collection.rs

1use serde_json::Value;
2use std::collections::HashMap;
3use tera::Error;
4
5use super::FilterResult;
6
7pub fn first(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
8    if let Some(arr) = value.as_array() {
9        Ok(arr.first().cloned().unwrap_or(Value::Null))
10    } else {
11        Ok(value.clone())
12    }
13}
14
15pub fn last(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
16    if let Some(arr) = value.as_array() {
17        Ok(arr.last().cloned().unwrap_or(Value::Null))
18    } else {
19        Ok(value.clone())
20    }
21}
22
23pub fn length(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
24    let len = match value {
25        Value::Array(arr) => arr.len(),
26        Value::Object(obj) => obj.len(),
27        Value::String(s) => s.chars().count(),
28        _ => 0,
29    };
30    Ok(Value::Number(len.into()))
31}
32
33pub fn reverse(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
34    if let Some(arr) = value.as_array().cloned() {
35        let mut rev = arr;
36        rev.reverse();
37        Ok(Value::Array(rev))
38    } else if let Some(s) = value.as_str() {
39        Ok(Value::String(s.chars().rev().collect()))
40    } else {
41        Ok(value.clone())
42    }
43}
44
45pub fn sort(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
46    if let Some(mut arr) = value.as_array().cloned() {
47        arr.sort_by(|a, b| {
48            let a_str = serde_json::to_string(a).unwrap_or_default();
49            let b_str = serde_json::to_string(b).unwrap_or_default();
50            a_str.cmp(&b_str)
51        });
52        Ok(Value::Array(arr))
53    } else {
54        Ok(value.clone())
55    }
56}
57
58pub fn group_by(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
59    let arr = value
60        .as_array()
61        .ok_or_else(|| Error::msg("Expected array"))?;
62    let key = args
63        .get("attribute")
64        .and_then(|v| v.as_str())
65        .ok_or_else(|| Error::msg("Missing attribute"))?;
66
67    let mut groups: HashMap<String, Vec<Value>> = HashMap::new();
68    for item in arr {
69        let group_key = item
70            .get(key)
71            .map(|v| serde_json::to_string(v).unwrap_or_default())
72            .unwrap_or_default();
73        groups.entry(group_key).or_default().push(item.clone());
74    }
75
76    Ok(Value::Object(
77        groups
78            .into_iter()
79            .map(|(k, v)| (k, serde_json::json!(v)))
80            .collect(),
81    ))
82}
83
84pub fn where_filter(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
85    let arr = value
86        .as_array()
87        .ok_or_else(|| Error::msg("Expected array"))?;
88    let key = args.get("attribute").and_then(|v| v.as_str());
89    let filter_value = args.get("value").cloned();
90
91    let result: Vec<Value> = arr
92        .iter()
93        .filter(|item| {
94            if let (Some(key), Some(fv)) = (key, &filter_value) {
95                item.get(key) == Some(fv)
96            } else {
97                item.is_object() && !item.as_object().map(|o| o.is_empty()).unwrap_or(true)
98            }
99        })
100        .cloned()
101        .collect();
102
103    Ok(Value::Array(result))
104}
105
106pub fn pluck(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
107    let arr = value
108        .as_array()
109        .ok_or_else(|| Error::msg("Expected array"))?;
110    let key = args
111        .get("attribute")
112        .and_then(|v| v.as_str())
113        .ok_or_else(|| Error::msg("Missing attribute"))?;
114
115    let result: Vec<Value> = arr
116        .iter()
117        .filter_map(|item| item.get(key).cloned())
118        .collect();
119    Ok(Value::Array(result))
120}
121
122pub fn join(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
123    let sep = args
124        .get("separator")
125        .and_then(|v| v.as_str())
126        .unwrap_or(",");
127    if let Some(arr) = value.as_array() {
128        let joined = arr
129            .iter()
130            .map(|v| v.as_str().unwrap_or(""))
131            .collect::<Vec<_>>()
132            .join(sep);
133        Ok(Value::String(joined))
134    } else {
135        Ok(value.clone())
136    }
137}
138
139pub fn slice(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
140    let start = args.get("start").and_then(|v| v.as_u64()).unwrap_or(0) as usize;
141    let length = args.get("length").and_then(|v| v.as_u64()).unwrap_or(1) as usize;
142
143    if let Some(arr) = value.as_array() {
144        let slice: Vec<Value> = arr.iter().skip(start).take(length).cloned().collect();
145        Ok(Value::Array(slice))
146    } else if let Some(s) = value.as_str() {
147        let chars: String = s.chars().skip(start).take(length).collect();
148        Ok(Value::String(chars))
149    } else {
150        Ok(value.clone())
151    }
152}
153
154pub fn uniq(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
155    if let Some(arr) = value.as_array() {
156        let mut seen = std::collections::HashSet::new();
157        let unique: Vec<Value> = arr
158            .iter()
159            .filter(|v| seen.insert(v.to_string()))
160            .cloned()
161            .collect();
162        Ok(Value::Array(unique))
163    } else {
164        Ok(value.clone())
165    }
166}
167
168pub fn shuffle(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
169    use rand::seq::SliceRandom;
170    use rand::thread_rng;
171
172    if let Some(arr) = value.as_array() {
173        let mut shuffled = arr.clone();
174        let mut rng = thread_rng();
175        shuffled.shuffle(&mut rng);
176        Ok(Value::Array(shuffled))
177    } else {
178        Ok(value.clone())
179    }
180}
181
182pub fn map_filter(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
183    let arr = value
184        .as_array()
185        .ok_or_else(|| Error::msg("Expected array"))?;
186    let prop = args.get("prop").and_then(|v| v.as_str()).unwrap_or("value");
187    let transform = args.get("transform").and_then(|v| v.as_str());
188
189    #[allow(clippy::needless_borrows_for_generic_args)]
190    let result: Vec<Value> = arr
191        .iter()
192        .map(|item| {
193            if let Some(transform_fn) = transform {
194                match transform_fn {
195                    "upper" => {
196                        if let Some(v) = item.get(&prop) {
197                            Value::String(v.as_str().unwrap_or("").to_uppercase())
198                        } else {
199                            item.clone()
200                        }
201                    }
202                    "lower" => {
203                        if let Some(v) = item.get(&prop) {
204                            Value::String(v.as_str().unwrap_or("").to_lowercase())
205                        } else {
206                            item.clone()
207                        }
208                    }
209                    "length" => {
210                        if let Some(v) = item.get(&prop) {
211                            Value::Number(v.as_array().map(|a| a.len()).unwrap_or(0).into())
212                        } else {
213                            Value::Number(0.into())
214                        }
215                    }
216                    _ => item.get(&prop).cloned().unwrap_or(Value::Null),
217                }
218            } else {
219                item.get(&prop).cloned().unwrap_or(Value::Null)
220            }
221        })
222        .collect();
223
224    Ok(Value::Array(result))
225}
226
227pub fn filter_filter(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
228    let arr = value
229        .as_array()
230        .ok_or_else(|| Error::msg("Expected array"))?;
231    let key = args.get("key").and_then(|v| v.as_str());
232    let value_filter = args.get("value").cloned();
233    let op = args.get("op").and_then(|v| v.as_str()).unwrap_or("eq");
234
235    let result: Vec<Value> = arr
236        .iter()
237        .filter(|item| {
238            if let (Some(k), Some(fv)) = (key, &value_filter) {
239                let item_val = item.get(k);
240                match op {
241                    "eq" => item_val == Some(fv),
242                    "ne" => item_val != Some(fv),
243                    "gt" => {
244                        if let (Some(iv), Some(f)) =
245                            (item_val.and_then(|v| v.as_f64()), fv.as_f64())
246                        {
247                            iv > f
248                        } else {
249                            false
250                        }
251                    }
252                    "gte" => {
253                        if let (Some(iv), Some(f)) =
254                            (item_val.and_then(|v| v.as_f64()), fv.as_f64())
255                        {
256                            iv >= f
257                        } else {
258                            false
259                        }
260                    }
261                    "lt" => {
262                        if let (Some(iv), Some(f)) =
263                            (item_val.and_then(|v| v.as_f64()), fv.as_f64())
264                        {
265                            iv < f
266                        } else {
267                            false
268                        }
269                    }
270                    "lte" => {
271                        if let (Some(iv), Some(f)) =
272                            (item_val.and_then(|v| v.as_f64()), fv.as_f64())
273                        {
274                            iv <= f
275                        } else {
276                            false
277                        }
278                    }
279                    "contains" => {
280                        if let Some(iv) = item_val {
281                            iv.to_string().contains(&fv.to_string())
282                        } else {
283                            false
284                        }
285                    }
286                    "exists" => item.is_object() && item.get(k).is_some(),
287                    _ => item_val == Some(fv),
288                }
289            } else {
290                !item.is_null() && !item.as_array().map(|a| a.is_empty()).unwrap_or(false)
291            }
292        })
293        .cloned()
294        .collect();
295
296    Ok(Value::Array(result))
297}
298
299pub fn each_filter(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
300    let arr = value
301        .as_array()
302        .ok_or_else(|| Error::msg("Expected array"))?;
303    let include_index = args.get("index").and_then(|v| v.as_bool()).unwrap_or(false);
304
305    if include_index {
306        let result: Vec<Value> = arr
307            .iter()
308            .enumerate()
309            .map(|(i, v)| serde_json::json!({"index": i, "value": v, "first": i == 0, "last": i == arr.len() - 1}))
310            .collect();
311        Ok(Value::Array(result))
312    } else {
313        Ok(Value::Array(arr.clone()))
314    }
315}
316
317pub fn reduce_filter(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
318    let arr = value
319        .as_array()
320        .ok_or_else(|| Error::msg("Expected array"))?;
321    let initial = args.get("initial").cloned().unwrap_or(Value::Null);
322    let prop = args.get("prop").and_then(|v| v.as_str());
323
324    let result = arr.iter().fold(initial, |acc, item| {
325        if let Some(p) = prop {
326            if let Some(v) = item.get(p) {
327                if let (Some(a), Some(b)) = (acc.as_f64(), v.as_f64()) {
328                    return serde_json::json!(a + b);
329                }
330            }
331        }
332        acc
333    });
334
335    Ok(result)
336}
337
338pub fn flatten_filter(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
339    if let Some(arr) = value.as_array() {
340        let mut result = Vec::new();
341        for item in arr {
342            if let Some(inner) = item.as_array() {
343                for i in inner {
344                    result.push(i.clone());
345                }
346            } else {
347                result.push(item.clone());
348            }
349        }
350        Ok(Value::Array(result))
351    } else {
352        Ok(value.clone())
353    }
354}
355
356pub fn partition_filter(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
357    let arr = value
358        .as_array()
359        .ok_or_else(|| Error::msg("Expected array"))?;
360    let key = args.get("key").and_then(|v| v.as_str());
361    let value_filter = args.get("value").cloned();
362
363    let (matched, rest): (Vec<&Value>, Vec<&Value>) = arr.iter().partition(|item| {
364        if let (Some(k), Some(fv)) = (key, &value_filter) {
365            item.get(k) == Some(fv)
366        } else {
367            !item.is_null()
368        }
369    });
370
371    Ok(serde_json::json!({
372        "matched": matched.iter().map(|v| (*v).clone()).collect::<Vec<_>>(),
373        "rest": rest.iter().map(|v| (*v).clone()).collect::<Vec<_>>()
374    }))
375}