mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-27 04:50:40 +00:00
generator: do argument coercion for |truncate
/ |center
Don't simply pass any arguments to the filter. The error message won't be useful otherwise. Also ensure that the argument is a `usize`.
This commit is contained in:
parent
de9d6b7d0e
commit
4be302338a
@ -5,7 +5,7 @@ use parser::{Expr, IntKind, Num, Span, StrLit, StrPrefix, TyGenerics, WithSpan};
|
||||
use super::{DisplayWrap, Generator, TargetIsize, TargetUsize};
|
||||
use crate::heritage::Context;
|
||||
use crate::integration::Buffer;
|
||||
use crate::{BUILTIN_FILTERS, BUILTIN_FILTERS_NEED_ALLOC, CompileError, MsgValidEscapers};
|
||||
use crate::{BUILTIN_FILTERS, CompileError, MsgValidEscapers};
|
||||
|
||||
impl<'a> Generator<'a, '_> {
|
||||
pub(super) fn visit_filter(
|
||||
@ -18,6 +18,7 @@ impl<'a> Generator<'a, '_> {
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
let filter = match name {
|
||||
"center" => Self::visit_center_filter,
|
||||
"deref" => Self::visit_deref_filter,
|
||||
"escape" | "e" => Self::visit_escape_filter,
|
||||
"filesizeformat" => Self::visit_humansize,
|
||||
@ -32,6 +33,7 @@ impl<'a> Generator<'a, '_> {
|
||||
"pluralize" => Self::visit_pluralize_filter,
|
||||
"ref" => Self::visit_ref_filter,
|
||||
"safe" => Self::visit_safe_filter,
|
||||
"truncate" => Self::visit_truncate_filter,
|
||||
"urlencode" => Self::visit_urlencode_filter,
|
||||
"urlencode_strict" => Self::visit_urlencode_strict_filter,
|
||||
"value" => return self.visit_value(ctx, buf, args, generics, node, "`value` filter"),
|
||||
@ -79,9 +81,6 @@ impl<'a> Generator<'a, '_> {
|
||||
generics: &[WithSpan<'a, TyGenerics<'a>>],
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
if BUILTIN_FILTERS_NEED_ALLOC.contains(&name) {
|
||||
ensure_filter_has_feature_alloc(ctx, name, node)?;
|
||||
}
|
||||
if !generics.is_empty() {
|
||||
return Err(
|
||||
ctx.generate_error(format_args!("unexpected generics on filter `{name}`"), node)
|
||||
@ -516,6 +515,60 @@ impl<'a> Generator<'a, '_> {
|
||||
buf.write(")?");
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn visit_center_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
args: &[WithSpan<'a, Expr<'a>>],
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
self.visit_center_truncate_filter(ctx, buf, args, node, "center")
|
||||
}
|
||||
|
||||
fn visit_truncate_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
args: &[WithSpan<'a, Expr<'a>>],
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
self.visit_center_truncate_filter(ctx, buf, args, node, "truncate")
|
||||
}
|
||||
|
||||
fn visit_center_truncate_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
args: &[WithSpan<'a, Expr<'a>>],
|
||||
node: Span<'_>,
|
||||
name: &str,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
ensure_filter_has_feature_alloc(ctx, name, node)?;
|
||||
let [arg, length] = args else {
|
||||
return Err(ctx.generate_error(
|
||||
format_args!("`{name}` filter needs one argument, the `length`"),
|
||||
node,
|
||||
));
|
||||
};
|
||||
|
||||
buf.write(format_args!("askama::filters::{name}("));
|
||||
self.visit_arg(ctx, buf, arg)?;
|
||||
buf.write(
|
||||
"\
|
||||
,\
|
||||
askama::helpers::core::primitive::usize::try_from(\
|
||||
askama::helpers::get_primitive_value(&(",
|
||||
);
|
||||
self.visit_arg(ctx, buf, length)?;
|
||||
buf.write(
|
||||
"\
|
||||
))\
|
||||
).map_err(|_| askama::Error::Fmt)?\
|
||||
)?",
|
||||
);
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_filter_has_feature_alloc(
|
||||
|
@ -602,15 +602,10 @@ pub(crate) use {fmt_left, fmt_right};
|
||||
// Askama or should refer to a local `filters` module.
|
||||
const BUILTIN_FILTERS: &[&str] = &[
|
||||
"capitalize",
|
||||
"center",
|
||||
"lower",
|
||||
"lowercase",
|
||||
"title",
|
||||
"trim",
|
||||
"truncate",
|
||||
"upper",
|
||||
"uppercase",
|
||||
];
|
||||
|
||||
// Built-in filters that need the `alloc` feature.
|
||||
const BUILTIN_FILTERS_NEED_ALLOC: &[&str] = &["center", "truncate"];
|
||||
|
@ -3,6 +3,7 @@
|
||||
extern crate serde_json;
|
||||
|
||||
use askama::Template;
|
||||
use assert_matches::assert_matches;
|
||||
#[cfg(feature = "serde_json")]
|
||||
use serde_json::Value;
|
||||
|
||||
@ -298,6 +299,32 @@ fn test_filter_truncate() {
|
||||
foo: "alpha bar".into(),
|
||||
};
|
||||
assert_eq!(t.render().unwrap(), "alpha baralpha...");
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = "{{ foo | truncate(length) }}", ext = "txt")]
|
||||
struct TruncateFilterLength<'a> {
|
||||
foo: String,
|
||||
length: &'a &'a &'a i32,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
TruncateFilterLength {
|
||||
foo: "alpha bar".into(),
|
||||
length: &&&5,
|
||||
}
|
||||
.render()
|
||||
.unwrap(),
|
||||
"alpha..."
|
||||
);
|
||||
assert_matches!(
|
||||
TruncateFilterLength {
|
||||
foo: "alpha bar".into(),
|
||||
length: &&&-5,
|
||||
}
|
||||
.render()
|
||||
.unwrap_err(),
|
||||
askama::Error::Fmt
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde_json")]
|
||||
|
24
testing/tests/ui/truncate.rs
Normal file
24
testing/tests/ui/truncate.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use askama::Template;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ text | truncate }}"#, ext = "html")]
|
||||
struct NoArgument<'a> {
|
||||
text: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ text | truncate(length) }}"#, ext = "html")]
|
||||
struct WrongArgumentType<'a> {
|
||||
text: &'a str,
|
||||
length: f32,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ text | truncate(length, extra) }}"#, ext = "html")]
|
||||
struct TooManyArguments<'a> {
|
||||
text: &'a str,
|
||||
length: usize,
|
||||
extra: bool,
|
||||
}
|
||||
|
||||
fn main() {}
|
30
testing/tests/ui/truncate.stderr
Normal file
30
testing/tests/ui/truncate.stderr
Normal file
@ -0,0 +1,30 @@
|
||||
error: filter `truncate` needs one argument, the `length`
|
||||
--> NoArgument.html:1:3
|
||||
"text | truncate }}"
|
||||
--> tests/ui/truncate.rs:4:21
|
||||
|
|
||||
4 | #[template(source = r#"{{ text | truncate }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: filter `truncate` needs one argument, the `length`
|
||||
--> TooManyArguments.html:1:3
|
||||
"text | truncate(length, extra) }}"
|
||||
--> tests/ui/truncate.rs:17:21
|
||||
|
|
||||
17 | #[template(source = r#"{{ text | truncate(length, extra) }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0277]: the trait bound `usize: TryFrom<f32>` is not satisfied
|
||||
--> tests/ui/truncate.rs:9:10
|
||||
|
|
||||
9 | #[derive(Template)]
|
||||
| ^^^^^^^^ the trait `From<f32>` is not implemented for `usize`
|
||||
|
|
||||
= help: the following other types implement trait `From<T>`:
|
||||
`usize` implements `From<bool>`
|
||||
`usize` implements `From<std::ptr::Alignment>`
|
||||
`usize` implements `From<u16>`
|
||||
`usize` implements `From<u8>`
|
||||
= note: required for `f32` to implement `Into<usize>`
|
||||
= note: required for `usize` to implement `TryFrom<f32>`
|
||||
= note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
|
Loading…
x
Reference in New Issue
Block a user