Skip to content

Can't pass single-quoted strings as parameters #575

@nickvollmar

Description

@nickvollmar

Reproduction

In tests/helper_macro.rs, within the function test_macro_helper, you can add the following assertion.

    assert_eq!(
        hbs.render_template("{{tag 'html'}}", &()).unwrap(),
        "<html>"
    );
thread 'test_macro_helper' panicked at 'called `Result::unwrap()` on an `Err` value: RenderError { desc: "`tag` helper: Couldn't convert parameter t to type `str`. It's Null as JSON. Got these params: [PathAndJson { relative_path: Some(\"'html'\"), value: Missing }]", template_name: None, line_no: Some(1), column_no: Some(1), cause: None, unimplemented: false }', tests/helper_macro.rs:77:52

Cause

I believe the origin of this behavior is

if let Ok(json) = Json::from_str(s) {
Parameter::Literal(json)
} else {
Parameter::Name(s.to_owned())
}

The single-quoted literal matches the Rule::literal parser rule, but it is not valid JSON, so it becomes a Name object.

As evidence of this, here is the data structure I get by parsing the template {{tag 'html'}}:

Template {
  name: None,
  elements: [
    Expression(HelperTemplate {
      name: Name("tag"),
      params: [Name("'html'")],
      hash: {},
      block_param: None,
      template: None,
      inverse: None,
      block: false
    })
  ],
  mapping: [TemplateMapping(1, 1)]
}

Proposal

I think the way forward is to somehow extract more information from the pest parse results.

Here is a rough sketch of how I think that logic might look. As you can see, I'm out of my depth here, but this passes all the tests!

Rule::literal => {
    let param = param.into_inner().next().unwrap();
    match param.as_rule() {
        Rule::string_literal => {
            let string_inner = param.into_inner().next().unwrap();
            // don't double-escape characters - see test_string_no_escape_422
            let double_quoted = format!("\"{}\"", string_inner.as_str());
            Parameter::Literal(Json::from_str(&double_quoted).unwrap())
        }
        _ => {
            Parameter::Literal(Json::from_str(param_span.as_str()).unwrap())
        }
    }
}

(In the suggestion above I removed the Name code path, since I don't think any other JSON data type would make sense as a Name.)

Thank you very much, for reading this report and for all your work on this project.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions