liquid_derive/
lib.rs

1//! Derive macros to aid in filter creation.
2
3mod filter;
4mod filter_parameters;
5pub(crate) mod helpers;
6mod object_view;
7mod parse_filter;
8mod value_view;
9
10use proc_macro::TokenStream;
11
12/// Implements `FilterParameters`, as well as two of the traits it requires
13/// (`Display` and `FilterParametersReflection`) and its evaluated counterpart.
14///
15/// Before declaring the struct, the `#[evaluated = "..."]` attribute may be
16/// used to rename the evaluated counterpart (defaults to this struct's name
17/// prepended by "Evaluated"). This may be useful to avoid name collisions.
18///
19/// Each parameter has the following structure:
20/// ```ignore
21/// #[parameter(...)]
22/// NAME: TYPE
23/// ```
24///
25/// `NAME` will be the name of the parameter (although it may be renamed
26/// if it collides with a rust keyword, see below for more information).
27///
28/// `TYPE` will be either `Expression` or `Option<Expression>`, marking the
29/// parameter, respectively, as either required or optional. Note `Expression`
30/// here is the type `::liquid_core::runtime::Expression`.
31///
32/// Inside the `#[parameter(...)]` attribute there may be some information
33/// about the parameter, such as:
34///     - `description` -> (REQUIRED) the description of this parameter for
35/// `FilterReflection`
36///     - `rename` -> overrides `NAME` as the liquid name of the parameter
37/// (to avoid collisions with rust keywords)
38///     - `mode` -> either "keyword" or "positional" (defaults to "positional")
39///     - `arg_type` -> a shortcut to unwrap the content of a value while evaluating
40/// the argument (defaults to "any"). See below for more information.
41///
42/// # Argument Type
43///
44/// If you want to only accept a certain type of `Value` for a given argument,
45/// you may mark its type with `arg_type = "..."`. This will take away the burden
46/// and boilerplate of handling errors and unwrapping the liquid `Value` into a rust
47/// type.
48///
49/// Right now, there is a default `arg_type`, "any", that accepts any value, as well
50/// as other 6 types, one for each type of `Scalar`:
51///     - "any" -> any `Value` is accepted, this is the default option and `evaluate` will
52/// only convert `Expression` to `Value`.
53///     - "integer" -> only `Scalar(Integer)` is accepted, `evaluate` will unwrap `Value`
54/// into `i64`.
55///     - "float" -> only `Scalar(Float)` is accepted, `evaluate` will unwrap `Value`
56/// into `f64`.
57///     - "bool" -> only `Scalar(Bool)` is accepted, `evaluate` will unwrap `Value`
58/// into `bool`.
59///     - "date" -> only `Scalar(Date)` is accepted, `evaluate` will unwrap `Value`
60/// into `Date`.
61///     - "str" -> only `Scalar(Str)` is accepted, `evaluate` will unwrap `Value`
62/// into `KStringCow`.
63///
64/// # Examples
65///
66/// From slice filter:
67/// ```ignore
68/// #[derive(Debug, FilterParameters)]
69/// struct SliceArgs {
70///     #[parameter(
71///         description = "The offset of the slice.",
72///         arg_type = "integer" // Only integer scalars are accepted
73///     )]
74///     offset: Expression, // Required parameter named `offset`
75///
76///     #[parameter(
77///         description = "The length of the slice.",
78///         arg_type = "integer" // Only integer scalars are accepted
79///     )]
80///     length: Option<Expression>, // Optional parameter named `length`
81/// }
82/// ```
83///
84/// From test filter:
85/// ```ignore
86/// #[derive(Debug, FilterParameters)]
87/// // The evaluated counterpart of this `FilterParameters` would have been
88/// // `EvaluatedTestMixedFilterParameters`, but because of this attribute
89/// // it will be named `TestMixedFilterParametersEvaluated` instead.
90/// #[evaluated(TestMixedFilterParametersEvaluated)]
91/// struct TestMixedFilterParameters {
92///     #[parameter(
93///         description = "1",
94///         arg_type = "integer", // Only integer scalars are accepted
95///         mode = "keyword" // This is a keyword parameter
96///     )]
97///     a: Option<Expression>, // Optional parameter named `a`
98///
99///     #[parameter(
100///         description = "2",
101///         arg_type = "bool" // Only boolean scalars are accepted
102///     )]
103///     b: Expression, // Required parameter named `b`
104///
105///     #[parameter(
106///         description = "3",
107///         arg_type = "float", // Only floating point scalars are accepted
108///         mode = "keyword" // This is a keyword parameter
109///     )]
110///     c: Option<Expression>, // Optional parameter named `c`
111///
112///     #[parameter(
113///         description = "4",
114///         arg_type = "date" // Only date scalars are accepted
115///     )]
116///     d: Expression, // Required parameter named `d`
117///
118///     #[parameter(
119///         description = "5",
120///         arg_type = "str" // Only string scalars are accepted
121///     )]
122///     e: Option<Expression>, // Optional parameter named `e`
123///
124///     #[parameter(
125///         rename = "type", // Will override field name in liquid
126///         description = "6",
127///         arg_type = "any", // Any `Value` is accepted. This is the same as not defining `arg_type`
128///         mode = "keyword" // This is a keyword parameter
129///     )]
130///     f: Expression, // Required parameter named `type` (see `rename` in attribute)
131/// }
132/// ```
133#[proc_macro_derive(FilterParameters, attributes(parameter, evaluated))]
134pub fn derive_filter_parameters(item: TokenStream) -> TokenStream {
135    let input = syn::parse_macro_input!(item as syn::DeriveInput);
136    filter_parameters::derive(&input).into()
137}
138
139/// Implements `ParseFilter`.
140///
141/// This is only useful for stateless filters (the most common case). For
142/// filters with configurable state, you will need to implement this trait
143/// manually.
144///
145/// Requires the `#[filter(...)]` attribute to define the filter, with the
146/// following information:
147///     - `parameters` -> (OPTIONAL) only required if the filter has parameters,
148/// the `FilterParameters` struct
149///     - `parsed` -> the `Filter` struct
150///
151/// # Example
152///
153/// ```ignore
154/// #[derive(Clone, ParseFilter, FilterReflection)]
155/// #[filter(
156///     name = "slice",  // Required by `FilterReflection`, not `ParseFilter`
157///     description = "Takes a slice of a given string or array.", // Required by `FilterReflection`, not `ParseFilter`
158///     parameters(SliceArgs), // The filter has parameters
159///     parsed(SliceFilter)
160/// )]
161/// pub struct Slice;
162/// ```
163#[proc_macro_derive(ParseFilter, attributes(filter))]
164pub fn derive_parse_filter(item: TokenStream) -> TokenStream {
165    let input = syn::parse_macro_input!(item as syn::DeriveInput);
166    parse_filter::parse::derive(&input).into()
167}
168
169/// Implements `FilterReflection` for a structure that intends to implement
170/// the `ParseFilter` trait.
171///
172/// Requires the `#[filter(...)]` attribute to define the filter, with the
173/// following information:
174///     - `name` -> the name of the filter
175///     - `description` -> the description of the filter
176///     - `parameters` -> (OPTIONAL) only required if the filter has parameters,
177/// the `FilterParameters` struct
178///
179/// # Example
180///
181/// ```ignore
182/// #[derive(Clone, ParseFilter, FilterReflection)]
183/// #[filter(
184///     name = "slice",
185///     description = "Takes a slice of a given string or array.",
186///     parameters(SliceArgs), // The filter has parameters
187///     parsed(SliceFilter) // Required by `ParseFilter`, not `FilterReflection`
188/// )]
189/// pub struct Slice;
190/// ```
191#[proc_macro_derive(FilterReflection, attributes(filter))]
192pub fn derive_filter_reflection(item: TokenStream) -> TokenStream {
193    let input = syn::parse_macro_input!(item as syn::DeriveInput);
194    parse_filter::filter_reflection::derive(&input).into()
195}
196
197/// Implements `From<T>` (T implements `FilterParameters`) for a structure that
198/// intends to implement the `Filter` trait.
199///
200/// The field that holds the parameters must be marked with `#[parameters]`.
201///
202/// # Example
203///
204/// ```ignore
205/// #[derive(Debug, FromFilterParameters, Display_filter)]
206/// #[name = "at_least"]
207/// struct AtLeastFilter {
208///     #[parameters] // Mark the FilterParameters struct
209///     args: AtLeastArgs, // A struct that implements `FilterParameters`
210/// }
211///
212/// impl Filter for AtLeastFilter {
213///     // ...
214/// }
215/// ```
216#[proc_macro_derive(FromFilterParameters, attributes(parameters))]
217pub fn derive_from_filter_parameters(item: TokenStream) -> TokenStream {
218    let input = syn::parse_macro_input!(item as syn::DeriveInput);
219    filter::from_filter_parameters::derive(&input).into()
220}
221
222/// Implements `Display` for a structure that intends to implement the `Filter`
223/// trait.
224///
225/// Requires the helper attribute `#[name = "..."]` to tell the name of the
226/// filter before its declaration.
227///
228/// If the filter has parameters, the field that holds them must be marked
229/// with `#[parameters]`.
230///
231/// # Example
232///
233/// ```ignore
234/// #[derive(Debug, FromFilterParameters, Display_filter)]
235/// #[name = "at_least"] // The name of the filter
236/// struct AtLeastFilter {
237///     #[parameters] // Mark the FilterParameters struct
238///     args: AtLeastArgs, // A struct that implements `FilterParameters`
239/// }
240///
241/// impl Filter for AtLeastFilter {
242///     // ...
243/// }
244/// ```
245#[proc_macro_derive(Display_filter, attributes(name, parameters))]
246pub fn derive_display_filter(item: TokenStream) -> TokenStream {
247    let input = syn::parse_macro_input!(item as syn::DeriveInput);
248    filter::display::derive(&input).into()
249}
250
251#[proc_macro_derive(CoreValueView)]
252pub fn derive_core_value_view(item: TokenStream) -> TokenStream {
253    let input = syn::parse_macro_input!(item as syn::DeriveInput);
254    value_view::core_derive(&input).into()
255}
256
257#[proc_macro_derive(CoreObjectView)]
258pub fn derive_core_object_view(item: TokenStream) -> TokenStream {
259    let input = syn::parse_macro_input!(item as syn::DeriveInput);
260    object_view::core_derive(&input).into()
261}
262
263#[proc_macro_derive(ValueView)]
264pub fn derive_value_view(item: TokenStream) -> TokenStream {
265    let input = syn::parse_macro_input!(item as syn::DeriveInput);
266    value_view::derive(&input).into()
267}
268
269#[proc_macro_derive(ObjectView)]
270pub fn derive_object_view(item: TokenStream) -> TokenStream {
271    let input = syn::parse_macro_input!(item as syn::DeriveInput);
272    object_view::derive(&input).into()
273}