1use crate::{Action, ActionKind, SelectedField, Selection, TypeKind, TypeUtils};
2use proc_macro2::{Ident, Span};
3use syn::{
4 braced, parenthesized,
5 parse::{Nothing, Parse, ParseStream},
6 punctuated::Punctuated,
7 Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Generics, Result, Token, Visibility,
8};
9
10impl Parse for TypeUtils {
11 fn parse(input: ParseStream) -> Result<Self> {
12 let input: DeriveInput = input.parse()?;
13 input.try_into()
14 }
15}
16
17impl TryFrom<DeriveInput> for TypeUtils {
18 type Error = Error;
19
20 fn try_from(input: DeriveInput) -> Result<Self> {
21 let mut actions = Vec::new();
22 let mut attrs = Vec::new();
23 let mut derives = Vec::new();
24 for attr in input.attrs {
25 let kind = match attr.path.get_ident() {
26 Some(ident) if ident == "tu_derive" => {
27 derives.extend(syn::parse2::<TuDerive>(attr.tokens)?.0);
28 continue;
29 }
30 Some(ident) if ident == "pick" => ActionKind::Pick,
31 Some(ident) if ident == "omit" => ActionKind::Omit,
32 Some(_) | None => {
33 attrs.push(attr);
34 continue;
35 }
36 };
37
38 let PartialAction {
39 vis,
40 ident,
41 generics,
42 selection,
43 } = syn::parse2::<PartialAction>(attr.tokens)?;
44
45 actions.push(Action {
46 kind,
47 derives: std::mem::take(&mut derives),
48 vis,
49 ident,
50 generics,
51 selection,
52 });
53 }
54
55 if let Some(first) = derives.first() {
56 return Err(Error::new(first.span(), "tu_derive applies to nothing"));
57 }
58
59 if actions.is_empty() {
60 return Err(Error::new(
61 Span::call_site(),
62 "TypeUtils derived with no actions",
63 ));
64 }
65
66 let utils = TypeUtils {
67 kind: input.data.try_into()?,
68 attrs,
69 actions,
70 ident: input.ident,
71 };
72
73 type Validator<'v> = Box<dyn FnMut((ActionKind, &Ident, &SelectedField)) -> Result<()> + 'v>;
74
75 let validate: Validator = match &utils.kind {
76 TypeKind::Struct(target_fields) => Box::new(|(action_kind, _action_ident, action_field)| {
77 if target_fields
78 .named
79 .iter()
80 .map(|named| named.ident.as_ref().unwrap())
81 .any(|target_ident| target_ident == &action_field.ident)
82 {
83 Ok(())
84 } else {
85 Err(Error::new(
86 action_field.ident.span(),
87 format!(
88 "unknown field `{}` in {action_kind} action for {}",
89 action_field.ident, utils.ident
90 ),
91 ))
92 }
93 }),
94 TypeKind::TupleStruct(_) => Box::new(|(action_kind, _action_ident, _action_field)| {
95 Err(Error::new(
96 Span::call_site(),
97 format!("{action_kind} action is not supported for tuple structs"),
98 ))
99 }),
100 TypeKind::UnitStruct => Box::new(|(action_kind, _action_ident, _action_field)| {
101 Err(Error::new(
102 Span::call_site(),
103 format!("{action_kind} action is not supported for unit structs"),
104 ))
105 }),
106 TypeKind::Enum(variants) => Box::new(|(action_kind, _action_ident, action_field)| {
107 if variants
108 .iter()
109 .any(|variant| variant.ident == action_field.ident)
110 {
111 Ok(())
112 } else {
113 Err(Error::new(
114 action_field.ident.span(),
115 format!(
116 "unknown variant `{}` in {action_kind} action for {}",
117 action_field.ident, utils.ident
118 ),
119 ))
120 }
121 }),
122 };
123
124 utils
125 .actions
126 .iter()
127 .map(|action| {
128 action
129 .selection
130 .0
131 .iter()
132 .map(|field| (action.kind, &action.ident, field))
133 })
134 .flatten()
135 .try_for_each(validate)?;
136
137 Ok(utils)
138 }
139}
140
141impl TryFrom<Data> for TypeKind {
142 type Error = Error;
143
144 fn try_from(data: Data) -> Result<Self> {
145 match data {
146 Data::Struct(DataStruct {
147 fields: Fields::Named(fields),
148 ..
149 }) => Ok(TypeKind::Struct(fields)),
150 Data::Struct(DataStruct {
151 fields: Fields::Unnamed(fields),
152 ..
153 }) => Ok(TypeKind::TupleStruct(fields)),
154 Data::Struct(DataStruct {
155 fields: Fields::Unit,
156 ..
157 }) => Ok(TypeKind::UnitStruct),
158 Data::Enum(DataEnum { variants, .. }) => Ok(TypeKind::Enum(
159 variants.into_iter().map(From::from).collect(),
160 )),
161 Data::Union(_) => Err(Error::new(
162 Span::call_site(),
163 "TypeUtils cannot be used on union types",
164 )),
165 }
166 }
167}
168
169impl Parse for Selection {
170 fn parse(input: ParseStream) -> Result<Self> {
171 let content;
172 braced!(content in input);
173 Ok(Self(
174 Punctuated::<_, Token![,]>::parse_separated_nonempty(&content)?
175 .into_iter()
176 .collect::<Vec<_>>(),
177 ))
178 }
179}
180
181impl Parse for SelectedField {
182 fn parse(input: ParseStream) -> Result<Self> {
183 Ok(SelectedField {
184 vis: input.parse()?,
185 ident: input.parse()?,
186 })
187 }
188}
189
190struct TuDerive(Punctuated<Ident, Token![,]>);
191
192impl Parse for TuDerive {
193 fn parse(input: ParseStream) -> Result<Self> {
194 let content;
195 parenthesized!(content in input);
196 let tu_derive = TuDerive(Punctuated::parse_separated_nonempty(&content)?);
197 content.parse::<Nothing>()?;
198 Ok(tu_derive)
199 }
200}
201
202struct PartialAction {
203 vis: Visibility,
204 ident: Ident,
205 generics: Generics,
206 selection: Selection,
207}
208
209impl Parse for PartialAction {
210 fn parse(input: ParseStream) -> Result<Self> {
211 let content;
212 parenthesized!(content in input);
213 Ok(PartialAction {
214 vis: content.parse()?,
215 ident: content.parse()?,
216 generics: {
217 let mut generics: Generics = content.parse()?;
218 generics.where_clause = content.parse()?;
219 generics
220 },
221 selection: content.parse()?,
222 })
223 }
224}