Keats / tera

A template engine for Rust based on Jinja2/Django
http://keats.github.io/tera/
MIT License
3.43k stars 279 forks source link

Tera context error: u128 is not supported #789

Open rogerta opened 1 year ago

rogerta commented 1 year ago

I am using Tera with Rocket to render an html page. The context passed to rocket_dyn_templates::Template::render() takes a structure that contains a u128 field:

  let ident: Identity = ...;
  Template::render(
    "info",
    context! {
      ident,   // <--- contains a u128 typed field.
    },
  )

I get the following error at runtime:

Tera context error: u128 is not supported.

Note that the info template does not actually attempt to read or render the u128 field. It seems just the presence in the structure causes the error. It would be nice to add support for u128. In the meantime, what is the suggested workaround?

Versions:

$ rustup --version
rustup 1.25.1 (bb60b1e89 2022-07-12)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active `rustc` version is `rustc 1.66.1 (90743e729 2023-01-10)`

From Cargo.toml:

rocket = { version = "0.5.0-rc.2", features = [ "json", "secrets" ] }
rocket_dyn_templates = { version = "0.1.0-rc.2", features = [ "tera" ] }
serde = { version = "1.0.145", features = ["derive"] }
serde_json = "1.0.87"

Thanks!

Keats commented 1 year ago

serde_json doesn't support u128 (well JSON doesn't) so you would have to remove that field from the struct

rogerta commented 1 year ago

That's not the case. serde_json does support u128. I persist the same structure to a file as JSON with no issues.

Keats commented 1 year ago

Looks like you need to enable the "arbitrary_precision" to support u/i128? which means all numbers will now be represented as strings and would be a big breaking change.

rogerta commented 1 year ago

Thanks, adding the "arbitrary_precision" feature to the serde_json dep in my Cargo.toml file fixed the runtime error. However, the rendering of the least significant digits is incorrect. For example the value 334916158057069523337570811379465810500 renders as 334916158057069530000000000000000000000.

The workaround I'm using at the moment is to define a new structure called IdentityForTera with the same fields as Identity, but where the u128 field is declared as a String. I then implemented a conversion functionIdentityForTera::from(ident: Identity) -> IdentityForTera which converts the u128 value to a string value via ident.field.to_string(). So my code becomes:

  let ident: Identity = ...;
  let ident = IdentityForTera::from(ident);  // <--- new code
  Template::render(
    "info",
    context! {
      ident,
    },
  )

I'm just looking for a better solution if possible.

Note that the same app writes Identity to a file correctly (i.e. no runtime error and the u128 is rendered with all digits) and does not need "arbitrary_precision", I'm trying to understand why this works but tera does not:

async fn create(ident: &Identity) -> Result<()> {
  let path = ...;
  let mut file = OpenOptions::new()
    .write(true)
    .create_new(true)
    .open(&path)
    .await
    .map_err(|e| Error::Io(e))?;
  let json = serde_json::to_string(ident)
    .map_err(|e| Error::Serde(e))?;
  file
    .write_all(json.as_bytes())
    .await
    .map_err(|e| Error::Io(e))
}
Keats commented 1 year ago

For example the value 334916158057069523337570811379465810500 renders as 334916158057069530000000000000000000000.

That's because Tera itself doesn't use that feature and by default serde_json only supports up to u64. While we keep using JSON for the context, this is not fixable I think. If v2 uses something different we could have support for u128/i128.

rogerta commented 1 year ago

Sounds good, you can close this issue if you like. I'll keep using my workaround. However, I don't think this is a serde_json issue, I believe it's how Tera uses serde_json (which may be fixable in v2). serde_json does have support for i128/u128 based on closed issues and on my own experience that it writes correctly to files.

rogerta commented 1 year ago

Sorry, my bad, that link is not serde_json issue link. So I'm not sure where the problem is, but things are working for me, so I'm good.

Keats commented 1 year ago

Yes it does support u128 with the feature mentioned. However enabling that means every single number will now be stored as a string, which would be a breaking change.