Tera context error: u128 is not supported
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!
serde_json doesn't support u128 (well JSON doesn't) so you would have to remove that field from the struct
That's not the case. serde_json does support u128. I persist the same structure to a file as JSON with no issues.
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.
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))
}
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.
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.
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.
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.