use super::{Ast, Expr, Node, Syntax, Whitespace, Ws}; fn check_ws_split(s: &str, res: &(&str, &str, &str)) { match super::split_ws_parts(s) { Node::Lit(lws, s, rws) => { assert_eq!(lws, res.0); assert_eq!(s, res.1); assert_eq!(rws, res.2); } _ => { panic!("fail"); } } } #[test] fn test_ws_splitter() { check_ws_split("", &("", "", "")); check_ws_split("a", &("", "a", "")); check_ws_split("\ta", &("\t", "a", "")); check_ws_split("b\n", &("", "b", "\n")); check_ws_split(" \t\r\n", &(" \t\r\n", "", "")); } #[test] #[should_panic] fn test_invalid_block() { Ast::from_str("{% extend \"blah\" %}", &Syntax::default()).unwrap(); } #[test] fn test_parse_filter() { use Expr::*; let syntax = Syntax::default(); assert_eq!( Ast::from_str("{{ strvar|e }}", &syntax).unwrap().nodes, vec![Node::Expr(Ws(None, None), Filter("e", vec![Var("strvar")]),)], ); assert_eq!( Ast::from_str("{{ 2|abs }}", &syntax).unwrap().nodes, vec![Node::Expr(Ws(None, None), Filter("abs", vec![NumLit("2")]),)], ); assert_eq!( Ast::from_str("{{ -2|abs }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Filter("abs", vec![Unary("-", NumLit("2").into())]), )], ); assert_eq!( Ast::from_str("{{ (1 - 2)|abs }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Filter( "abs", vec![Group( BinOp("-", NumLit("1").into(), NumLit("2").into()).into() )] ), )], ); } #[test] fn test_parse_numbers() { let syntax = Syntax::default(); assert_eq!( Ast::from_str("{{ 2 }}", &syntax).unwrap().nodes, vec![Node::Expr(Ws(None, None), Expr::NumLit("2"),)], ); assert_eq!( Ast::from_str("{{ 2.5 }}", &syntax).unwrap().nodes, vec![Node::Expr(Ws(None, None), Expr::NumLit("2.5"),)], ); } #[test] fn test_parse_var() { let s = Syntax::default(); assert_eq!( Ast::from_str("{{ foo }}", &s).unwrap().nodes, vec![Node::Expr(Ws(None, None), Expr::Var("foo"))], ); assert_eq!( Ast::from_str("{{ foo_bar }}", &s).unwrap().nodes, vec![Node::Expr(Ws(None, None), Expr::Var("foo_bar"))], ); assert_eq!( Ast::from_str("{{ none }}", &s).unwrap().nodes, vec![Node::Expr(Ws(None, None), Expr::Var("none"))], ); } #[test] fn test_parse_const() { let s = Syntax::default(); assert_eq!( Ast::from_str("{{ FOO }}", &s).unwrap().nodes, vec![Node::Expr(Ws(None, None), Expr::Path(vec!["FOO"]))], ); assert_eq!( Ast::from_str("{{ FOO_BAR }}", &s).unwrap().nodes, vec![Node::Expr(Ws(None, None), Expr::Path(vec!["FOO_BAR"]))], ); assert_eq!( Ast::from_str("{{ NONE }}", &s).unwrap().nodes, vec![Node::Expr(Ws(None, None), Expr::Path(vec!["NONE"]))], ); } #[test] fn test_parse_path() { let s = Syntax::default(); assert_eq!( Ast::from_str("{{ None }}", &s).unwrap().nodes, vec![Node::Expr(Ws(None, None), Expr::Path(vec!["None"]))], ); assert_eq!( Ast::from_str("{{ Some(123) }}", &s).unwrap().nodes, vec![Node::Expr( Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["Some"])), vec![Expr::NumLit("123")] ), )], ); assert_eq!( Ast::from_str("{{ Ok(123) }}", &s).unwrap().nodes, vec![Node::Expr( Ws(None, None), Expr::Call(Box::new(Expr::Path(vec!["Ok"])), vec![Expr::NumLit("123")]), )], ); assert_eq!( Ast::from_str("{{ Err(123) }}", &s).unwrap().nodes, vec![Node::Expr( Ws(None, None), Expr::Call(Box::new(Expr::Path(vec!["Err"])), vec![Expr::NumLit("123")]), )], ); } #[test] fn test_parse_var_call() { assert_eq!( Ast::from_str("{{ function(\"123\", 3) }}", &Syntax::default()) .unwrap() .nodes, vec![Node::Expr( Ws(None, None), Expr::Call( Box::new(Expr::Var("function")), vec![Expr::StrLit("123"), Expr::NumLit("3")] ), )], ); } #[test] fn test_parse_path_call() { let s = Syntax::default(); assert_eq!( Ast::from_str("{{ Option::None }}", &s).unwrap().nodes, vec![Node::Expr( Ws(None, None), Expr::Path(vec!["Option", "None"]) )], ); assert_eq!( Ast::from_str("{{ Option::Some(123) }}", &s).unwrap().nodes, vec![Node::Expr( Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["Option", "Some"])), vec![Expr::NumLit("123")], ), )], ); assert_eq!( Ast::from_str("{{ self::function(\"123\", 3) }}", &s) .unwrap() .nodes, vec![Node::Expr( Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["self", "function"])), vec![Expr::StrLit("123"), Expr::NumLit("3")], ), )], ); } #[test] fn test_parse_root_path() { let syntax = Syntax::default(); assert_eq!( Ast::from_str("{{ std::string::String::new() }}", &syntax) .unwrap() .nodes, vec![Node::Expr( Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["std", "string", "String", "new"])), vec![] ), )], ); assert_eq!( Ast::from_str("{{ ::std::string::String::new() }}", &syntax) .unwrap() .nodes, vec![Node::Expr( Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["", "std", "string", "String", "new"])), vec![] ), )], ); } #[test] fn test_rust_macro() { let syntax = Syntax::default(); assert_eq!( Ast::from_str("{{ vec!(1, 2, 3) }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Expr::RustMacro(vec!["vec"], "1, 2, 3",), )], ); assert_eq!( Ast::from_str("{{ alloc::vec!(1, 2, 3) }}", &syntax) .unwrap() .nodes, vec![Node::Expr( Ws(None, None), Expr::RustMacro(vec!["alloc", "vec"], "1, 2, 3",), )], ); assert_eq!( Ast::from_str("{{a!()}}", &syntax).unwrap().nodes, [Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))], ); assert_eq!( Ast::from_str("{{a !()}}", &syntax).unwrap().nodes, [Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))], ); assert_eq!( Ast::from_str("{{a! ()}}", &syntax).unwrap().nodes, [Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))], ); assert_eq!( Ast::from_str("{{a ! ()}}", &syntax).unwrap().nodes, [Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))], ); assert_eq!( Ast::from_str("{{A!()}}", &syntax).unwrap().nodes, [Node::Expr(Ws(None, None), Expr::RustMacro(vec!["A"], ""),)], ); assert_eq!( &*Ast::from_str("{{a.b.c!( hello )}}", &syntax) .unwrap_err() .to_string(), "problems parsing template source at row 1, column 7 near:\n\"!( hello )}}\"", ); } #[test] fn change_delimiters_parse_filter() { let syntax = Syntax { expr_start: "{=", expr_end: "=}", ..Syntax::default() }; Ast::from_str("{= strvar|e =}", &syntax).unwrap(); } #[test] fn test_precedence() { use Expr::*; let syntax = Syntax::default(); assert_eq!( Ast::from_str("{{ a + b == c }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), BinOp( "==", BinOp("+", Var("a").into(), Var("b").into()).into(), Var("c").into(), ) )], ); assert_eq!( Ast::from_str("{{ a + b * c - d / e }}", &syntax) .unwrap() .nodes, vec![Node::Expr( Ws(None, None), BinOp( "-", BinOp( "+", Var("a").into(), BinOp("*", Var("b").into(), Var("c").into()).into(), ) .into(), BinOp("/", Var("d").into(), Var("e").into()).into(), ) )], ); assert_eq!( Ast::from_str("{{ a * (b + c) / -d }}", &syntax) .unwrap() .nodes, vec![Node::Expr( Ws(None, None), BinOp( "/", BinOp( "*", Var("a").into(), Group(BinOp("+", Var("b").into(), Var("c").into()).into()).into() ) .into(), Unary("-", Var("d").into()).into() ) )], ); assert_eq!( Ast::from_str("{{ a || b && c || d && e }}", &syntax) .unwrap() .nodes, vec![Node::Expr( Ws(None, None), BinOp( "||", BinOp( "||", Var("a").into(), BinOp("&&", Var("b").into(), Var("c").into()).into(), ) .into(), BinOp("&&", Var("d").into(), Var("e").into()).into(), ) )], ); } #[test] fn test_associativity() { use Expr::*; let syntax = Syntax::default(); assert_eq!( Ast::from_str("{{ a + b + c }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), BinOp( "+", BinOp("+", Var("a").into(), Var("b").into()).into(), Var("c").into() ) )], ); assert_eq!( Ast::from_str("{{ a * b * c }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), BinOp( "*", BinOp("*", Var("a").into(), Var("b").into()).into(), Var("c").into() ) )], ); assert_eq!( Ast::from_str("{{ a && b && c }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), BinOp( "&&", BinOp("&&", Var("a").into(), Var("b").into()).into(), Var("c").into() ) )], ); assert_eq!( Ast::from_str("{{ a + b - c + d }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), BinOp( "+", BinOp( "-", BinOp("+", Var("a").into(), Var("b").into()).into(), Var("c").into() ) .into(), Var("d").into() ) )], ); assert_eq!( Ast::from_str("{{ a == b != c > d > e == f }}", &syntax) .unwrap() .nodes, vec![Node::Expr( Ws(None, None), BinOp( "==", BinOp( ">", BinOp( ">", BinOp( "!=", BinOp("==", Var("a").into(), Var("b").into()).into(), Var("c").into() ) .into(), Var("d").into() ) .into(), Var("e").into() ) .into(), Var("f").into() ) )], ); } #[test] fn test_odd_calls() { use Expr::*; let syntax = Syntax::default(); assert_eq!( Ast::from_str("{{ a[b](c) }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Call( Box::new(Index(Box::new(Var("a")), Box::new(Var("b")))), vec![Var("c")], ), )], ); assert_eq!( Ast::from_str("{{ (a + b)(c) }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Call( Box::new(Group(Box::new(BinOp( "+", Box::new(Var("a")), Box::new(Var("b")) )))), vec![Var("c")], ), )], ); assert_eq!( Ast::from_str("{{ a + b(c) }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), BinOp( "+", Box::new(Var("a")), Box::new(Call(Box::new(Var("b")), vec![Var("c")])), ), )], ); assert_eq!( Ast::from_str("{{ (-a)(b) }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Call( Box::new(Group(Box::new(Unary("-", Box::new(Var("a")))))), vec![Var("b")], ), )], ); assert_eq!( Ast::from_str("{{ -a(b) }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Unary("-", Box::new(Call(Box::new(Var("a")), vec![Var("b")])),), )], ); } #[test] fn test_parse_comments() { let s = &Syntax::default(); assert_eq!( Ast::from_str("{##}", s).unwrap().nodes, vec![Node::Comment(Ws(None, None))], ); assert_eq!( Ast::from_str("{#- #}", s).unwrap().nodes, vec![Node::Comment(Ws(Some(Whitespace::Suppress), None))], ); assert_eq!( Ast::from_str("{# -#}", s).unwrap().nodes, vec![Node::Comment(Ws(None, Some(Whitespace::Suppress)))], ); assert_eq!( Ast::from_str("{#--#}", s).unwrap().nodes, vec![Node::Comment(Ws( Some(Whitespace::Suppress), Some(Whitespace::Suppress) ))], ); assert_eq!( Ast::from_str("{#- foo\n bar -#}", s).unwrap().nodes, vec![Node::Comment(Ws( Some(Whitespace::Suppress), Some(Whitespace::Suppress) ))], ); assert_eq!( Ast::from_str("{#- foo\n {#- bar\n -#} baz -#}", s) .unwrap() .nodes, vec![Node::Comment(Ws( Some(Whitespace::Suppress), Some(Whitespace::Suppress) ))], ); assert_eq!( Ast::from_str("{#+ #}", s).unwrap().nodes, vec![Node::Comment(Ws(Some(Whitespace::Preserve), None))], ); assert_eq!( Ast::from_str("{# +#}", s).unwrap().nodes, vec![Node::Comment(Ws(None, Some(Whitespace::Preserve)))], ); assert_eq!( Ast::from_str("{#++#}", s).unwrap().nodes, vec![Node::Comment(Ws( Some(Whitespace::Preserve), Some(Whitespace::Preserve) ))], ); assert_eq!( Ast::from_str("{#+ foo\n bar +#}", s).unwrap().nodes, vec![Node::Comment(Ws( Some(Whitespace::Preserve), Some(Whitespace::Preserve) ))], ); assert_eq!( Ast::from_str("{#+ foo\n {#+ bar\n +#} baz -+#}", s) .unwrap() .nodes, vec![Node::Comment(Ws( Some(Whitespace::Preserve), Some(Whitespace::Preserve) ))], ); assert_eq!( Ast::from_str("{#~ #}", s).unwrap().nodes, vec![Node::Comment(Ws(Some(Whitespace::Minimize), None))], ); assert_eq!( Ast::from_str("{# ~#}", s).unwrap().nodes, vec![Node::Comment(Ws(None, Some(Whitespace::Minimize)))], ); assert_eq!( Ast::from_str("{#~~#}", s).unwrap().nodes, vec![Node::Comment(Ws( Some(Whitespace::Minimize), Some(Whitespace::Minimize) ))], ); assert_eq!( Ast::from_str("{#~ foo\n bar ~#}", s).unwrap().nodes, vec![Node::Comment(Ws( Some(Whitespace::Minimize), Some(Whitespace::Minimize) ))], ); assert_eq!( Ast::from_str("{#~ foo\n {#~ bar\n ~#} baz -~#}", s) .unwrap() .nodes, vec![Node::Comment(Ws( Some(Whitespace::Minimize), Some(Whitespace::Minimize) ))], ); assert_eq!( Ast::from_str("{# foo {# bar #} {# {# baz #} qux #} #}", s) .unwrap() .nodes, vec![Node::Comment(Ws(None, None))], ); } #[test] fn test_parse_tuple() { use super::Expr::*; let syntax = Syntax::default(); assert_eq!( Ast::from_str("{{ () }}", &syntax).unwrap().nodes, vec![Node::Expr(Ws(None, None), Tuple(vec![]),)], ); assert_eq!( Ast::from_str("{{ (1) }}", &syntax).unwrap().nodes, vec![Node::Expr(Ws(None, None), Group(Box::new(NumLit("1"))),)], ); assert_eq!( Ast::from_str("{{ (1,) }}", &syntax).unwrap().nodes, vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( Ast::from_str("{{ (1, ) }}", &syntax).unwrap().nodes, vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( Ast::from_str("{{ (1 ,) }}", &syntax).unwrap().nodes, vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( Ast::from_str("{{ (1 , ) }}", &syntax).unwrap().nodes, vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( Ast::from_str("{{ (1, 2) }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Tuple(vec![NumLit("1"), NumLit("2")]), )], ); assert_eq!( Ast::from_str("{{ (1, 2,) }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Tuple(vec![NumLit("1"), NumLit("2")]), )], ); assert_eq!( Ast::from_str("{{ (1, 2, 3) }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Tuple(vec![NumLit("1"), NumLit("2"), NumLit("3")]), )], ); assert_eq!( Ast::from_str("{{ ()|abs }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Filter("abs", vec![Tuple(vec![])]), )], ); assert_eq!( Ast::from_str("{{ () | abs }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), BinOp("|", Box::new(Tuple(vec![])), Box::new(Var("abs"))), )], ); assert_eq!( Ast::from_str("{{ (1)|abs }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Filter("abs", vec![Group(Box::new(NumLit("1")))]), )], ); assert_eq!( Ast::from_str("{{ (1) | abs }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), BinOp( "|", Box::new(Group(Box::new(NumLit("1")))), Box::new(Var("abs")) ), )], ); assert_eq!( Ast::from_str("{{ (1,)|abs }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Filter("abs", vec![Tuple(vec![NumLit("1")])]), )], ); assert_eq!( Ast::from_str("{{ (1,) | abs }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), BinOp( "|", Box::new(Tuple(vec![NumLit("1")])), Box::new(Var("abs")) ), )], ); assert_eq!( Ast::from_str("{{ (1, 2)|abs }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), Filter("abs", vec![Tuple(vec![NumLit("1"), NumLit("2")])]), )], ); assert_eq!( Ast::from_str("{{ (1, 2) | abs }}", &syntax).unwrap().nodes, vec![Node::Expr( Ws(None, None), BinOp( "|", Box::new(Tuple(vec![NumLit("1"), NumLit("2")])), Box::new(Var("abs")) ), )], ); } #[test] fn test_missing_space_after_kw() { let syntax = Syntax::default(); let err = Ast::from_str("{%leta=b%}", &syntax).unwrap_err(); assert!(matches!( &*err.to_string(), "unable to parse template:\n\n\"{%leta=b%}\"" )); }