1use crate::helpers::{assign_str_value, parse_str_value, AssignOnce};
2use proc_macro2::{Ident, Span, TokenStream};
3use quote::{quote, ToTokens};
4use std::str::FromStr;
5use syn::punctuated::Punctuated;
6use syn::spanned::Spanned;
7use syn::{
8 Attribute, Data, DeriveInput, Error, Field, Fields, GenericArgument, Path, PathArguments,
9 PathSegment, Result, Token, Type, Visibility,
10};
11
12struct FilterParameters<'a> {
14 name: &'a Ident,
15 evaluated_name: Ident,
16 fields: FilterParametersFields<'a>,
17 vis: &'a Visibility,
18}
19
20impl<'a> FilterParameters<'a> {
21 fn parse_attrs(attrs: &[Attribute]) -> Result<Option<Ident>> {
23 let mut evaluated_attrs = attrs
24 .iter()
25 .filter(|attr| attr.path().is_ident("evaluated"));
26
27 match (evaluated_attrs.next(), evaluated_attrs.next()) {
28 (Some(attr), None) => Ok(Self::parse_evaluated_attr(attr)?.get_ident().cloned()),
29
30 (_, Some(attr)) => Err(Error::new_spanned(
31 attr,
32 "Found multiple definitions for `evaluated` attribute.",
33 )),
34
35 _ => Ok(None),
36 }
37 }
38
39 fn parse_evaluated_attr(attr: &Attribute) -> Result<Path> {
41 let mut ident = None;
42 attr.parse_nested_meta(|meta| {
43 ident = Some(meta.path);
44 Ok(())
45 })?;
46 ident.ok_or_else(|| Error::new(attr.span(), "expected ident"))
47 }
48
49 fn from_input(input: &'a DeriveInput) -> Result<Self> {
51 let DeriveInput {
52 attrs,
53 vis,
54 generics,
55 data,
56 ident,
57 } = input;
58
59 if !generics.params.is_empty() {
60 return Err(Error::new_spanned(
61 generics,
62 "Generics are cannot be used in FilterParameters.",
63 ));
64 }
65
66 let fields = match data {
67 Data::Struct(data) => FilterParametersFields::from_fields(&data.fields)?,
68 Data::Enum(data) => {
69 return Err(Error::new_spanned(
70 data.enum_token,
71 "Enums cannot be FilterParameters.",
72 ));
73 }
74 Data::Union(data) => {
75 return Err(Error::new_spanned(
76 data.union_token,
77 "Unions cannot be FilterParameters.",
78 ));
79 }
80 };
81
82 if let Some(parameter) = fields.required_after_optional() {
83 return Err(Error::new_spanned(
84 parameter,
85 "Found required positional parameter after an optional positional parameter. The user can't input this parameters without inputting the optional ones first.",
86 ));
87 }
88
89 let name = ident;
90 let evaluated_name = Self::parse_attrs(attrs)?
91 .unwrap_or_else(|| Ident::new(&format!("Evaluated{name}"), Span::call_site()));
92
93 Ok(FilterParameters {
94 name,
95 evaluated_name,
96 fields,
97 vis,
98 })
99 }
100}
101
102struct FilterParametersFields<'a> {
104 parameters: Punctuated<FilterParameter<'a>, Token![,]>,
105}
106
107impl<'a> FilterParametersFields<'a> {
108 fn required_after_optional(&self) -> Option<&FilterParameter<'_>> {
114 self.parameters
115 .iter()
116 .filter(|parameter| parameter.is_positional())
117 .skip_while(|parameter| parameter.is_required())
118 .find(|parameter| !parameter.is_optional())
119 }
120
121 fn from_fields(fields: &'a Fields) -> Result<Self> {
123 match fields {
124 Fields::Named(fields) => {
125 let parameters = fields
126 .named
127 .iter()
128 .map(|field| {
129 let name = field.ident.as_ref().expect("Fields are named.");
130 FilterParameter::new(name, field)
131 })
132 .collect::<Result<Punctuated<_, Token![,]>>>()?;
133
134 if parameters.is_empty() {
135 Err(Error::new_spanned(
136 fields,
137 "FilterParameters fields must have at least one field. To define an argumentless filter, just skip the `parameters(...)` element in `ParseFilter`.",
138 ))
139 } else {
140 Ok(Self { parameters })
141 }
142 }
143
144 Fields::Unnamed(fields) => {
145 Err(Error::new_spanned(
146 fields,
147 "FilterParameters fields must have explicit names. Tuple structs are not allowed.",
148 ))
149 }
150
151 Fields::Unit => {
152 Err(Error::new_spanned(
153 fields,
154 "FilterParameters fields must have at least one field. To define an argumentless filter, just skip the `parameters(...)` element in `ParseFilter`.",
155 ))
156 }
157 }
158 }
159}
160
161struct FilterParameter<'a> {
163 name: &'a Ident,
164 is_optional: bool,
165 meta: FilterParameterMeta,
166}
167
168impl<'a> FilterParameter<'a> {
169 const ERROR_INVALID_TYPE: &'static str = "Invalid type. All fields in FilterParameters must be either of type `Expression` or `Option<Expression>`";
171
172 fn get_type_name(ty: &Type) -> Result<&PathSegment> {
175 match ty {
176 Type::Path(ty) => {
177 let path = match ty.path.segments.last() {
178 Some(path) => path,
179 None => return Err(Error::new_spanned(ty, Self::ERROR_INVALID_TYPE)),
180 };
181
182 Ok(path)
183 }
184 ty => Err(Error::new_spanned(ty, Self::ERROR_INVALID_TYPE)),
185 }
186 }
187
188 fn parse_type_is_optional(ty: &Type) -> Result<bool> {
194 let path = Self::get_type_name(ty)?;
195 match path.ident.to_string().as_str() {
196 "Option" => match &path.arguments {
197 PathArguments::AngleBracketed(arguments) => {
198 let args = &arguments.args;
199 if args.len() != 1 {
200 return Err(Error::new_spanned(ty, Self::ERROR_INVALID_TYPE));
201 }
202 let arg = match args.last() {
203 Some(arg) => arg,
204 None => return Err(Error::new_spanned(ty, Self::ERROR_INVALID_TYPE)),
205 };
206
207 if let GenericArgument::Type(ty) = arg {
208 let path = Self::get_type_name(ty)?;
209 if path.ident.to_string().as_str() == "Expression"
210 && path.arguments.is_empty()
211 {
212 return Ok(true);
213 }
214 }
215 Err(Error::new_spanned(ty, Self::ERROR_INVALID_TYPE))
216 }
217 _ => Err(Error::new_spanned(ty, Self::ERROR_INVALID_TYPE)),
218 },
219 "Expression" => {
220 if !path.arguments.is_empty() {
221 Err(Error::new_spanned(ty, Self::ERROR_INVALID_TYPE))
222 } else {
223 Ok(false)
224 }
225 }
226 _ => Err(Error::new_spanned(ty, Self::ERROR_INVALID_TYPE)),
227 }
228 }
229
230 fn new(name: &'a Ident, field: &Field) -> Result<Self> {
232 let is_optional = Self::parse_type_is_optional(&field.ty)?;
233 let meta = FilterParameterMeta::from_field(field)?;
234
235 Ok(FilterParameter {
236 name,
237 is_optional,
238 meta,
239 })
240 }
241
242 fn is_optional(&self) -> bool {
244 self.is_optional
245 }
246
247 fn is_required(&self) -> bool {
249 !self.is_optional
250 }
251
252 fn is_positional(&self) -> bool {
254 self.meta.mode == FilterParameterMode::Positional
255 }
256
257 fn is_keyword(&self) -> bool {
259 self.meta.mode == FilterParameterMode::Keyword
260 }
261
262 fn liquid_name(&self) -> String {
267 match &self.meta.rename {
268 Some(name) => name.clone(),
269 None => self.name.to_string(),
270 }
271 }
272}
273
274impl ToTokens for FilterParameter<'_> {
275 fn to_tokens(&self, tokens: &mut TokenStream) {
276 self.name.to_tokens(tokens);
277 }
278}
279
280#[derive(PartialEq)]
282enum FilterParameterMode {
283 Keyword,
284 Positional,
285}
286
287impl FromStr for FilterParameterMode {
288 type Err = String;
289 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
290 match s {
291 "keyword" => Ok(FilterParameterMode::Keyword),
292 "positional" => Ok(FilterParameterMode::Positional),
293 s => Err(format!(
294 "Expected either \"keyword\" or \"positional\". Found \"{s}\"."
295 )),
296 }
297 }
298}
299
300enum FilterParameterType {
302 Value,
304
305 Scalar,
307 Integer,
308 Float,
309 Bool,
310 DateTime,
311 Date,
312 Str,
313}
314
315impl FromStr for FilterParameterType {
316 type Err = String;
317 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
318 match s {
319 "any" => Ok(FilterParameterType::Value),
320 "scalar" => Ok(FilterParameterType::Scalar),
321 "integer" => Ok(FilterParameterType::Integer),
322 "float" => Ok(FilterParameterType::Float),
323 "bool" => Ok(FilterParameterType::Bool),
324 "date_time" => Ok(FilterParameterType::DateTime),
325 "date" => Ok(FilterParameterType::Date),
326 "str" => Ok(FilterParameterType::Str),
327 _ => Err(format!("Expected one of the following: \"any\", \"integer\", \"float\", \"bool\", \"date_time\", \"date\", \"scalar\", or \"str\". Found \"{s}\".")),
328 }
329 }
330}
331
332struct FilterParameterMeta {
334 rename: Option<String>,
335 description: String,
336 mode: FilterParameterMode,
337 ty: FilterParameterType,
338}
339
340impl FilterParameterMeta {
341 fn parse_parameter_attribute(attr: &Attribute) -> Result<Self> {
343 let mut rename = AssignOnce::Unset;
344 let mut description = AssignOnce::Unset;
345 let mut mode = AssignOnce::Unset;
346 let mut arg_type = AssignOnce::Unset;
347 attr.parse_nested_meta(|meta| {
348 if meta.path.is_ident("rename") {
349 assign_str_value(&mut rename, attr, "rename", &meta)?;
350 } else if meta.path.is_ident("description") {
351 assign_str_value(&mut description, attr, "description", &meta)?;
352 } else if meta.path.is_ident("mode") {
353 parse_str_value(&mut mode, attr, "mode", &meta)?;
354 } else if meta.path.is_ident("arg_type") {
355 parse_str_value(&mut arg_type, attr, "arg_type", &meta)?;
356 } else {
357 return Err(Error::new(
358 attr.span(),
359 format!(
360 "unknown `{}` parameter attribute",
361 meta.path.to_token_stream()
362 ),
363 ));
364 }
365 Ok(())
366 })?;
367
368 let rename = rename.into_option();
369 let description = description.unwrap_or_err(|| Error::new_spanned(
370 attr,
371 "Found parameter without description. Description is necessary in order to properly generate ParameterReflection.",
372 ))?;
373 let mode = mode.default_to(FilterParameterMode::Positional);
374 let ty = arg_type.default_to(FilterParameterType::Value);
375
376 Ok(FilterParameterMeta {
377 rename,
378 description,
379 mode,
380 ty,
381 })
382 }
383
384 fn from_field(field: &Field) -> Result<Self> {
386 let mut parameter_attrs = field
387 .attrs
388 .iter()
389 .filter(|attr| attr.path().is_ident("parameter"));
390
391 match (parameter_attrs.next(), parameter_attrs.next()) {
392 (Some(attr), None) => Self::parse_parameter_attribute(attr),
393
394 (_, Some(attr)) => Err(Error::new_spanned(
395 attr,
396 "Found multiple definitions for `parameter` attribute.",
397 )),
398
399 _ => Err(Error::new_spanned(
400 field,
401 "Found parameter without #[parameter] attribute. All filter parameters must be accompanied by this attribute.",
402 )),
403 }
404 }
405}
406
407fn generate_construct_positional_field(
409 field: &FilterParameter<'_>,
410 required: usize,
411) -> TokenStream {
412 let name = &field.name;
413
414 if field.is_optional() {
415 quote! {
416 let #name = args.positional.next();
417 }
418 } else {
419 let plural = if required == 1 { None } else { Some("s") };
420 quote! {
421 let #name = args.positional.next().ok_or_else(||
422 ::liquid_core::error::Error::with_msg("Invalid number of arguments")
423 .context("cause", concat!("expected at least ", #required, " positional argument", #plural))
424 )?;
425 }
426 }
427}
428
429fn generate_evaluate_field(field: &FilterParameter<'_>) -> TokenStream {
431 let name = &field.name;
432 let liquid_name = field.liquid_name();
433 let ty = &field.meta.ty;
434
435 let to_type = match ty {
436 FilterParameterType::Value => quote! { ::std::result::Result::Ok(#name) },
437 FilterParameterType::Scalar => quote! {
438 match #name {
439 ::liquid_core::ValueCow::Owned(v) => {
440 v.as_scalar().into_owned()
441 },
442 ::liquid_core::ValueCow::Borrowed(v) => {
443 v.as_scalar()
444 },
445 }.ok_or_else(||
446 ::liquid_core::error::Error::with_msg("Invalid argument")
447 .context("argument", #liquid_name)
448 .context("cause", "Scalar expected")
449 )
450 },
451 FilterParameterType::Integer => quote! {
452 #name.as_scalar()
453 .and_then(|s| s.to_integer())
454 .ok_or_else(||
455 ::liquid_core::error::Error::with_msg("Invalid argument")
456 .context("argument", #liquid_name)
457 .context("cause", "Whole number expected")
458 )
459 },
460 FilterParameterType::Float => quote! {
461 #name.as_scalar()
462 .and_then(|s| s.to_float())
463 .ok_or_else(||
464 ::liquid_core::error::Error::with_msg("Invalid argument")
465 .context("argument", #liquid_name)
466 .context("cause", "Fractional number expected")
467 )
468 },
469 FilterParameterType::Bool => quote! {
470 #name.as_scalar()
471 .and_then(|s| s.to_bool())
472 .ok_or_else(||
473 ::liquid_core::error::Error::with_msg("Invalid argument")
474 .context("argument", #liquid_name)
475 .context("cause", "Boolean expected")
476 )
477 },
478 FilterParameterType::DateTime => quote! {
479 #name.as_scalar()
480 .and_then(|s| s.to_date_time())
481 .ok_or_else(||
482 ::liquid_core::error::Error::with_msg("Invalid argument")
483 .context("argument", #liquid_name)
484 .context("cause", "DateTime expected")
485 )
486 },
487 FilterParameterType::Date => quote! {
488 #name.as_scalar()
489 .and_then(|s| s.to_date())
490 .ok_or_else(||
491 ::liquid_core::error::Error::with_msg("Invalid argument")
492 .context("argument", #liquid_name)
493 .context("cause", "Date expected")
494 )
495 },
496 FilterParameterType::Str => quote! {
497 match #name {
498 ::liquid_core::ValueCow::Owned(v) => {
499 ::std::result::Result::Ok(v.to_kstr().into_owned().into())
500 },
501 ::liquid_core::ValueCow::Borrowed(v) => {
502 ::std::result::Result::Ok(v.to_kstr())
503 },
504 }
505 },
506 };
507
508 if field.is_optional() {
509 quote! {
510 let #name = self.#name.as_ref().map(|field| {
511 let #name = field.evaluate(runtime)?;
512 let #name = #to_type?;
513 ::std::result::Result::Ok(#name)
514 }).transpose()?;
515 }
516 } else {
517 quote! {
518 let #name = self.#name.evaluate(runtime)?;
519 let #name = #to_type?;
520 }
521 }
522}
523
524fn generate_keyword_match_arm(field: &FilterParameter<'_>) -> TokenStream {
526 let rust_name = &field.name;
527 let liquid_name = field.liquid_name();
528
529 quote! {
530 #liquid_name => if #rust_name.is_none() {
531 #rust_name = ::std::option::Option::Some(arg.1);
532 } else {
533 return ::std::result::Result::Err(::liquid_core::error::Error::with_msg(concat!("Multiple definitions of `", #liquid_name, "`")));
534 },
535 }
536}
537
538fn generate_impl_filter_parameters(filter_parameters: &FilterParameters<'_>) -> TokenStream {
540 let FilterParameters {
541 name,
542 evaluated_name,
543 fields,
544 ..
545 } = filter_parameters;
546
547 let num_min_positional = fields
548 .parameters
549 .iter()
550 .filter(|parameter| parameter.is_positional() && parameter.is_required())
551 .count();
552
553 let num_max_positional = fields
554 .parameters
555 .iter()
556 .filter(|parameter| parameter.is_positional())
557 .count();
558
559 let too_many_args = {
560 let plural = if num_max_positional == 1 {
561 None
562 } else {
563 Some("s")
564 };
565 quote! {
566 ::liquid_core::error::Error::with_msg("Invalid number of positional arguments")
567 .context("cause", concat!("expected at most ", #num_max_positional, " positional argument", #plural))
568 }
569 };
570
571 let field_names = fields.parameters.iter().map(|field| &field.name);
572 let comma_separated_field_names = quote! { #(#field_names,)* };
573
574 let evaluate_fields = fields.parameters.iter().map(generate_evaluate_field);
575
576 let construct_positional_fields = fields
577 .parameters
578 .iter()
579 .filter(|parameter| parameter.is_positional())
580 .map(|field| generate_construct_positional_field(field, num_min_positional));
581
582 let keyword_fields = fields
583 .parameters
584 .iter()
585 .filter(|parameter| parameter.is_keyword());
586
587 let match_keyword_parameters_arms = fields
588 .parameters
589 .iter()
590 .filter(|parameter| parameter.is_keyword())
591 .map(generate_keyword_match_arm);
592
593 let unwrap_required_keyword_fields = fields
594 .parameters
595 .iter()
596 .filter(|parameter| parameter.is_keyword() && parameter.is_required())
597 .map(|field| {
598 let liquid_name = field.liquid_name();
599 quote!{ let #field = #field.ok_or_else(|| ::liquid_core::error::Error::with_msg(concat!("Expected named argument `", #liquid_name, "`")))?; }
600 });
601
602 quote! {
603 impl<'a> ::liquid_core::parser::FilterParameters<'a> for #name {
604 type EvaluatedFilterParameters = #evaluated_name<'a>;
605
606 fn from_args(mut args: ::liquid_core::parser::FilterArguments) -> ::liquid_core::error::Result<Self> {
607 #![allow(clippy::ref_option_ref)]
608
609 #(#construct_positional_fields)*
610 if let ::std::option::Option::Some(arg) = args.positional.next() {
611 return ::std::result::Result::Err(#too_many_args);
612 }
613
614 #(let mut #keyword_fields = ::std::option::Option::None;)*
615 #[allow(clippy::never_loop)] while let ::std::option::Option::Some(arg) = args.keyword.next() {
617 match arg.0 {
618 #(#match_keyword_parameters_arms)*
619 keyword => return ::std::result::Result::Err(::liquid_core::error::Error::with_msg(format!("Unexpected named argument `{}`", keyword))),
620 }
621 }
622 #(#unwrap_required_keyword_fields)*
623
624 Ok( #name { #comma_separated_field_names } )
625 }
626
627 fn evaluate(&'a self, runtime: &'a dyn ::liquid_core::runtime::Runtime) -> ::liquid_core::error::Result<Self::EvaluatedFilterParameters> {
628 #![allow(clippy::ref_option_ref)]
629
630 #(#evaluate_fields)*
631
632 Ok( #evaluated_name { #comma_separated_field_names __phantom_data: ::std::marker::PhantomData } )
633 }
634 }
635 }
636}
637
638fn generate_evaluated_struct(filter_parameters: &FilterParameters<'_>) -> TokenStream {
640 let FilterParameters {
641 evaluated_name,
642 fields,
643 vis,
644 ..
645 } = filter_parameters;
646
647 let field_types = fields.parameters.iter().map(|field| {
648 let ty = match &field.meta.ty {
649 FilterParameterType::Value => quote! { ::liquid_core::model::ValueCow<'a> },
650 FilterParameterType::Scalar => quote! { ::liquid_core::model::ScalarCow<'a> },
651 FilterParameterType::Integer => quote! { i64 },
652 FilterParameterType::Float => quote! { f64 },
653 FilterParameterType::Bool => quote! { bool },
654 FilterParameterType::DateTime => quote! { ::liquid_core::model::DateTime },
655 FilterParameterType::Date => quote! { ::liquid_core::model::Date },
656 FilterParameterType::Str => quote! { ::liquid_core::model::KStringCow<'a> },
657 };
658
659 if field.is_optional() {
660 quote! { ::std::option::Option< #ty > }
661 } else {
662 quote! { #ty }
663 }
664 });
665
666 let field_names = fields.parameters.iter().map(|field| &field.name);
667
668 quote! {
669 #[allow(clippy::ref_option_ref)]
670 #vis struct #evaluated_name <'a>{
671 #(#field_names : #field_types,)*
672 __phantom_data: ::std::marker::PhantomData<&'a ()>
673 }
674 }
675}
676
677fn generate_parameter_reflection(field: &FilterParameter<'_>) -> TokenStream {
679 let name = field.liquid_name();
680 let description = &field.meta.description;
681 let is_optional = field.is_optional();
682
683 quote! {
684 ::liquid_core::parser::ParameterReflection {
685 name: #name,
686 description: #description,
687 is_optional: #is_optional,
688 },
689 }
690}
691
692fn generate_impl_reflection(filter_parameters: &FilterParameters<'_>) -> TokenStream {
694 let FilterParameters { name, fields, .. } = filter_parameters;
695
696 let kw_params_reflection = fields
697 .parameters
698 .iter()
699 .filter(|parameter| parameter.is_keyword())
700 .map(generate_parameter_reflection);
701
702 let pos_params_reflection = fields
703 .parameters
704 .iter()
705 .filter(|parameter| parameter.is_positional())
706 .map(generate_parameter_reflection);
707
708 quote! {
709 impl ::liquid_core::parser::FilterParametersReflection for #name {
710 fn positional_parameters() -> &'static [::liquid_core::parser::ParameterReflection] {
711 #![allow(clippy::ref_option_ref)]
712 &[ #(#pos_params_reflection)* ]
713 }
714
715 fn keyword_parameters() -> &'static [::liquid_core::parser::ParameterReflection] {
716 #![allow(clippy::ref_option_ref)]
717 &[ #(#kw_params_reflection)* ]
718 }
719 }
720 }
721}
722
723fn generate_access_positional_field_for_display(field: &FilterParameter<'_>) -> TokenStream {
725 let rust_name = &field.name;
726
727 if field.is_optional() {
728 quote! {
729 self.#rust_name.as_ref()
730 }
731 } else {
732 quote! {
733 ::std::option::Option::Some(&self.#rust_name)
734 }
735 }
736}
737
738fn generate_access_keyword_field_for_display(field: &FilterParameter<'_>) -> TokenStream {
740 let rust_name = &field.name;
741 let liquid_name = field.liquid_name();
742
743 if field.is_optional() {
744 quote! {
745 (#liquid_name, self.#rust_name.as_ref())
746 }
747 } else {
748 quote! {
749 (#liquid_name, ::std::option::Option::Some(&self.#rust_name))
750 }
751 }
752}
753
754fn generate_impl_display(filter_parameters: &FilterParameters<'_>) -> TokenStream {
756 let FilterParameters { name, fields, .. } = filter_parameters;
757
758 let positional_fields = fields
759 .parameters
760 .iter()
761 .filter(|parameter| parameter.is_positional())
762 .map(generate_access_positional_field_for_display);
763
764 let keyword_fields = fields
765 .parameters
766 .iter()
767 .filter(|parameter| parameter.is_keyword())
768 .map(generate_access_keyword_field_for_display);
769
770 quote! {
771 impl ::std::fmt::Display for #name {
772 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
773 #![allow(clippy::ref_option_ref)]
774 let positional = [#(#positional_fields ,)*];
775 let keyword = [#(#keyword_fields ,)*];
776
777 let positional = positional
778 .iter()
779 .filter_map(|p: &::std::option::Option<&::liquid_core::runtime::Expression>| p.as_ref())
780 .map(|p| p.to_string());
781 let keyword = keyword.iter().filter_map(|p: &(&str, ::std::option::Option<&::liquid_core::runtime::Expression>)| match p.1 {
782 ::std::option::Option::Some(p1) => ::std::option::Option::Some(format!("{}: {}", p.0, p1)),
783 ::std::option::Option::None => ::std::option::Option::None,
784 });
785
786 let parameters = positional
787 .chain(keyword)
788 .collect::<::std::vec::Vec<::std::string::String>>()
789 .join(", ");
790
791 write!(
792 f,
793 "{}",
794 parameters
795 )
796 }
797 }
798 }
799}
800
801pub(crate) fn derive(input: &DeriveInput) -> TokenStream {
802 let filter_parameters = match FilterParameters::from_input(input) {
803 Ok(filter_parameters) => filter_parameters,
804 Err(err) => return err.to_compile_error(),
805 };
806
807 let mut output = TokenStream::new();
808 output.extend(generate_impl_filter_parameters(&filter_parameters));
809 output.extend(generate_impl_reflection(&filter_parameters));
810 output.extend(generate_impl_display(&filter_parameters));
811 output.extend(generate_evaluated_struct(&filter_parameters));
812
813 output
814}