mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-27 13:00:57 +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 super::{DisplayWrap, Generator, TargetIsize, TargetUsize};
|
||||||
use crate::heritage::Context;
|
use crate::heritage::Context;
|
||||||
use crate::integration::Buffer;
|
use crate::integration::Buffer;
|
||||||
use crate::{BUILTIN_FILTERS, BUILTIN_FILTERS_NEED_ALLOC, CompileError, MsgValidEscapers};
|
use crate::{BUILTIN_FILTERS, CompileError, MsgValidEscapers};
|
||||||
|
|
||||||
impl<'a> Generator<'a, '_> {
|
impl<'a> Generator<'a, '_> {
|
||||||
pub(super) fn visit_filter(
|
pub(super) fn visit_filter(
|
||||||
@ -18,6 +18,7 @@ impl<'a> Generator<'a, '_> {
|
|||||||
node: Span<'_>,
|
node: Span<'_>,
|
||||||
) -> Result<DisplayWrap, CompileError> {
|
) -> Result<DisplayWrap, CompileError> {
|
||||||
let filter = match name {
|
let filter = match name {
|
||||||
|
"center" => Self::visit_center_filter,
|
||||||
"deref" => Self::visit_deref_filter,
|
"deref" => Self::visit_deref_filter,
|
||||||
"escape" | "e" => Self::visit_escape_filter,
|
"escape" | "e" => Self::visit_escape_filter,
|
||||||
"filesizeformat" => Self::visit_humansize,
|
"filesizeformat" => Self::visit_humansize,
|
||||||
@ -32,6 +33,7 @@ impl<'a> Generator<'a, '_> {
|
|||||||
"pluralize" => Self::visit_pluralize_filter,
|
"pluralize" => Self::visit_pluralize_filter,
|
||||||
"ref" => Self::visit_ref_filter,
|
"ref" => Self::visit_ref_filter,
|
||||||
"safe" => Self::visit_safe_filter,
|
"safe" => Self::visit_safe_filter,
|
||||||
|
"truncate" => Self::visit_truncate_filter,
|
||||||
"urlencode" => Self::visit_urlencode_filter,
|
"urlencode" => Self::visit_urlencode_filter,
|
||||||
"urlencode_strict" => Self::visit_urlencode_strict_filter,
|
"urlencode_strict" => Self::visit_urlencode_strict_filter,
|
||||||
"value" => return self.visit_value(ctx, buf, args, generics, node, "`value` 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>>],
|
generics: &[WithSpan<'a, TyGenerics<'a>>],
|
||||||
node: Span<'_>,
|
node: Span<'_>,
|
||||||
) -> Result<DisplayWrap, CompileError> {
|
) -> Result<DisplayWrap, CompileError> {
|
||||||
if BUILTIN_FILTERS_NEED_ALLOC.contains(&name) {
|
|
||||||
ensure_filter_has_feature_alloc(ctx, name, node)?;
|
|
||||||
}
|
|
||||||
if !generics.is_empty() {
|
if !generics.is_empty() {
|
||||||
return Err(
|
return Err(
|
||||||
ctx.generate_error(format_args!("unexpected generics on filter `{name}`"), node)
|
ctx.generate_error(format_args!("unexpected generics on filter `{name}`"), node)
|
||||||
@ -516,6 +515,60 @@ impl<'a> Generator<'a, '_> {
|
|||||||
buf.write(")?");
|
buf.write(")?");
|
||||||
Ok(DisplayWrap::Unwrapped)
|
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(
|
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.
|
// Askama or should refer to a local `filters` module.
|
||||||
const BUILTIN_FILTERS: &[&str] = &[
|
const BUILTIN_FILTERS: &[&str] = &[
|
||||||
"capitalize",
|
"capitalize",
|
||||||
"center",
|
|
||||||
"lower",
|
"lower",
|
||||||
"lowercase",
|
"lowercase",
|
||||||
"title",
|
"title",
|
||||||
"trim",
|
"trim",
|
||||||
"truncate",
|
|
||||||
"upper",
|
"upper",
|
||||||
"uppercase",
|
"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;
|
extern crate serde_json;
|
||||||
|
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
use assert_matches::assert_matches;
|
||||||
#[cfg(feature = "serde_json")]
|
#[cfg(feature = "serde_json")]
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
@ -298,6 +299,32 @@ fn test_filter_truncate() {
|
|||||||
foo: "alpha bar".into(),
|
foo: "alpha bar".into(),
|
||||||
};
|
};
|
||||||
assert_eq!(t.render().unwrap(), "alpha baralpha...");
|
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")]
|
#[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