diff --git a/minijinja/src/filters.rs b/minijinja/src/filters.rs index 8ecfe823..28aab7a4 100644 --- a/minijinja/src/filters.rs +++ b/minijinja/src/filters.rs @@ -754,6 +754,56 @@ mod builtins { }) } + /// indents Value with spaces + /// + /// The first optional parameter to the filter can be set to `true` to + /// indent the first line. The parameter defaults to false. + /// the second optional parameter to the filter can be set to `true` + /// to indent blank lines. The parameter defaults to false. + /// This filter is useful, if you want to template yaml-files + /// + /// ```jinja + /// example: + /// config: + /// {{ global_conifg|indent(2) }} #does not indent first line + /// {{ global_config|indent(2,true) }} #indent whole Value with two spaces + /// {{ global_config|indent(2,true,true)}} #indent whole Value and all Blank Lines value + /// ``` + #[cfg_attr(docsrs, doc(cfg(all(feature = "builtins"))))] + pub fn indent( + mut value: String, + width: usize, + indent_first_line: Option, + indent_blank_lines: Option, + ) -> String { + fn strip_trailing_newline(input: &mut String) { + if let Some(stripped) = input.strip_suffix(&['\r', '\n'][..]) { + input.truncate(stripped.len()); + } + } + + strip_trailing_newline(&mut value); + + let mut output: String = String::new(); + let mut iterator = value.split('\n'); + if !indent_first_line.unwrap_or(false) { + output.push_str(iterator.next().unwrap()); + output.push('\n'); + } + for line in iterator { + if line.is_empty() { + if indent_blank_lines.unwrap_or(false) { + output.push_str(&" ".repeat(width)); + } + } else { + output.push_str(format!("{}{}", " ".repeat(width), line).as_str()); + } + output.push('\n'); + } + strip_trailing_newline(&mut output); + output + } + /// URL encodes a value. /// /// If given a map it encodes the parameters into a query set, otherwise it diff --git a/minijinja/tests/test_filters.rs b/minijinja/tests/test_filters.rs new file mode 100644 index 00000000..9bddd492 --- /dev/null +++ b/minijinja/tests/test_filters.rs @@ -0,0 +1,50 @@ +#![cfg(feature = "builtins")] +use minijinja::filters::indent; + +#[test] +fn test_indent_one_empty_line() { + let teststring = String::from("\n"); + assert_eq!(indent(teststring, 2, None, None), String::from("")); +} + +#[test] +fn test_indent_one_line() { + let teststring = String::from("test\n"); + assert_eq!(indent(teststring, 2, None, None), String::from("test")); +} + +#[test] +fn test_indent() { + let teststring = String::from("test\ntest1\n\ntest2\n"); + assert_eq!( + indent(teststring, 2, None, None), + String::from("test\n test1\n\n test2") + ); +} + +#[test] +fn test_indent_with_indented_first_line() { + let teststring = String::from("test\ntest1\n\ntest2\n"); + assert_eq!( + indent(teststring, 2, Some(true), None), + String::from(" test\n test1\n\n test2") + ); +} + +#[test] +fn test_indent_with_indented_blank_line() { + let teststring = String::from("test\ntest1\n\ntest2\n"); + assert_eq!( + indent(teststring, 2, None, Some(true)), + String::from("test\n test1\n \n test2") + ); +} + +#[test] +fn test_indent_with_all_indented() { + let teststring = String::from("test\ntest1\n\ntest2\n"); + assert_eq!( + indent(teststring, 2, Some(true), Some(true)), + String::from(" test\n test1\n \n test2") + ); +}