Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend documentation of custom filters #744

Merged
merged 3 commits into from
Nov 16, 2022
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions book/src/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,19 +408,29 @@ This will output formatted YAML for any value that implements the required
## Custom Filters
[#custom-filters]: #custom-filters

To define your own filters, simply have a module named filters in scope of the context deriving a `Template` impl.
To define your own filters, simply have a module named `filters` in scope of the context deriving a `Template` impl and define the filters as functions within this module. The functions must have at least one argument and the return type must be `::askama::Result<T>` where `T` is some type that implements `Display`.
djc marked this conversation as resolved.
Show resolved Hide resolved

Note that in case of name collision, the built in filters take precedence.
The arguments to the filters are passed as follows. The first argument corresponds to the expression they are applied to. Subsequent arguments, if any, must be given directly when calling the filter. The first argument <em>may</em> be passed as reference whenever Askama can not determine that it can be passed directly. If you want a filter to accept a first argument with or without reference, you may use traits to bound the argument. For example, the `trim` built-in filter accepts any value implementing `Display`. Its signature is similar to `fn trim<T: fmt::Display>(s: T) -> ::askama::Result<String>`.
djc marked this conversation as resolved.
Show resolved Hide resolved

Note that built-in filters have preference over custom filters, so, in case of name collision, the built-in filter is applied.

### Examples

Implementing a filter that replaces all instances of `"oo"` for `"aa"`.
```rust
use askama::Template;

#[derive(Template)]
#[template(source = "{{ s|myfilter }}", ext = "txt")]
struct MyFilterTemplate<'a> {
s: &'a str,
}

// Any filter defined in the module `filters` is accessible in your template.
mod filters {
pub fn myfilter(s: &str) -> ::askama::Result<String> {
// This filter does not have extra arguments
pub fn myfilter<T: std::fmt::Display>(s: T) -> ::askama::Result<String> {
let s = s.to_string();
Ok(s.replace("oo", "aa"))
}
}
Expand All @@ -430,3 +440,30 @@ fn main() {
assert_eq!(t.render().unwrap(), "faa");
}
```

Implementing a filter that replaces all instances of `"oo"` for `n` times `"a"`.
```rust
use askama::Template;

#[derive(Template)]
#[template(source = "{{ s|myfilter(4) }}", ext = "txt")]
struct MyFilterTemplate<'a> {
s: &'a str,
}

// Any filter defined in the module `filters` is accessible in your template.
mod filters {
// This filter requires a `usize` input when called in templates
pub fn myfilter<T: std::fmt::Display>(s: T, n: usize) -> ::askama::Result<String> {
let s = s.to_string();
let mut replace = String::with_capacity(n);
replace.extend((0..n).map(|_| "a"));
Ok(s.replace("oo", &replace))
}
}

fn main() {
let t = MyFilterTemplate { s: "foo" };
assert_eq!(t.render().unwrap(), "faaaa");
}
```