Skip to content

Commit

Permalink
derive: unify filter argument handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Kijewski committed Jan 20, 2025
1 parent d5c65ab commit 98e1791
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 49 deletions.
112 changes: 64 additions & 48 deletions rinja_derive/src/generator/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,12 @@ impl<'a> Generator<'a, '_> {
));
}

let arg = get_filter_argument(ctx, name, args, node)?;
// Both filters return HTML-safe strings.
buf.write(format_args!(
"rinja::filters::HtmlSafeOutput(rinja::filters::{name}(",
));
self._visit_args(ctx, buf, args)?;
self._visit_arg(ctx, buf, arg)?;
buf.write(")?)");
Ok(DisplayWrap::Unwrapped)
}
Expand All @@ -321,14 +322,15 @@ impl<'a> Generator<'a, '_> {
buf: &mut Buffer,
name: &str,
args: &[WithSpan<'_, Expr<'a>>],
_node: Span<'_>,
node: Span<'_>,
) -> Result<DisplayWrap, CompileError> {
let arg = get_filter_argument(ctx, name, args, node)?;
// All filters return numbers, and any default formatted number is HTML safe.
buf.write(format_args!(
"rinja::filters::HtmlSafeOutput(rinja::filters::{name}(\
rinja::helpers::get_primitive_value(&("
));
self._visit_args(ctx, buf, args)?;
self._visit_arg(ctx, buf, arg)?;
buf.write(")) as rinja::helpers::core::primitive::f32)?)");
Ok(DisplayWrap::Unwrapped)
}
Expand All @@ -337,7 +339,7 @@ impl<'a> Generator<'a, '_> {
&mut self,
ctx: &Context<'_>,
buf: &mut Buffer,
_name: &str,
name: &str,
args: &[WithSpan<'_, Expr<'a>>],
node: Span<'_>,
) -> Result<DisplayWrap, CompileError> {
Expand All @@ -357,9 +359,7 @@ impl<'a> Generator<'a, '_> {
[count, sg] => (count, sg, PLURAL),
[count, sg, pl] => (count, sg, pl),
_ => {
return Err(
ctx.generate_error("unexpected argument(s) in `pluralize` filter", node)
);
return Err(unexpected_filter_arguments(ctx, name, args, node));
}
};
if let Some(is_singular) = expr_is_int_lit_plus_minus_one(count) {
Expand All @@ -386,16 +386,11 @@ impl<'a> Generator<'a, '_> {
node: Span<'_>,
) -> Result<DisplayWrap, CompileError> {
ensure_filter_has_feature_alloc(ctx, name, node)?;
if args.len() != 1 {
return Err(ctx.generate_error(
format_args!("unexpected argument(s) in `{name}` filter"),
node,
));
}
let arg = get_filter_argument(ctx, name, args, node)?;
buf.write(format_args!(
"rinja::filters::{name}(&(&&rinja::filters::AutoEscaper::new(&(",
));
self._visit_args(ctx, buf, args)?;
self._visit_arg(ctx, buf, arg)?;
// The input is always HTML escaped, regardless of the selected escaper:
buf.write("), rinja::filters::Html)).rinja_auto_escape()?)?");
// The output is marked as HTML safe, not safe in all contexts:
Expand All @@ -406,14 +401,11 @@ impl<'a> Generator<'a, '_> {
&mut self,
ctx: &Context<'_>,
buf: &mut Buffer,
_name: &str,
name: &str,
args: &[WithSpan<'_, Expr<'a>>],
node: Span<'_>,
) -> Result<DisplayWrap, CompileError> {
let arg = match args {
[arg] => arg,
_ => return Err(ctx.generate_error("unexpected argument(s) in `as_ref` filter", node)),
};
let arg = get_filter_argument(ctx, name, args, node)?;
buf.write('&');
self.visit_expr(ctx, buf, arg)?;
Ok(DisplayWrap::Unwrapped)
Expand All @@ -423,14 +415,11 @@ impl<'a> Generator<'a, '_> {
&mut self,
ctx: &Context<'_>,
buf: &mut Buffer,
_name: &str,
name: &str,
args: &[WithSpan<'_, Expr<'a>>],
node: Span<'_>,
) -> Result<DisplayWrap, CompileError> {
let arg = match args {
[arg] => arg,
_ => return Err(ctx.generate_error("unexpected argument(s) in `deref` filter", node)),
};
let arg = get_filter_argument(ctx, name, args, node)?;
buf.write('*');
self.visit_expr(ctx, buf, arg)?;
Ok(DisplayWrap::Unwrapped)
Expand All @@ -440,7 +429,7 @@ impl<'a> Generator<'a, '_> {
&mut self,
ctx: &Context<'_>,
buf: &mut Buffer,
_name: &str,
name: &str,
args: &[WithSpan<'_, Expr<'a>>],
node: Span<'_>,
) -> Result<DisplayWrap, CompileError> {
Expand All @@ -454,9 +443,8 @@ impl<'a> Generator<'a, '_> {
let filter = match args.len() {
1 => "json",
2 => "json_pretty",
_ => return Err(ctx.generate_error("unexpected argument(s) in `json` filter", node)),
_ => return Err(unexpected_filter_arguments(ctx, name, args, node)),
};

buf.write(format_args!("rinja::filters::{filter}("));
self._visit_args(ctx, buf, args)?;
buf.write(")?");
Expand All @@ -467,15 +455,13 @@ impl<'a> Generator<'a, '_> {
&mut self,
ctx: &Context<'_>,
buf: &mut Buffer,
_name: &str,
name: &str,
args: &[WithSpan<'_, Expr<'a>>],
node: Span<'_>,
) -> Result<DisplayWrap, CompileError> {
if args.len() != 1 {
return Err(ctx.generate_error("unexpected argument(s) in `safe` filter", node));
}
let arg = get_filter_argument(ctx, name, args, node)?;
buf.write("rinja::filters::safe(");
self._visit_args(ctx, buf, args)?;
self._visit_arg(ctx, buf, arg)?;
buf.write(format_args!(", {})?", self.input.escaper));
Ok(DisplayWrap::Wrapped)
}
Expand All @@ -484,15 +470,16 @@ impl<'a> Generator<'a, '_> {
&mut self,
ctx: &Context<'_>,
buf: &mut Buffer,
_name: &str,
name: &str,
args: &[WithSpan<'_, Expr<'a>>],
node: Span<'_>,
) -> Result<DisplayWrap, CompileError> {
if args.len() > 2 {
return Err(ctx.generate_error("only two arguments allowed to escape filter", node));
}
let opt_escaper = match args.get(1).map(|expr| &**expr) {
Some(Expr::StrLit(StrLit { prefix, content })) => {
let (arg, opt_escaper) = match args {
[arg] => (arg, None),
[arg, escaper] => {
let Expr::StrLit(StrLit { prefix, content }) = &**escaper else {
return Err(ctx.generate_error("invalid escaper type for escape filter", node));
};
if let Some(prefix) = prefix {
let kind = if *prefix == StrPrefix::Binary {
"slice"
Expand All @@ -506,12 +493,9 @@ impl<'a> Generator<'a, '_> {
args[1].span(),
));
}
Some(content)
}
Some(_) => {
return Err(ctx.generate_error("invalid escaper type for escape filter", node));
(arg, Some(content))
}
None => None,
_ => return Err(unexpected_filter_arguments(ctx, name, args, node)),
};
let escaper = match opt_escaper {
Some(name) => self
Expand All @@ -536,7 +520,7 @@ impl<'a> Generator<'a, '_> {
None => self.input.escaper,
};
buf.write("rinja::filters::escape(");
self._visit_args(ctx, buf, &args[..1])?;
self._visit_arg(ctx, buf, arg)?;
buf.write(format_args!(", {escaper})?"));
Ok(DisplayWrap::Wrapped)
}
Expand Down Expand Up @@ -1065,18 +1049,50 @@ impl<'a> Generator<'a, '_> {
}
}

#[inline]
fn get_filter_argument<'a, 'b>(
ctx: &Context<'_>,
name: &str,
args: &'b [WithSpan<'b, Expr<'a>>],
node: Span<'_>,
) -> Result<&'b WithSpan<'b, Expr<'a>>, CompileError> {
match args {
[arg] => Ok(arg),
_ => Err(unexpected_filter_arguments(ctx, name, args, node)),
}
}

#[cold]
#[inline(never)]
fn unexpected_filter_arguments(
ctx: &Context<'_>,
name: &str,
args: &[WithSpan<'_, Expr<'_>>],
node: Span<'_>,
) -> CompileError {
ctx.generate_error(
format_args!(
"unexpected argument{} in `{name}` filter",
if args.len() == 2 { "" } else { "s" },
),
node,
)
}

#[inline]
fn ensure_filter_has_feature_alloc(
ctx: &Context<'_>,
name: &str,
node: Span<'_>,
) -> Result<(), CompileError> {
if !cfg!(feature = "alloc") {
return Err(ctx.generate_error(
if cfg!(feature = "alloc") {
Ok(())
} else {
Err(ctx.generate_error(
format_args!("the `{name}` filter requires the `alloc` feature to be enabled"),
node,
));
))
}
Ok(())
}

fn expr_is_int_lit_plus_minus_one(expr: &WithSpan<'_, Expr<'_>>) -> Option<bool> {
Expand Down
2 changes: 1 addition & 1 deletion testing/tests/ui/json-too-many-args.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: unexpected argument(s) in `json` filter
error: unexpected arguments in `json` filter
--> OneTwoThree.txt:1:3
"1|json(2, 3) }}"
--> tests/ui/json-too-many-args.rs:6:34
Expand Down

0 comments on commit 98e1791

Please sign in to comment.