1use std::collections::HashMap;
3
4#[cfg(feature = "builtins")]
5use humansize::format_size;
6use serde_json::value::{to_value, Value};
7
8use crate::errors::{Error, Result};
9
10pub fn abs(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
12 if value.as_u64().is_some() {
13 Ok(value.clone())
14 } else if let Some(num) = value.as_i64() {
15 Ok(to_value(num.abs()).unwrap())
16 } else if let Some(num) = value.as_f64() {
17 Ok(to_value(num.abs()).unwrap())
18 } else {
19 Err(Error::msg("Filter `abs` was used on a value that isn't a number."))
20 }
21}
22
23pub fn pluralize(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
27 let num = try_get_value!("pluralize", "value", f64, value);
28
29 let plural = match args.get("plural") {
30 Some(val) => try_get_value!("pluralize", "plural", String, val),
31 None => "s".to_string(),
32 };
33
34 let singular = match args.get("singular") {
35 Some(val) => try_get_value!("pluralize", "singular", String, val),
36 None => "".to_string(),
37 };
38
39 if (num.abs() - 1.).abs() > ::std::f64::EPSILON {
41 Ok(to_value(plural).unwrap())
42 } else {
43 Ok(to_value(singular).unwrap())
44 }
45}
46
47pub fn round(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
52 let num = try_get_value!("round", "value", f64, value);
53 let method = match args.get("method") {
54 Some(val) => try_get_value!("round", "method", String, val),
55 None => "common".to_string(),
56 };
57 let precision = match args.get("precision") {
58 Some(val) => try_get_value!("round", "precision", i32, val),
59 None => 0,
60 };
61 let multiplier = if precision == 0 { 1.0 } else { 10.0_f64.powi(precision) };
62
63 match method.as_ref() {
64 "common" => Ok(to_value((multiplier * num).round() / multiplier).unwrap()),
65 "ceil" => Ok(to_value((multiplier * num).ceil() / multiplier).unwrap()),
66 "floor" => Ok(to_value((multiplier * num).floor() / multiplier).unwrap()),
67 _ => Err(Error::msg(format!(
68 "Filter `round` received an incorrect value for arg `method`: got `{:?}`, \
69 only common, ceil and floor are allowed",
70 method
71 ))),
72 }
73}
74
75#[cfg(feature = "builtins")]
77pub fn filesizeformat(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
78 let num = try_get_value!("filesizeformat", "value", usize, value);
79 let binary = match args.get("binary") {
80 Some(binary) => try_get_value!("filesizeformat", "binary", bool, binary),
81 None => false,
82 };
83 let format = if binary { humansize::BINARY } else { humansize::WINDOWS };
84 Ok(to_value(format_size(num, format))
85 .expect("json serializing should always be possible for a string"))
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use serde_json::value::to_value;
92 use std::collections::HashMap;
93
94 #[test]
95 fn test_abs_unsigend() {
96 let result = abs(&to_value(1).unwrap(), &HashMap::new());
97 assert!(result.is_ok());
98 assert_eq!(result.unwrap(), to_value(1).unwrap());
99 }
100
101 #[test]
102 fn test_abs_negative_integer() {
103 let result = abs(&to_value(-1).unwrap(), &HashMap::new());
104 assert!(result.is_ok());
105 assert_eq!(result.unwrap(), to_value(1).unwrap());
106 }
107
108 #[test]
109 fn test_abs_negative_float() {
110 let result = abs(&to_value(-1.0).unwrap(), &HashMap::new());
111 assert!(result.is_ok());
112 assert_eq!(result.unwrap(), to_value(1.0).unwrap());
113 }
114
115 #[test]
116 fn test_abs_non_number() {
117 let result = abs(&to_value("nan").unwrap(), &HashMap::new());
118 assert!(result.is_err());
119 assert_eq!(
120 result.unwrap_err().to_string(),
121 "Filter `abs` was used on a value that isn't a number."
122 );
123 }
124
125 #[test]
126 fn test_pluralize_single() {
127 let result = pluralize(&to_value(1).unwrap(), &HashMap::new());
128 assert!(result.is_ok());
129 assert_eq!(result.unwrap(), to_value("").unwrap());
130 }
131
132 #[test]
133 fn test_pluralize_multiple() {
134 let result = pluralize(&to_value(2).unwrap(), &HashMap::new());
135 assert!(result.is_ok());
136 assert_eq!(result.unwrap(), to_value("s").unwrap());
137 }
138
139 #[test]
140 fn test_pluralize_zero() {
141 let result = pluralize(&to_value(0).unwrap(), &HashMap::new());
142 assert!(result.is_ok());
143 assert_eq!(result.unwrap(), to_value("s").unwrap());
144 }
145
146 #[test]
147 fn test_pluralize_multiple_custom_plural() {
148 let mut args = HashMap::new();
149 args.insert("plural".to_string(), to_value("es").unwrap());
150 let result = pluralize(&to_value(2).unwrap(), &args);
151 assert!(result.is_ok());
152 assert_eq!(result.unwrap(), to_value("es").unwrap());
153 }
154
155 #[test]
156 fn test_pluralize_multiple_custom_singular() {
157 let mut args = HashMap::new();
158 args.insert("singular".to_string(), to_value("y").unwrap());
159 let result = pluralize(&to_value(1).unwrap(), &args);
160 assert!(result.is_ok());
161 assert_eq!(result.unwrap(), to_value("y").unwrap());
162 }
163
164 #[test]
165 fn test_round_default() {
166 let result = round(&to_value(2.1).unwrap(), &HashMap::new());
167 assert!(result.is_ok());
168 assert_eq!(result.unwrap(), to_value(2.0).unwrap());
169 }
170
171 #[test]
172 fn test_round_default_precision() {
173 let mut args = HashMap::new();
174 args.insert("precision".to_string(), to_value(2).unwrap());
175 let result = round(&to_value(3.15159265359).unwrap(), &args);
176 assert!(result.is_ok());
177 assert_eq!(result.unwrap(), to_value(3.15).unwrap());
178 }
179
180 #[test]
181 fn test_round_ceil() {
182 let mut args = HashMap::new();
183 args.insert("method".to_string(), to_value("ceil").unwrap());
184 let result = round(&to_value(2.1).unwrap(), &args);
185 assert!(result.is_ok());
186 assert_eq!(result.unwrap(), to_value(3.0).unwrap());
187 }
188
189 #[test]
190 fn test_round_ceil_precision() {
191 let mut args = HashMap::new();
192 args.insert("method".to_string(), to_value("ceil").unwrap());
193 args.insert("precision".to_string(), to_value(1).unwrap());
194 let result = round(&to_value(2.11).unwrap(), &args);
195 assert!(result.is_ok());
196 assert_eq!(result.unwrap(), to_value(2.2).unwrap());
197 }
198
199 #[test]
200 fn test_round_floor() {
201 let mut args = HashMap::new();
202 args.insert("method".to_string(), to_value("floor").unwrap());
203 let result = round(&to_value(2.1).unwrap(), &args);
204 assert!(result.is_ok());
205 assert_eq!(result.unwrap(), to_value(2.0).unwrap());
206 }
207
208 #[test]
209 fn test_round_floor_precision() {
210 let mut args = HashMap::new();
211 args.insert("method".to_string(), to_value("floor").unwrap());
212 args.insert("precision".to_string(), to_value(1).unwrap());
213 let result = round(&to_value(2.91).unwrap(), &args);
214 assert!(result.is_ok());
215 assert_eq!(result.unwrap(), to_value(2.9).unwrap());
216 }
217
218 #[cfg(feature = "builtins")]
219 #[test]
220 fn test_filesizeformat() {
221 let args = HashMap::new();
222 let result = filesizeformat(&to_value(123456789).unwrap(), &args);
223 assert!(result.is_ok());
224 assert_eq!(result.unwrap(), to_value("117.74 MB").unwrap());
225 }
226
227 #[cfg(feature = "builtins")]
228 #[test]
229 fn test_filesizeformat_binary() {
230 let mut args = HashMap::new();
231 args.insert("binary".to_string(), to_value(true).unwrap());
232 let result = filesizeformat(&to_value(123456789).unwrap(), &args);
233 assert!(result.is_ok());
234 assert_eq!(result.unwrap(), to_value("117.74 MiB").unwrap());
235 }
236}