]> git.proxmox.com Git - rustc.git/blob - vendor/tera/src/builtins/filters/number.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / vendor / tera / src / builtins / filters / number.rs
1 /// Filters operating on numbers
2 use std::collections::HashMap;
3
4 #[cfg(feature = "builtins")]
5 use humansize::{file_size_opts, FileSize};
6 use serde_json::value::{to_value, Value};
7
8 use crate::errors::{Error, Result};
9
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);
15
16 let plural = match args.get("plural") {
17 Some(val) => try_get_value!("pluralize", "plural", String, val),
18 None => "s".to_string(),
19 };
20
21 let singular = match args.get("singular") {
22 Some(val) => try_get_value!("pluralize", "singular", String, val),
23 None => "".to_string(),
24 };
25
26 // English uses plural when it isn't one
27 if (num.abs() - 1.).abs() > ::std::f64::EPSILON {
28 Ok(to_value(&plural).unwrap())
29 } else {
30 Ok(to_value(&singular).unwrap())
31 }
32 }
33
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(),
43 };
44 let precision = match args.get("precision") {
45 Some(val) => try_get_value!("round", "precision", i32, val),
46 None => 0,
47 };
48 let multiplier = if precision == 0 { 1.0 } else { 10.0_f64.powi(precision) };
49
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",
57 method
58 ))),
59 }
60 }
61
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)
67 .map_err(|_| {
68 Error::msg(format!("Filter `filesizeformat` was called on a negative number: {}", num))
69 })
70 .map(to_value)
71 .map(std::result::Result::unwrap)
72 }
73
74 #[cfg(test)]
75 mod tests {
76 use super::*;
77 use serde_json::value::to_value;
78 use std::collections::HashMap;
79
80 #[test]
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());
85 }
86
87 #[test]
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());
92 }
93
94 #[test]
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());
99 }
100
101 #[test]
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());
108 }
109
110 #[test]
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());
117 }
118
119 #[test]
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());
124 }
125
126 #[test]
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());
133 }
134
135 #[test]
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());
142 }
143
144 #[test]
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());
152 }
153
154 #[test]
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());
161 }
162
163 #[test]
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());
171 }
172
173 #[cfg(feature = "builtins")]
174 #[test]
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());
180 }
181 }