diff --git a/src/template_parser.rs b/src/template_parser.rs index a34e793f9..6679f5883 100644 --- a/src/template_parser.rs +++ b/src/template_parser.rs @@ -399,9 +399,21 @@ fn parse_string_method<'a, I: 'a>( self_property: impl TemplateProperty + 'a, name: Pair, args_pair: Pair, - _parse_keyword: &impl Fn(Pair) -> TemplateParseResult>, + parse_keyword: &impl Fn(Pair) -> TemplateParseResult>, ) -> TemplateParseResult> { let property = match name.as_str() { + "contains" => { + let [needle_pair] = expect_exact_arguments(args_pair)?; + // TODO: or .try_into_string() to disable implicit type cast? + let needle_property = + parse_template_rule(needle_pair, parse_keyword)?.into_plain_text(); + Property::Boolean(chain_properties( + (self_property, needle_property), + TemplatePropertyFn(|(haystack, needle): &(String, String)| { + haystack.contains(needle) + }), + )) + } "first_line" => { expect_no_arguments(args_pair)?; Property::String(chain_properties( diff --git a/tests/test_templater.rs b/tests/test_templater.rs index 70c2380f7..d32419ced 100644 --- a/tests/test_templater.rs +++ b/tests/test_templater.rs @@ -148,6 +148,15 @@ fn test_templater_parse_error() { = Method "foo" doesn't exist for type "String" "###); + insta::assert_snapshot!(render_err(r#"description.contains()"#), @r###" + Error: Failed to parse template: --> 1:22 + | + 1 | description.contains() + | ^ + | + = Expected 1 arguments + "###); + insta::assert_snapshot!(render_err(r#"description.first_line("foo")"#), @r###" Error: Failed to parse template: --> 1:24 | @@ -206,8 +215,15 @@ fn test_templater_string_method() { let test_env = TestEnvironment::default(); test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]); let repo_path = test_env.env_root().join("repo"); + test_env.jj_cmd_success(&repo_path, &["commit", "-m=description 1"]); let render = |template| get_template_output(&test_env, &repo_path, "@-", template); + insta::assert_snapshot!(render(r#""fooo".contains("foo")"#), @"true"); + insta::assert_snapshot!(render(r#""foo".contains("fooo")"#), @"false"); + insta::assert_snapshot!(render(r#"description.contains("description")"#), @"true"); + insta::assert_snapshot!( + render(r#""description 123".contains(description.first_line())"#), @"true"); + insta::assert_snapshot!(render(r#""".first_line()"#), @""); insta::assert_snapshot!(render(r#""foo\nbar".first_line()"#), @"foo"); }