From 03b9d05b269cf59312590719d62a7bba1bc5e473 Mon Sep 17 00:00:00 2001 From: uncenter <47499684+uncenter@users.noreply.github.com> Date: Tue, 28 May 2024 11:32:26 -0400 Subject: [PATCH] Add pad filter --- docs/content/docs/_index.md | 3 ++ src/builtins/filters/string.rs | 51 ++++++++++++++++++++++++++++++++++ src/tera.rs | 1 + 3 files changed, 55 insertions(+) diff --git a/docs/content/docs/_index.md b/docs/content/docs/_index.md index 7eda240e..e1008dc9 100644 --- a/docs/content/docs/_index.md +++ b/docs/content/docs/_index.md @@ -786,6 +786,9 @@ Converts a string to uppercase. #### wordcount Returns the number of words in a string. +#### pad +Pad a string to a given length with a given fill character. + #### capitalize Returns the string with all its characters lowercased apart from the first char which is uppercased. diff --git a/src/builtins/filters/string.rs b/src/builtins/filters/string.rs index d874bd6d..ce553f97 100644 --- a/src/builtins/filters/string.rs +++ b/src/builtins/filters/string.rs @@ -368,6 +368,38 @@ pub fn split(value: &Value, args: &HashMap) -> Result { Ok(to_value(s.split(&pat).collect::>()).unwrap()) } +/// Pad the given string to a given length with a given fill character. +pub fn pad(value: &Value, args: &HashMap) -> Result { + let s = try_get_value!("pad", "value", String, value); + + let len = match args.get("len") { + Some(val) => try_get_value!("pad", "len", usize, val), + None => return Err(Error::msg("Filter `pad` expected an arg called `len`")), + }; + + let fill = match args.get("fill") { + Some(val) => try_get_value!("pad", "fill", String, val), + None => " ".to_string(), + }; + + let before = match args.get("before") { + Some(val) => try_get_value!("pad", "before", bool, val), + None => false, + }; + + let diff = len.checked_sub(s.len()).unwrap_or(0); + let mut output: String = String::with_capacity(std::cmp::max(s.len(), len)); + if !before { + output.push_str(&s); + }; + output.push_str(&fill.repeat(std::cmp::max(0, diff))); + if before { + output.push_str(&s); + }; + + Ok(to_value(output).unwrap()) +} + /// Convert the value to a signed integer number pub fn int(value: &Value, args: &HashMap) -> Result { let default = match args.get("default") { @@ -809,6 +841,25 @@ mod tests { } } + #[test] + fn test_pad() { + let tests: Vec<(&str, i32, &str, bool, &str)> = vec![ + ("abc", 4, " ", false, "abc "), + ("abc", 4, " ", true, " abc"), + ("abc", 1, " ", false, "abc"), + ("abc", 1, " ", true, "abc"), + ("abc", 5, "-", false, "abc--"), + ]; + for (input, len, fill, before, expected) in tests { + let mut args = HashMap::new(); + args.insert("len".to_string(), to_value(len).unwrap()); + args.insert("fill".to_string(), to_value(fill).unwrap()); + args.insert("before".to_string(), to_value(before).unwrap()); + let result = pad(&to_value(input).unwrap(), &args).unwrap(); + assert_eq!(result, expected); + } + } + #[test] fn test_xml_escape() { let tests = vec![ diff --git a/src/tera.rs b/src/tera.rs index 0c694f5c..e756dd40 100644 --- a/src/tera.rs +++ b/src/tera.rs @@ -696,6 +696,7 @@ impl Tera { self.register_filter("slugify", string::slugify); self.register_filter("addslashes", string::addslashes); self.register_filter("split", string::split); + self.register_filter("pad", string::pad); self.register_filter("int", string::int); self.register_filter("float", string::float);