liquid_core/parser/
filter.rs

1use std::fmt::{Debug, Display};
2
3use crate::error::Result;
4use crate::model::{Value, ValueView};
5use crate::runtime::{Expression, Runtime};
6
7/// A structure that holds the information of a single parameter in a filter.
8/// This includes its name, description and whether it is optional or required.
9///
10/// This is the return type in some `FilterReflection` functions.
11pub struct ParameterReflection {
12    pub name: &'static str,
13    pub description: &'static str,
14    pub is_optional: bool,
15}
16
17/// A trait that holds the information of the parameters of a filter.
18///
19/// All structs that implement `FilterParameters` must implement this.
20/// This is actually automatically implemented with `#[derive(FilterParameters)]`.
21///
22/// This trait allows `FilterReflection` macro to extract the parameters information
23/// from a given `FilterParameters` structure.
24pub trait FilterParametersReflection {
25    fn positional_parameters() -> &'static [ParameterReflection];
26    fn keyword_parameters() -> &'static [ParameterReflection];
27}
28
29/// A trait that holds the information of a filter about itself, such as
30/// its name, description and parameters.
31///
32/// All structs that implement `ParseFilter` must implement this.
33///
34/// # Deriving
35///
36/// This trait may be derived with `liquid-derive`'s `#[derive(FilterReflection)]`. However,
37/// it is necessary to use the `#[filter(...)]`  helper attribute. See documentation on
38/// `liquid-derive` for more information.
39pub trait FilterReflection {
40    fn name(&self) -> &str;
41    fn description(&self) -> &str;
42
43    fn positional_parameters(&self) -> &'static [ParameterReflection];
44    fn keyword_parameters(&self) -> &'static [ParameterReflection];
45}
46
47/// A trait that declares and holds the parameters of a filter.
48///
49/// Provides `from_args`, to construct itself from `FilterArguments` (parses the arguments)
50/// and `evaluate`, to construct its evaluated counterpart (evaluates the arguments).
51///
52/// # Deriving
53///
54/// The whole point of this structure is to facilitate the process of deriving a filter.
55/// Thus, this trait and all traits it requires may be derived with `#[derive(Debug, FilterParameters)]`.
56///
57/// See documentation for `FilterParameters` macro on `liquid-derive` for more information.
58pub trait FilterParameters<'a>: Sized + FilterParametersReflection + Debug + Display {
59    type EvaluatedFilterParameters;
60    fn from_args(args: FilterArguments) -> Result<Self>;
61    fn evaluate(&'a self, runtime: &'a dyn Runtime) -> Result<Self::EvaluatedFilterParameters>;
62}
63
64/// Structure that holds the unparsed arguments of a filter, both positional and keyword.
65pub struct FilterArguments<'a> {
66    pub positional: Box<dyn Iterator<Item = Expression>>,
67    pub keyword: Box<dyn Iterator<Item = (&'a str, Expression)> + 'a>,
68}
69
70/// A trait that holds a filter, ready to evaluate.
71///
72/// # Deriving
73///
74/// You cannot derive `Filter`, as it would go against the very point of creating your own filter.
75/// You can, however, derive some other traits that are necessary in order to implement it.
76///
77/// In order to implement this trait, the struct must also implement `Debug` and `Display`, as well
78/// as either `Default` or `From<T>` (where T is the FilterParameters struct), respectively, in a
79/// filter without or with parameters.
80///
81/// For `Debug` and `Default`, one may use rust's `#[derive(Debug)]` and `#[derive(Default)]` macros.
82/// For `Display` and `From<T>`, one may use `liquid-derive`'s `#[derive(Display_filter)]` and
83/// `#[derive(FromFilterParameters)]`.
84///
85/// Note, however, that you may need helper attributes like `#[name = "..."]` and `#[parameters]` for
86/// using liquid-derive`'s macros. See documentation on `liquid-derive` for more information.
87///
88/// # Examples
89///
90/// Filter for filter with no arguments:
91/// ```ignore
92/// #[derive(Debug, Default, Display_filter)]
93/// #[name = "abs"] // The name of the filter, for `Display_filter`.
94/// struct AbsFilter; // There are no parameters, so implements `Default`.
95///
96/// impl Filter for AbsFilter {
97///     fn evaluate(&self, input: &dyn  ValueView, _runtime: &dyn Runtime) -> Result<Value> {
98///         // Implementation of the filter here
99///     }
100/// }
101/// ```
102///
103/// Filter for filter with arguments:
104/// ```ignore
105/// #[derive(Debug, FromFilterParameters, Display_filter)]
106/// #[name = "at_least"] // The name of the filter for derives
107/// struct AtLeastFilter { // There are parameters, so derives `FromFilterParameters`.
108///     #[parameters] // Mark the FilterParameters struct for derives
109///     args: AtLeastArgs, // A struct that implements `FilterParameters`
110/// }
111///
112/// impl Filter for AtLeastFilter {
113///     fn evaluate(&self, input: &ValueView, runtime: &dyn Runtime) -> Result<Value> {
114///         // Evaluate the `FilterParameters`
115///         let args = self.args.evaluate(runtime)?;
116///
117///         // Implementation of the filter here
118///     }
119/// }
120/// ```
121///
122/// Filter for a configurable filter:
123/// ```ignore
124/// #[derive(Debug, Display_filter)]
125/// #[name = "example"] // The name of the filter for `Display`
126/// // Because construction happens manually (without derive) in `FilterParser`
127/// // no need to derive neither `Default` nor `FromFilterParameters`.
128/// struct ExampleFilter {
129///     #[parameters] // Mark the FilterParameters struct for `Display`
130///     args: ExampleArgs, // A struct that implements `FilterParameters`
131///     state: i32, // See `ParseFilter` example for runtime
132/// }
133///
134/// impl Filter for AtLeastFilter {
135///     fn evaluate(&self, input: &dyn ValueView, runtime: &dyn Runtime) -> Result<Value> {
136///         // Evaluate the `FilterParameters`
137///         let args = self.args.evaluate(runtime)?;
138///
139///         // Implementation of the filter here
140///     }
141/// }
142/// ```
143pub trait Filter: Send + Sync + Debug + Display {
144    // This will evaluate the expressions and evaluate the filter.
145    fn evaluate(&self, input: &dyn ValueView, runtime: &dyn Runtime) -> Result<Value>;
146}
147
148/// A trait to register a new filter in the `liquid::Parser`.
149///
150/// To implement this trait, the structure must also implement `FilterReflection`, thus giving
151/// meta information about the filter (namely it's name).
152///
153/// Every time a filter with that name is encountered, it is parsed with the `ParseFilter::parse`
154/// method, yielding a new `Filter`.
155///
156/// # Deriving
157///
158/// In order to implement this trait, the struct must also implement `FilterReflection` and
159/// `Clone`.
160///
161/// `Clone` may be derived with rust's `#[derive(Clone)]`. `FilterReflection` may be derived
162/// with `liquid-derive`'s `#[derive(FilterReflection)]`. ParseFilter may be derived with
163/// `#[derive(FilterReflection)]`.
164///
165/// In order to use `liquid-derive`'s macros, however, it is necessary to use the `#[filter(...)]`
166/// helper attribute. See documentation on `liquid-derive` for more information.
167///
168/// # Examples
169///
170/// ParseFilter for filter with no arguments:
171/// ```ignore
172/// #[derive(Clone, ParseFilter, FilterReflection)]
173/// #[filter(
174///     name = "abs",
175///     description = "Returns the absolute value of a number.",
176///     parsed(AbsFilter) // A struct that implements `Filter` (must implement `Default`)
177/// )]
178/// pub struct Abs;
179/// ```
180///
181/// ParseFilter for filter with arguments:
182/// ```ignore
183/// #[derive(Clone, ParseFilter, FilterReflection)]
184/// #[filter(
185///     name = "slice",
186///     description = "Takes a slice of a given string or array.",
187///     parameters(SliceArgs), // A struct that implements `FilterParameters`
188///     parsed(SliceFilter) // A struct that implements `Filter` (must implement `From<SliceArgs>`)
189/// )]
190/// pub struct Slice;
191/// ```
192///
193/// ParseFilter for a configurable filter:
194/// ```ignore
195/// #[derive(Clone, FilterReflection)]
196/// #[filter(
197///     name = "example",
198///     description = "This filter exists for example purposes.",
199///     parameters(ExampleArgs) // A struct that implements `FilterParameters`
200/// )]
201/// pub struct ExampleParser {
202///     // You can add as many fields as you find necessary to configure the filter
203///     // before registering it.
204///     pub mode: i32,
205/// }
206///
207/// // For configurable filters, there is no default implementation of `ParseFilter`
208/// impl ParseFilter for ExampleParser {
209///     fn parse(&self, arguments: FilterArguments) -> Result<Box<Filter>> {
210///         // Create the `FilterParameters` struct from the given `arguments`
211///         let args = ExampleArgs::from_args(arguments)?;
212///         // Use the configuration state of the `ParseFilter`
213///         let state = self.state;
214///
215///         // Create the `Filter` struct and return it, passing the information
216///         // about the arguments and the configuration of the `ParseFilter`.
217///         Ok(Box::new(ExampleFilter { args, state }))
218///     }
219/// }
220/// ```
221pub trait ParseFilter: Send + Sync + ParseFilterClone {
222    /// Filter `input` based on `arguments`.
223    fn parse(&self, arguments: FilterArguments) -> Result<Box<dyn Filter>>;
224
225    fn reflection(&self) -> &dyn FilterReflection;
226}
227
228/// Support cloning of `Box<ParseFilter>`.
229pub trait ParseFilterClone {
230    /// Cloning of `dyn ParseFilter`.
231    fn clone_box(&self) -> Box<dyn ParseFilter>;
232}
233
234impl<T> ParseFilterClone for T
235where
236    T: 'static + ParseFilter + Clone,
237{
238    fn clone_box(&self) -> Box<dyn ParseFilter> {
239        Box::new(self.clone())
240    }
241}
242
243impl Clone for Box<dyn ParseFilter> {
244    fn clone(&self) -> Box<dyn ParseFilter> {
245        self.clone_box()
246    }
247}
248
249impl<T> From<T> for Box<dyn ParseFilter>
250where
251    T: 'static + ParseFilter,
252{
253    fn from(filter: T) -> Self {
254        Box::new(filter)
255    }
256}