type_utils/
parse_impl.rs

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}