Replies: 5 comments 6 replies
-
Option 1: Tuples - Variadic generics for the poorOne way to get support for a variable number of arguments - but not for named arguments - is to have the code generator pass all parameters as one tuple. That would allow everything we currently have:// filters with no arguments
fn my_awesome_filter1<T: Display>(value: T, _: &dyn askama::Values, args: &()) -> askama::Result<String> {
// ...
}
// filters with n arguments
fn my_awesome_filter2<T: Display>(value: T, _: &dyn askama::Values, args: &(String, i64)) -> askama::Result<String> {
// ...
}
// filters with n arguments using generics
fn my_awesome_filter3<T: Display, Arg0: Display>(
value: T,
_: &dyn askama::Values,
args: &(Arg0, i64)
) -> askama::Result<String> {
// ...
}But - and that's the interesting part - it would also allow:pub struct IndentFilterArgs {
width: usize,
first: bool,
blank: bool
}
// indent(<width>)
impl From<(usize,)> for IndentFilterArgs {
fn from(value: (usize,)) -> Self {
Self {
width: value.0,
first: false,
blank: false
}
}
}
// indent(<width>, <first>)
impl From<(usize, bool)> for IndentFilterArgs {
fn from(value: (usize, bool)) -> Self {
Self {
width: value.0,
first: value.1,
blank: false
}
}
}
// indent(<width>, <first>, <blank>)
impl From<(usize, bool, bool)> for IndentFilterArgs {
fn from(value: (usize, bool, bool)) -> Self {
Self {
width: value.0,
first: value.1,
blank: value.2
}
}
}
// filter that accepts all tuples as argument, which implement Into<IndentFilterArgs>
fn indent<T: Display>(value: T, args: &impl Into<IndentFilterArgs>) -> askama::Result<String> {
// ...
} |
Beta Was this translation helpful? Give feedback.
-
|
One (extremely crazy) idea I had was to add a plug-in infrastructure to the generator by means of |
Beta Was this translation helpful? Give feedback.
-
Option 2: proc-macro for filter functions & Builder PatternThere is this brillant concept for builder patterns that check at compile-time whether all required parameters have been supplied, while still accepting the required parameters in random order: https://dev.to/mindflavor/rust-builder-pattern-with-types-3chf I think we can use this! My idea would be to introduce a proc-macro that askama filter functions have to be annotated with. #[askama::filter_fn]
fn indent<T: Display>(
value: T,
_: &dyn askama::Values,
width: usize, /* required */
height: usize, /* required */
#[optional(default = false)] first: bool, /* optional */
) -> askama::Result<String> {
// ...
}The // the filter function itself
fn indent<T: Display>(value: T, _: &dyn askama::Values, width: usize, height: usize, first: bool) -> askama::Result<String> {
// ...
}
// a builder struct that helps askama's code generator to
// "accumulate" all arguments for the filter invocation
struct IndentFilter<const P0: bool, const P1: bool> {
/* required args */
width: Option<usize>,
height: Option<usize>,
/* optional args with default values */
first: bool
}
// entry-point to the builder
impl Default for IndentFilter<false, false> {
fn default() -> Self {
Self {
width: None,
height: None,
first: false /* from #[optional(default = XXX)] annotation above */
}
}
}
// setter methods that askama's code generator can use to arbitrarily mix
// positioned and named arguments to fill the struct with filter arguments
// P0 and P1 track for each (positional) required argument, whether it was
// supplied into the builder - or whether it's still missing
impl<const P0: bool, const P1: bool> IndentFilter<P0, P1> {
/* REQUIRED ARGUMENT: width */
//named
pub fn with_width(mut self, value: usize) -> IndentFilter<true, P1> {
IndentFilter {
width: Some(value),
height: self.height,
first: self.first
}
}
// positional
pub fn with_arg0(mut self, value: usize) -> IndentFilter<true, P1> {
self.with_width(value)
}
// #############################################
/* REQUIRED ARGUMENT: height */
//named
pub fn with_height(mut self, value: usize) -> IndentFilter<P0, true> {
IndentFilter {
width: self.width,
height: Some(value),
first: self.first
}
}
// positional
pub fn with_arg1(mut self, value: usize) -> IndentFilter<P0, true> {
self.with_height(value)
}
// #############################################
/* OPTIONAL ARGUMENT: first */
// named
pub fn with_first(mut self, value: bool) -> Self {
self.first = value;
self
}
// positional
pub fn with_arg2(mut self, value: bool) -> Self {
self.with_first(value)
}
}
// if all required arguments have been supplied (P0 == true, P1 == true)
// ... the execute() method is "unlocked":
impl IndentFilter<true, true> {
pub fn execute<T: Display>(self, value: T, env: &dyn askama::Values) -> askama::Result<String> {
unsafe {
indent(value, env, self.width.unwrap_unchecked(), self.height.unwrap_unchecked(), self.first)
}
}
}What askama's code generator then generates: // illegal - missing required parameter height
// {{ "input" | indent(0) }}
IndentFilter::default()
.with_arg0(0)
.execute("input", env); // fails to build
// illegal - missing required parameter height
// {{ "input" | indent(width = 0) }}
IndentFilter::default()
.with_width(0)
.execute("input", env); // fails to build
// illegal - missing required parameter width
// {{ "input" | indent(height = 0) }}
IndentFilter::default()
.with_height(0)
.execute("input", env); // fails to build
// illegal - missing required parameter height
// {{ "input" | indent(0, width = 5 }}
IndentFilter::default()
.with_arg0(0)
.with_width(5)
.execute("input", env); // fails to build
// {{ "input" | indent(0, height = 5 }}
IndentFilter::default()
.with_arg0(0)
.with_height(5)
.execute("input", env); // works
// {{ "input" | indent(0, height = 5, true }}
IndentFilter::default()
.with_arg0(0)
.with_height(5)
.with_arg2(true)
.execute("input", env); // works |
Beta Was this translation helpful? Give feedback.
-
|
A third solution would be to take a map as parameter of the filter and to add some new syntax in {{ x|my_filter(x = 12)to generate a map containing only |
Beta Was this translation helpful? Give feedback.
-
|
Resolved with #545 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
As a user of askama, I'm a bit jealous of askama itself supporting variadic and even named arguments for its built-in filters, like
indent.Of course I understand where the limitation comes from, so I've been brainstorming a bit of how we could improve the situation for askama's users - such as myself.
I would like to make this thread a collection of ideas we could use to improve upon the current level of support.
Or in other words:

Beta Was this translation helpful? Give feedback.
All reactions