sunng87 / handlebars-rust

Rust templating with Handlebars
MIT License
1.26k stars 137 forks source link

XPath helper questions #623

Closed hhalex closed 9 months ago

hhalex commented 9 months ago

Hello again,

Context: I am writing a small mock server, allowing you to define responses based on some http request data like headers, url params, url query params... and body data... So far you can access fields from the body if it is json, and the syntax integrates pretty well with the rest. But I have cases where the response depends on a xml field of the request.

I first thought of the json like approach:

-> parsing the xml and passing it as data... It is ok, it works, but my users prefer xpath, and it involves parsing all the request body, transforming it to json... It seems heavy

But I also thought of some helper addition like this:

{{xpath body.text "//el"}}
  {{@el.name}}
{{/xpath}}

I wanted to do something similar to the "each" helper but I realized everything is stored in a json fashion and then traversed, I have to mimic this behaviour.

The approach I see is the following:

  1. Compute xpath queries and inject the results like this
registry
  .render(
     TEMPLATE_KEY,
     &json!({
       "body":{
          "xpath":  {
              "//element[@test]": computeResult()
           }
        }
     }),
  )
  1. Access body.xpath["//element[@test]"] through the "xpath" helper I would define and register in HandlebarsRs

So, the helper would have no dependency to any xml-stuff and would rather be a way to bind variables to data injected from elsewhere (the json data object). The helper might be better this way, since the goal is only to query requests body

{{xpath "//el"}}
  {{@el.name}}
{{/xpath}}

WDYT ?

sunng87 commented 9 months ago

May I know the data structure of el in

  {{@el.name}}

I think you can make xpath a block helper and use it like:

{{#xpath xmlvar "//el"}}
  {{@el.name}}
{{/xpath}}

In the helper you evaluate //el against value of xmlvar and convert its result to json , the put into local variable el.

hhalex commented 9 months ago

For now el is a json object containing the parameters of the xml element, so @el.name is a string

<root>
  <el name="el1" />
  <el name="el2" />
  <el name="el3" />
</root>

Here is my first try that does not work totally (@el.name produces [Object] when called from the template)

https://gist.github.com/hhalex/aac4717c74b4de575602061f185b12e9

I copied the each helper file, imported a few local functions of handlebars to make it compile it.

sunng87 commented 9 months ago

[Object] means the value is a json object.

For now el is a json object containing the parameters of the xml element, so @el.name is a string

I think you can simplify the helper by converting matches in to a json array (because there can be multiple matches) and use each within the block, so you don't have to reimplement each:

{{#xpath xmlvar "//el"}}
  {{#each @el}}
    {{name}}
  {{/each}}
{{/xpath}}
hhalex commented 9 months ago

Yes this works totally!

Final solution: input xml

<node>
    <el name="v1" />
    <el name="v2" />
    <el name="v3" />
</node>

handlebars template

<node>
  {{#xpath body.text "//el[@name=\"v1\"]"}}
    <len>{{len @results}}</len>
    <results>
    {{#each @results}}
      <n>{{name}}</n>
    {{/each}}
    </results>
  {{/xpath}}
</node>

xml output

<node>
  <len>1</len>
  <results>
    <n>v1</n>
  </results>
</node>