liquid_derive/parse_filter/
mod.rs1use 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
11struct ParseFilter<'a> {
13 name: &'a Ident,
14 meta: ParseFilterMeta,
15 generics: &'a Generics,
16}
17
18impl<'a> ParseFilter<'a> {
19 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 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 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 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
84struct 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 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}