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