From 25fab29d29a788f192e2b8c58fac14d5e6a23fb1 Mon Sep 17 00:00:00 2001 From: Nick Vollmar Date: Thu, 4 May 2023 20:10:06 -0500 Subject: [PATCH 1/4] Fix parameter passing for single-quoted string literals --- src/template.rs | 23 ++++++++++++++++++----- tests/escape.rs | 25 +++++++++++++++++++++++++ tests/helper_macro.rs | 8 +++++++- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/template.rs b/src/template.rs index 8cffb9297..5cb7c514b 100644 --- a/src/template.rs +++ b/src/template.rs @@ -288,11 +288,24 @@ impl Template { Parameter::Path(Path::new(param_span.as_str(), path_segs)) } Rule::literal => { - let s = param_span.as_str(); - if let Ok(json) = Json::from_str(s) { - Parameter::Literal(json) - } else { - Parameter::Name(s.to_owned()) + // Parse the parameter as a JSON literal + let param_literal = it.next().unwrap(); + match param_literal.as_rule() { + Rule::string_literal if it.peek().unwrap().as_rule() == Rule::string_inner_single_quote => { + // ...unless the parameter is a single-quoted string. + // In that case, transform it to a double-quoted string + // and then parse it as a JSON literal. + let string_inner_single_quote = it.next().unwrap(); + let double_quoted = format!( + "\"{}\"", + string_inner_single_quote.as_str() + .replace("\\'", "'") + .replace("\"", "\\\"")); + Parameter::Literal(Json::from_str(&double_quoted).unwrap()) + } + _ => { + Parameter::Literal(Json::from_str(param_span.as_str()).unwrap()) + } } } Rule::subexpression => { diff --git a/tests/escape.rs b/tests/escape.rs index 4a737f5ce..2b9512290 100644 --- a/tests/escape.rs +++ b/tests/escape.rs @@ -27,19 +27,44 @@ fn test_string_no_escape_422() { handlebars_helper!(replace: |input: str, from: str, to: str| { input.replace(from, to) }); + handlebars_helper!(echo: |input: str| { + input + }); hbs.register_helper("replace", Box::new(replace)); + hbs.register_helper("echo", Box::new(echo)); assert_eq!( r#"some\ path"#, hbs.render_template(r#"{{replace "some/path" "/" "\\ " }}"#, &()) .unwrap() ); + assert_eq!( + r#"some\ path"#, + hbs.render_template(r#"{{replace 'some/path' '/' '\\ ' }}"#, &()) + .unwrap() + ); assert_eq!( r#"some\path"#, hbs.render_template(r#"{{replace "some/path" "/" "\\" }}"#, &()) .unwrap() ); + assert_eq!( + r#"some\path"#, + hbs.render_template(r#"{{replace 'some/path' '/' '\\' }}"#, &()) + .unwrap() + ); + + assert_eq!( + r#"double-quoted \ 'with' "nesting""#, + hbs.render_template(r#"{{echo "double-quoted \\ 'with' \"nesting\""}}"#, &()) + .unwrap() + ); + assert_eq!( + r#"single-quoted \ 'with' "nesting""#, + hbs.render_template(r#"{{echo 'single-quoted \\ \'with\' "nesting"'}}"#, &()) + .unwrap() + ); } #[test] diff --git a/tests/helper_macro.rs b/tests/helper_macro.rs index 4b055d6f4..c3db7a446 100644 --- a/tests/helper_macro.rs +++ b/tests/helper_macro.rs @@ -100,5 +100,11 @@ fn test_macro_helper() { ) .unwrap(), "false" - ) + ); + + assert_eq!( + hbs.render_template("{{tag 'html'}}", &()).unwrap(), + "<html>" + ); + } From 2fdd527131b1ef938b8747f30e4c596620a582a7 Mon Sep 17 00:00:00 2001 From: Nick Vollmar Date: Fri, 5 May 2023 15:58:01 -0500 Subject: [PATCH 2/4] Fix cargo fmt errors --- src/template.rs | 14 ++++++++------ tests/helper_macro.rs | 1 - 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/template.rs b/src/template.rs index 5cb7c514b..ec9c4232b 100644 --- a/src/template.rs +++ b/src/template.rs @@ -291,21 +291,23 @@ impl Template { // Parse the parameter as a JSON literal let param_literal = it.next().unwrap(); match param_literal.as_rule() { - Rule::string_literal if it.peek().unwrap().as_rule() == Rule::string_inner_single_quote => { + Rule::string_literal + if it.peek().unwrap().as_rule() == Rule::string_inner_single_quote => + { // ...unless the parameter is a single-quoted string. // In that case, transform it to a double-quoted string // and then parse it as a JSON literal. let string_inner_single_quote = it.next().unwrap(); let double_quoted = format!( "\"{}\"", - string_inner_single_quote.as_str() + string_inner_single_quote + .as_str() .replace("\\'", "'") - .replace("\"", "\\\"")); + .replace("\"", "\\\"") + ); Parameter::Literal(Json::from_str(&double_quoted).unwrap()) } - _ => { - Parameter::Literal(Json::from_str(param_span.as_str()).unwrap()) - } + _ => Parameter::Literal(Json::from_str(param_span.as_str()).unwrap()), } } Rule::subexpression => { diff --git a/tests/helper_macro.rs b/tests/helper_macro.rs index c3db7a446..f40ab778a 100644 --- a/tests/helper_macro.rs +++ b/tests/helper_macro.rs @@ -106,5 +106,4 @@ fn test_macro_helper() { hbs.render_template("{{tag 'html'}}", &()).unwrap(), "<html>" ); - } From b81328840634eba80e7344677a5592830a6e7fca Mon Sep 17 00:00:00 2001 From: Nick Vollmar Date: Fri, 5 May 2023 16:24:45 -0500 Subject: [PATCH 3/4] Fix clippy error --- src/template.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/template.rs b/src/template.rs index ec9c4232b..45aa2c7ce 100644 --- a/src/template.rs +++ b/src/template.rs @@ -303,7 +303,7 @@ impl Template { string_inner_single_quote .as_str() .replace("\\'", "'") - .replace("\"", "\\\"") + .replace('"', "\\\"") ); Parameter::Literal(Json::from_str(&double_quoted).unwrap()) } From 21341df807cdd3fb4b4b78e8561f10580738aa16 Mon Sep 17 00:00:00 2001 From: Nick Vollmar Date: Mon, 8 May 2023 12:11:10 -0500 Subject: [PATCH 4/4] Return TemplateError on json parse failure --- src/template.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/template.rs b/src/template.rs index 45aa2c7ce..d9697b81b 100644 --- a/src/template.rs +++ b/src/template.rs @@ -290,7 +290,7 @@ impl Template { Rule::literal => { // Parse the parameter as a JSON literal let param_literal = it.next().unwrap(); - match param_literal.as_rule() { + let json_result = match param_literal.as_rule() { Rule::string_literal if it.peek().unwrap().as_rule() == Rule::string_inner_single_quote => { @@ -305,9 +305,16 @@ impl Template { .replace("\\'", "'") .replace('"', "\\\"") ); - Parameter::Literal(Json::from_str(&double_quoted).unwrap()) + Json::from_str(&double_quoted) } - _ => Parameter::Literal(Json::from_str(param_span.as_str()).unwrap()), + _ => Json::from_str(param_span.as_str()), + }; + if let Ok(json) = json_result { + Parameter::Literal(json) + } else { + return Err(TemplateError::of(TemplateErrorReason::InvalidParam( + param_span.as_str().to_owned(), + ))); } } Rule::subexpression => {