Skip to main content

atom_engine/filters/
number.rs

1use serde_json::{json, Value};
2use std::collections::HashMap;
3use tera::Error;
4
5use super::FilterResult;
6
7pub fn round(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
8    let n = value
9        .as_f64()
10        .ok_or_else(|| Error::msg("Expected number"))?;
11    let precision = args.get("precision").and_then(|v| v.as_u64()).unwrap_or(0) as usize;
12    let multiplier = 10_f64.powi(precision as i32);
13    let result = (n * multiplier).round() / multiplier;
14    Ok(json!(result))
15}
16
17pub fn abs(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
18    let n = value
19        .as_f64()
20        .ok_or_else(|| Error::msg("Expected number"))?;
21    Ok(json!(n.abs()))
22}
23
24pub fn format_number(value: &Value, args: &HashMap<String, Value>) -> FilterResult {
25    let n = value
26        .as_f64()
27        .ok_or_else(|| Error::msg("Expected number"))?;
28    let format = args.get("format").and_then(|v| v.as_str()).unwrap_or(",");
29
30    let s = format!("{}", n);
31    let parts: Vec<&str> = s.split('.').collect();
32    let int_part = parts[0];
33    let dec_part = parts.get(1);
34
35    let formatted_int = int_part
36        .chars()
37        .rev()
38        .enumerate()
39        .map(|(i, c)| {
40            if i > 0 && i % 3 == 0 {
41                format!("{}{}", format, c)
42            } else {
43                c.to_string()
44            }
45        })
46        .collect::<Vec<_>>()
47        .into_iter()
48        .rev()
49        .collect::<String>();
50
51    let result = match dec_part {
52        Some(d) => format!("{}.{}", formatted_int, d),
53        None => formatted_int,
54    };
55
56    Ok(Value::String(result))
57}
58
59pub fn min_filter(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
60    if let Some(arr) = value.as_array() {
61        let min = arr
62            .iter()
63            .filter_map(|v| v.as_f64())
64            .fold(f64::INFINITY, f64::min);
65        Ok(Value::Number(
66            serde_json::Number::from_f64(min).unwrap_or(serde_json::Number::from(0)),
67        ))
68    } else {
69        Ok(value.clone())
70    }
71}
72
73pub fn max_filter(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
74    if let Some(arr) = value.as_array() {
75        let max = arr
76            .iter()
77            .filter_map(|v| v.as_f64())
78            .fold(f64::NEG_INFINITY, f64::max);
79        Ok(Value::Number(
80            serde_json::Number::from_f64(max).unwrap_or(serde_json::Number::from(0)),
81        ))
82    } else {
83        Ok(value.clone())
84    }
85}
86
87pub fn sum(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
88    if let Some(arr) = value.as_array() {
89        let total: f64 = arr.iter().filter_map(|v| v.as_f64()).sum();
90        Ok(Value::Number(
91            serde_json::Number::from_f64(total).unwrap_or(serde_json::Number::from(0)),
92        ))
93    } else {
94        Ok(value.clone())
95    }
96}
97
98pub fn avg(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
99    if let Some(arr) = value.as_array() {
100        let nums: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
101        let avg = if nums.is_empty() {
102            0.0
103        } else {
104            nums.iter().sum::<f64>() / nums.len() as f64
105        };
106        Ok(Value::Number(
107            serde_json::Number::from_f64(avg).unwrap_or(serde_json::Number::from(0)),
108        ))
109    } else {
110        Ok(value.clone())
111    }
112}
113
114pub fn ceil(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
115    if let Some(n) = value.as_f64() {
116        Ok(Value::Number(
117            serde_json::Number::from_f64(n.ceil()).unwrap_or(serde_json::Number::from(0)),
118        ))
119    } else {
120        Ok(value.clone())
121    }
122}
123
124pub fn floor(value: &Value, _: &HashMap<String, Value>) -> FilterResult {
125    if let Some(n) = value.as_f64() {
126        Ok(Value::Number(
127            serde_json::Number::from_f64(n.floor()).unwrap_or(serde_json::Number::from(0)),
128        ))
129    } else {
130        Ok(value.clone())
131    }
132}