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}