1 /// Filters operating on numbers
2 use std
::collections
::HashMap
;
4 #[cfg(feature = "builtins")]
5 use humansize
::{file_size_opts, FileSize}
;
6 use serde_json
::value
::{to_value, Value}
;
8 use crate::errors
::{Error, Result}
;
10 /// Returns a plural suffix if the value is not equal to ±1, or a singular
11 /// suffix otherwise. The plural suffix defaults to `s` and the singular suffix
12 /// defaults to the empty string (i.e nothing).
13 pub fn pluralize(value
: &Value
, args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
14 let num
= try_get_value
!("pluralize", "value", f64, value
);
16 let plural
= match args
.get("plural") {
17 Some(val
) => try_get_value
!("pluralize", "plural", String
, val
),
18 None
=> "s".to_string(),
21 let singular
= match args
.get("singular") {
22 Some(val
) => try_get_value
!("pluralize", "singular", String
, val
),
23 None
=> "".to_string(),
26 // English uses plural when it isn't one
27 if (num
.abs() - 1.).abs() > ::std
::f64::EPSILON
{
28 Ok(to_value(&plural
).unwrap())
30 Ok(to_value(&singular
).unwrap())
34 /// Returns a rounded number using the `method` arg and `precision` given.
35 /// `method` defaults to `common` which will round to the nearest number.
36 /// `ceil` and `floor` are also available as method.
37 /// `precision` defaults to `0`, meaning it will round to an integer
38 pub fn round(value
: &Value
, args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
39 let num
= try_get_value
!("round", "value", f64, value
);
40 let method
= match args
.get("method") {
41 Some(val
) => try_get_value
!("round", "method", String
, val
),
42 None
=> "common".to_string(),
44 let precision
= match args
.get("precision") {
45 Some(val
) => try_get_value
!("round", "precision", i32, val
),
48 let multiplier
= if precision
== 0 { 1.0 }
else { 10.0_f64.powi(precision) }
;
50 match method
.as_ref() {
51 "common" => Ok(to_value((multiplier
* num
).round() / multiplier
).unwrap()),
52 "ceil" => Ok(to_value((multiplier
* num
).ceil() / multiplier
).unwrap()),
53 "floor" => Ok(to_value((multiplier
* num
).floor() / multiplier
).unwrap()),
54 _
=> Err(Error
::msg(format
!(
55 "Filter `round` received an incorrect value for arg `method`: got `{:?}`, \
56 only common, ceil and floor are allowed",
62 /// Returns a human-readable file size (i.e. '110 MB') from an integer
63 #[cfg(feature = "builtins")]
64 pub fn filesizeformat(value
: &Value
, _
: &HashMap
<String
, Value
>) -> Result
<Value
> {
65 let num
= try_get_value
!("filesizeformat", "value", usize, value
);
66 num
.file_size(file_size_opts
::CONVENTIONAL
)
68 Error
::msg(format
!("Filter `filesizeformat` was called on a negative number: {}", num
))
71 .map(std
::result
::Result
::unwrap
)
77 use serde_json
::value
::to_value
;
78 use std
::collections
::HashMap
;
81 fn test_pluralize_single() {
82 let result
= pluralize(&to_value(1).unwrap(), &HashMap
::new());
83 assert
!(result
.is_ok());
84 assert_eq
!(result
.unwrap(), to_value("").unwrap());
88 fn test_pluralize_multiple() {
89 let result
= pluralize(&to_value(2).unwrap(), &HashMap
::new());
90 assert
!(result
.is_ok());
91 assert_eq
!(result
.unwrap(), to_value("s").unwrap());
95 fn test_pluralize_zero() {
96 let result
= pluralize(&to_value(0).unwrap(), &HashMap
::new());
97 assert
!(result
.is_ok());
98 assert_eq
!(result
.unwrap(), to_value("s").unwrap());
102 fn test_pluralize_multiple_custom_plural() {
103 let mut args
= HashMap
::new();
104 args
.insert("plural".to_string(), to_value("es").unwrap());
105 let result
= pluralize(&to_value(2).unwrap(), &args
);
106 assert
!(result
.is_ok());
107 assert_eq
!(result
.unwrap(), to_value("es").unwrap());
111 fn test_pluralize_multiple_custom_singular() {
112 let mut args
= HashMap
::new();
113 args
.insert("singular".to_string(), to_value("y").unwrap());
114 let result
= pluralize(&to_value(1).unwrap(), &args
);
115 assert
!(result
.is_ok());
116 assert_eq
!(result
.unwrap(), to_value("y").unwrap());
120 fn test_round_default() {
121 let result
= round(&to_value(2.1).unwrap(), &HashMap
::new());
122 assert
!(result
.is_ok());
123 assert_eq
!(result
.unwrap(), to_value(2.0).unwrap());
127 fn test_round_default_precision() {
128 let mut args
= HashMap
::new();
129 args
.insert("precision".to_string(), to_value(2).unwrap());
130 let result
= round(&to_value(3.15159265359).unwrap(), &args
);
131 assert
!(result
.is_ok());
132 assert_eq
!(result
.unwrap(), to_value(3.15).unwrap());
136 fn test_round_ceil() {
137 let mut args
= HashMap
::new();
138 args
.insert("method".to_string(), to_value("ceil").unwrap());
139 let result
= round(&to_value(2.1).unwrap(), &args
);
140 assert
!(result
.is_ok());
141 assert_eq
!(result
.unwrap(), to_value(3.0).unwrap());
145 fn test_round_ceil_precision() {
146 let mut args
= HashMap
::new();
147 args
.insert("method".to_string(), to_value("ceil").unwrap());
148 args
.insert("precision".to_string(), to_value(1).unwrap());
149 let result
= round(&to_value(2.11).unwrap(), &args
);
150 assert
!(result
.is_ok());
151 assert_eq
!(result
.unwrap(), to_value(2.2).unwrap());
155 fn test_round_floor() {
156 let mut args
= HashMap
::new();
157 args
.insert("method".to_string(), to_value("floor").unwrap());
158 let result
= round(&to_value(2.1).unwrap(), &args
);
159 assert
!(result
.is_ok());
160 assert_eq
!(result
.unwrap(), to_value(2.0).unwrap());
164 fn test_round_floor_precision() {
165 let mut args
= HashMap
::new();
166 args
.insert("method".to_string(), to_value("floor").unwrap());
167 args
.insert("precision".to_string(), to_value(1).unwrap());
168 let result
= round(&to_value(2.91).unwrap(), &args
);
169 assert
!(result
.is_ok());
170 assert_eq
!(result
.unwrap(), to_value(2.9).unwrap());
173 #[cfg(feature = "builtins")]
175 fn test_filesizeformat() {
176 let args
= HashMap
::new();
177 let result
= filesizeformat(&to_value(123456789).unwrap(), &args
);
178 assert
!(result
.is_ok());
179 assert_eq
!(result
.unwrap(), to_value("117.74 MB").unwrap());