loose_liquid_derive/
lib.rs

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