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}