atom_engine/filters/
collection.rs1use 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}