liquid_derive/parse_filter/
mod.rs

1use proc_macro2::{Ident, Span, TokenStream};
2use quote::{quote, quote_spanned, ToTokens};
3use syn::spanned::Spanned as _;
4use syn::{Attribute, Data, DeriveInput, Error, Generics, Path, Result};
5
6use crate::helpers::{assign_path, assign_str_value, AssignOnce};
7
8pub(crate) mod filter_reflection;
9pub(crate) mod parse;
10
11/// Struct that contains information to generate the necessary code for `ParseFilter`.
12struct ParseFilter<'a> {
13    name: &'a Ident,
14    meta: ParseFilterMeta,
15    generics: &'a Generics,
16}
17
18impl<'a> ParseFilter<'a> {
19    /// Generates `impl` declaration of the given trait for the structure
20    /// represented by `self`.
21    fn generate_impl(&self, trait_name: TokenStream) -> TokenStream {
22        let name = &self.name;
23        let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
24        quote! {
25            impl #impl_generics #trait_name for #name #ty_generics #where_clause
26        }
27    }
28
29    /// Asserts that this is an empty struct.
30    fn validate_data(data: &Data) -> Result<()> {
31        match data {
32            Data::Struct(_) => Ok(()),
33            Data::Enum(data) => Err(Error::new_spanned(
34                data.enum_token,
35                "Enums cannot be ParseFilter.",
36            )),
37            Data::Union(data) => Err(Error::new_spanned(
38                data.union_token,
39                "Unions cannot be ParseFilter.",
40            )),
41        }
42    }
43
44    /// Searches for `#[filter(...)]` in order to parse `ParseFilterMeta`.
45    fn parse_attrs(attrs: &[Attribute]) -> Result<ParseFilterMeta> {
46        let mut filter_attrs = attrs.iter().filter(|attr| attr.path().is_ident("filter"));
47
48        match (filter_attrs.next(), filter_attrs.next()) {
49            (Some(attr), None) => ParseFilterMeta::from_attr(attr),
50
51            (_, Some(attr)) => Err(Error::new_spanned(
52                attr,
53                "Found multiple definitions for `filter` attribute.",
54            )),
55
56            _ => Err(Error::new(
57                Span::call_site(),
58                "Cannot find `filter` attribute in target struct. Have you tried adding `#[parser(name=\"...\", description=\"...\", parameters(...), parsed(...))]`?",
59            )),
60        }
61    }
62
63    /// Tries to create a new `ParseFilter` from the given `DeriveInput`
64    fn from_input(input: &'a DeriveInput) -> Result<Self> {
65        let DeriveInput {
66            attrs,
67            data,
68            ident,
69            generics,
70            ..
71        } = input;
72
73        Self::validate_data(data)?;
74        let meta = Self::parse_attrs(attrs)?;
75
76        Ok(ParseFilter {
77            name: ident,
78            meta,
79            generics,
80        })
81    }
82}
83
84/// Struct that contains information parsed in `#[filter(...)]` attribute.
85struct ParseFilterMeta {
86    filter_name: Result<String>,
87    filter_description: Result<String>,
88    parameters_struct_name: Option<Path>,
89    filter_struct_name: Result<Path>,
90}
91
92impl ParseFilterMeta {
93    /// Tries to create a new `ParseFilterMeta` from the given `Attribute`
94    fn from_attr(attr: &Attribute) -> Result<Self> {
95        let mut name = AssignOnce::Unset;
96        let mut description = AssignOnce::Unset;
97        let mut parameters = AssignOnce::Unset;
98        let mut parsed = AssignOnce::Unset;
99
100        attr.parse_nested_meta(|meta| {
101            if meta.path.is_ident("name") {
102                assign_str_value(&mut name, attr, "name", &meta)?;
103            } else if meta.path.is_ident("description") {
104                assign_str_value(&mut description, attr, "description", &meta)?;
105            } else if meta.path.is_ident("parameters") {
106                assign_path(&mut parameters, attr, "parameters", &meta)?;
107            } else if meta.path.is_ident("parsed") {
108                assign_path(&mut parsed, attr, "parsed", &meta)?;
109            } else {
110                return Err(Error::new(
111                    attr.span(),
112                    format!(
113                        "unknown `{}` parameter attribute",
114                        meta.path.to_token_stream()
115                    ),
116                ));
117            }
118            Ok(())
119        })?;
120
121        let filter_name = name.unwrap_or_err(|| Error::new_spanned(
122            attr,
123            "FilterReflection does not have a name. Have you tried `#[filter(name=\"...\", description=\"...\", parameters(...), parsed(...))]`?",
124        ));
125        let filter_description = description.unwrap_or_err(|| Error::new_spanned(
126            attr,
127            "FilterReflection does not have a description. Have you tried `#[filter(name=\"...\", description=\"...\", parameters(...), parsed(...))]`?",
128        ));
129        let parameters_struct_name = parameters.into_option();
130        let filter_struct_name = parsed.unwrap_or_err(|| Error::new_spanned(
131            attr,
132            "ParseFilter does not have a Filter to return. Have you tried `#[filter(name=\"...\", description=\"...\", parameters(...), parsed(...))]`?",
133        ));
134
135        Ok(ParseFilterMeta {
136            filter_name,
137            filter_description,
138            parameters_struct_name,
139            filter_struct_name,
140        })
141    }
142}