Add num_format
filter (#1460)
* Add `num_format` filter for displaying formatted numbers * Register the filter * Update docs * Make `locale` argument required * Revert "Make `locale` argument required" This reverts commit 9cdbf285915f0257d1b9157572588b0ce6698a44. * Pull the default locale from the site config * Add note about defaults to the docs * Add missing borrow
This commit is contained in:
parent
0a7692ad85
commit
93900fb623
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -57,6 +57,15 @@ version = "0.1.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
|
||||
dependencies = [
|
||||
"nodrop",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
|
@ -1163,7 +1172,7 @@ version = "0.7.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"arrayvec 0.5.2",
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"ryu",
|
||||
|
@ -1533,6 +1542,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodrop"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
|
@ -1627,6 +1642,16 @@ dependencies = [
|
|||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-format"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.12",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
|
@ -2571,6 +2596,7 @@ dependencies = [
|
|||
"library",
|
||||
"mockito",
|
||||
"nom-bibtex",
|
||||
"num-format",
|
||||
"rendering",
|
||||
"reqwest",
|
||||
"serde",
|
||||
|
|
|
@ -12,6 +12,10 @@ pub fn register_early_global_fns(site: &mut Site) -> TeraResult<()> {
|
|||
site.permalinks.clone(),
|
||||
)?,
|
||||
);
|
||||
site.tera.register_filter(
|
||||
"num_format",
|
||||
filters::NumFormatFilter::new(&site.config.default_language),
|
||||
);
|
||||
|
||||
site.tera.register_function(
|
||||
"get_url",
|
||||
|
|
|
@ -16,6 +16,7 @@ serde_derive = "1"
|
|||
sha2 = "0.9"
|
||||
url = "2"
|
||||
nom-bibtex = "0.3"
|
||||
num-format = "0.4"
|
||||
|
||||
errors = { path = "../errors" }
|
||||
utils = { path = "../utils" }
|
||||
|
|
|
@ -6,7 +6,10 @@ use std::path::PathBuf;
|
|||
use base64::{decode, encode};
|
||||
use config::Config;
|
||||
use rendering::{render_content, RenderContext};
|
||||
use tera::{to_value, try_get_value, Filter as TeraFilter, Result as TeraResult, Tera, Value};
|
||||
use tera::{
|
||||
to_value, try_get_value, Error as TeraError, Filter as TeraFilter, Result as TeraResult, Tera,
|
||||
Value,
|
||||
};
|
||||
|
||||
use crate::load_tera;
|
||||
|
||||
|
@ -72,13 +75,43 @@ pub fn base64_decode<S: BuildHasher>(
|
|||
Ok(to_value(&String::from_utf8(decode(s.as_bytes()).unwrap()).unwrap()).unwrap())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NumFormatFilter {
|
||||
default_language: String,
|
||||
}
|
||||
|
||||
impl NumFormatFilter {
|
||||
pub fn new<S: Into<String>>(default_language: S) -> Self {
|
||||
Self { default_language: default_language.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl TeraFilter for NumFormatFilter {
|
||||
fn filter(&self, value: &Value, args: &HashMap<String, Value>) -> TeraResult<Value> {
|
||||
use num_format::{Locale, ToFormattedString};
|
||||
|
||||
let num = try_get_value!("num_format", "value", i64, value);
|
||||
let locale = match args.get("locale") {
|
||||
Some(locale) => try_get_value!("num_format", "locale", String, locale),
|
||||
None => self.default_language.clone(),
|
||||
};
|
||||
let locale = Locale::from_name(&locale).map_err(|_| {
|
||||
TeraError::msg(format!(
|
||||
"Filter `num_format` was called with an invalid `locale` argument: `{}`.",
|
||||
locale
|
||||
))
|
||||
})?;
|
||||
Ok(to_value(num.to_formatted_string(&locale)).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use tera::{to_value, Filter};
|
||||
|
||||
use super::{base64_decode, base64_encode, MarkdownFilter};
|
||||
use super::{base64_decode, base64_encode, MarkdownFilter, NumFormatFilter};
|
||||
use config::Config;
|
||||
|
||||
#[test]
|
||||
|
@ -201,4 +234,44 @@ mod tests {
|
|||
assert_eq!(result.unwrap(), to_value(expected).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_format_filter() {
|
||||
let tests = vec![
|
||||
(100, "100"),
|
||||
(1_000, "1,000"),
|
||||
(10_000, "10,000"),
|
||||
(100_000, "100,000"),
|
||||
(1_000_000, "1,000,000"),
|
||||
];
|
||||
|
||||
for (input, expected) in tests {
|
||||
let args = HashMap::new();
|
||||
let result = NumFormatFilter::new("en").filter(&to_value(input).unwrap(), &args);
|
||||
let result = dbg!(result);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), to_value(expected).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_format_filter_with_locale() {
|
||||
let tests = vec![
|
||||
("en", 1_000_000, "1,000,000"),
|
||||
("en-IN", 1_000_000, "10,00,000"),
|
||||
// Note:
|
||||
// U+202F is the "NARROW NO-BREAK SPACE" code point.
|
||||
// When displayed to the screen, it looks like a space.
|
||||
("fr", 1_000_000, "1\u{202f}000\u{202f}000"),
|
||||
];
|
||||
|
||||
for (locale, input, expected) in tests {
|
||||
let mut args = HashMap::new();
|
||||
args.insert("locale".to_string(), to_value(locale).unwrap());
|
||||
let result = NumFormatFilter::new("en").filter(&to_value(input).unwrap(), &args);
|
||||
let result = dbg!(result);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), to_value(expected).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,22 @@ Encode the variable to base64.
|
|||
### base64_decode
|
||||
Decode the variable from base64.
|
||||
|
||||
### num_format
|
||||
Format a number into its string representation.
|
||||
|
||||
```jinja2
|
||||
{{ 1000000 | num_format }}
|
||||
<!-- 1,000,000 -->
|
||||
```
|
||||
|
||||
By default this will format the number using the locale set by `config.default_language` in config.toml.
|
||||
|
||||
To format a number for a specific locale, you can use the `locale` argument and pass the name of the desired locale:
|
||||
|
||||
```jinja2
|
||||
{{ 1000000 | num_format(locale="en-IN") }}
|
||||
<!-- 10,00,000 -->
|
||||
```
|
||||
|
||||
## Built-in functions
|
||||
|
||||
|
|
Loading…
Reference in a new issue