From 7d2d7718aa8467ddd5ca9753bde2c7a87ff55c9f Mon Sep 17 00:00:00 2001 From: defyrlt Date: Tue, 29 Aug 2017 23:02:13 +0300 Subject: Add `join` filter & tests for it --- askama_shared/src/filters/mod.rs | 46 +++++++++++++++++++++++++++++++++++++ askama_shared/src/generator.rs | 25 ++++++++++++++++++++ testing/templates/filters_join.html | 1 + testing/tests/filters.rs | 25 ++++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 testing/templates/filters_join.html diff --git a/askama_shared/src/filters/mod.rs b/askama_shared/src/filters/mod.rs index 73b311d..75014fa 100644 --- a/askama_shared/src/filters/mod.rs +++ b/askama_shared/src/filters/mod.rs @@ -12,6 +12,7 @@ mod json; pub use self::json::json; use std::fmt; + use super::Result; @@ -96,6 +97,27 @@ pub fn trim(s: &fmt::Display) -> Result { Ok(s.trim().to_owned()) } +/// Joins iterable into a string separated by provided argument +pub fn join(input: I, separator: S) -> Result + where T: fmt::Display, + I: Iterator, + S: AsRef +{ + let separator: &str = separator.as_ref(); + + let mut rv = String::new(); + + for (num, item) in input.enumerate() { + if num > 0 { + rv.push_str(separator); + } + + rv.push_str(&format!("{}", item)); + } + + Ok(rv) +} + #[cfg(test)] mod tests { use super::*; @@ -127,4 +149,28 @@ mod tests { fn test_trim() { assert_eq!(trim(&" Hello\tworld\t").unwrap(), "Hello\tworld"); } + + #[test] + fn test_join() { + assert_eq!(join((&["hello", "world"]).into_iter(), ", ").unwrap(), "hello, world"); + assert_eq!(join((&["hello"]).into_iter(), ", ").unwrap(), "hello"); + + let empty: &[&str] = &[]; + assert_eq!(join(empty.into_iter(), ", ").unwrap(), ""); + + let input: Vec = vec!["foo".into(), "bar".into(), "bazz".into()]; + assert_eq!(join((&input).into_iter(), ":".to_string()).unwrap(), "foo:bar:bazz"); + assert_eq!(join(input.clone().into_iter(), ":").unwrap(), "foo:bar:bazz"); + assert_eq!(join(input.clone().into_iter(), ":".to_string()).unwrap(), "foo:bar:bazz"); + + let input: &[String] = &["foo".into(), "bar".into()]; + assert_eq!(join(input.into_iter(), ":").unwrap(), "foo:bar"); + assert_eq!(join(input.into_iter(), ":".to_string()).unwrap(), "foo:bar"); + + let real: String = "blah".into(); + let input: Vec<&str> = vec![&real]; + assert_eq!(join(input.into_iter(), ";").unwrap(), "blah"); + + assert_eq!(join((&&&&&["foo", "bar"]).into_iter(), ", ").unwrap(), "foo, bar"); + } } diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index e5ffd08..836da34 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -238,10 +238,35 @@ impl<'a> Generator<'a> { self.write(")"); } + fn _visit_join_filter(&mut self, args: &[Expr]) { + // this is a separate handler because we need to generate code like + // this to force auto-deref for first argument: + // `join((&&&&&&self.s).into_iter(), ", ")` + // otherwise, we were getting errors described in PR #39 + self.write("::askama::filters::join((&"); + + for (i, arg) in args.iter().enumerate() { + if i > 0 { + self.write(", &"); + } + + self.visit_expr(arg); + + if i == 0 { + self.write(").into_iter()"); + } + } + + self.write(")?"); + } + fn visit_filter(&mut self, name: &str, args: &[Expr]) { if name == "format" { self._visit_format_filter(args); return; + } else if name == "join" { + self._visit_join_filter(args); + return; } if BUILT_IN_FILTERS.contains(&name) { diff --git a/testing/templates/filters_join.html b/testing/templates/filters_join.html new file mode 100644 index 0000000..61cc249 --- /dev/null +++ b/testing/templates/filters_join.html @@ -0,0 +1 @@ +{{ s|join(", ") }} diff --git a/testing/tests/filters.rs b/testing/tests/filters.rs index 524014e..b24ff61 100644 --- a/testing/tests/filters.rs +++ b/testing/tests/filters.rs @@ -49,3 +49,28 @@ fn test_my_filter() { let t = MyFilterTemplate { s: "foo" }; assert_eq!(t.render().unwrap(), "faa"); } + + +#[derive(Template)] +#[template(path= "filters_join.html")] +struct JoinTemplate<'a> { + s: &'a [&'a str], +} + +#[test] +fn test_join() { + let t = JoinTemplate { s: &["foo", "bar", "bazz"] }; + assert_eq!(t.render().unwrap(), "foo, bar, bazz"); +} + +#[derive(Template)] +#[template(path= "filters_join.html")] +struct VecJoinTemplate { + s: Vec +} + +#[test] +fn test_vec_join() { + let t = VecJoinTemplate { s: vec!["foo".into(), "bar".into(), "bazz".into()] }; + assert_eq!(t.render().unwrap(), "foo, bar, bazz"); +} -- cgit