typed_builder_macro/
mutator.rs
1use std::collections::HashSet;
2
3use proc_macro2::Ident;
4use syn::{
5 parse::{Parse, ParseStream},
6 parse_quote,
7 punctuated::Punctuated,
8 spanned::Spanned,
9 Error, Expr, FnArg, ItemFn, PatIdent, ReturnType, Signature, Token, Type,
10};
11
12use crate::util::{pat_to_ident, ApplyMeta, AttrArg};
13
14#[derive(Debug, Clone)]
15pub struct Mutator {
16 pub fun: ItemFn,
17 pub required_fields: HashSet<Ident>,
18}
19
20#[derive(Default)]
21struct MutatorAttribute {
22 requires: HashSet<Ident>,
23}
24
25impl ApplyMeta for MutatorAttribute {
26 fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> {
27 if expr.name() != "requires" {
28 return Err(Error::new_spanned(expr.name(), "Only `requires` is supported"));
29 }
30
31 match expr.key_value()?.parse_value()? {
32 Expr::Array(syn::ExprArray { elems, .. }) => self.requires.extend(
33 elems
34 .into_iter()
35 .map(|expr| match expr {
36 Expr::Path(path) if path.path.get_ident().is_some() => {
37 Ok(path.path.get_ident().cloned().expect("should be ident"))
38 }
39 expr => Err(Error::new_spanned(expr, "Expected field name")),
40 })
41 .collect::<Result<Vec<_>, _>>()?,
42 ),
43 expr => {
44 return Err(Error::new_spanned(
45 expr,
46 "Only list of field names [field1, field2, …] supported",
47 ))
48 }
49 }
50 Ok(())
51 }
52}
53
54impl Parse for Mutator {
55 fn parse(input: ParseStream) -> syn::Result<Self> {
56 let mut fun: ItemFn = input.parse()?;
57
58 let mut attribute = MutatorAttribute::default();
59
60 let mut i = 0;
61 while i < fun.attrs.len() {
62 let attr = &fun.attrs[i];
63 if attr.path().is_ident("mutator") {
64 attribute.apply_attr(attr)?;
65 fun.attrs.remove(i);
66 } else {
67 i += 1;
68 }
69 }
70
71 if let Some(FnArg::Receiver(receiver)) = fun.sig.inputs.first_mut() {
73 *receiver = parse_quote!(&mut self);
74 } else {
75 return Err(syn::Error::new(
77 fun.sig
78 .inputs
79 .first()
80 .map(Spanned::span)
81 .unwrap_or(fun.sig.paren_token.span.span()),
82 "mutator needs to take a reference to `self`",
83 ));
84 };
85
86 Ok(Self {
87 fun,
88 required_fields: attribute.requires,
89 })
90 }
91}
92
93impl Mutator {
94 pub fn outer_sig(&self, output: Type) -> Signature {
96 let mut sig = self.fun.sig.clone();
97 sig.output = ReturnType::Type(Default::default(), output.into());
98
99 sig.inputs = sig
100 .inputs
101 .into_iter()
102 .enumerate()
103 .map(|(i, input)| match input {
104 FnArg::Receiver(_) => parse_quote!(self),
105 FnArg::Typed(mut input) => {
106 input.pat = Box::new(
107 PatIdent {
108 attrs: Vec::new(),
109 by_ref: None,
110 mutability: None,
111 ident: pat_to_ident(i, &input.pat),
112 subpat: None,
113 }
114 .into(),
115 );
116 FnArg::Typed(input)
117 }
118 })
119 .collect();
120 sig
121 }
122
123 pub fn arguments(&self) -> Punctuated<Ident, Token![,]> {
125 self.fun
126 .sig
127 .inputs
128 .iter()
129 .enumerate()
130 .filter_map(|(i, input)| match &input {
131 FnArg::Receiver(_) => None,
132 FnArg::Typed(input) => Some(pat_to_ident(i, &input.pat)),
133 })
134 .collect()
135 }
136}