1use proc_macro2::TokenStream;
36use quote::ToTokens;
37use syn::{
38 punctuated::Pair,
39 token::{Mut, Ref},
40 *,
41};
42
43use crate::{def_site, is_attr_name, syn_ext::PairExt};
44
45#[derive(Debug, Clone)]
47pub struct Binder<'a> {
48 ident: &'a Ident,
49 body: &'a Data,
50 attrs: &'a [Attribute],
51}
52
53impl<'a> Binder<'a> {
54 pub const fn new(ident: &'a Ident, body: &'a Data, attrs: &'a [Attribute]) -> Self {
57 Binder { ident, body, attrs }
58 }
59
60 pub fn new_from(input: &'a DeriveInput) -> Self {
61 Self::new(&input.ident, &input.data, &input.attrs)
62 }
63
64 pub fn variants(&self) -> Vec<VariantBinder<'a>> {
65 match *self.body {
66 Data::Enum(DataEnum { ref variants, .. }) => {
67 let enum_name = &self.ident;
68 variants
69 .iter()
70 .map(|v| VariantBinder::new(Some(enum_name), &v.ident, &v.fields, &v.attrs))
71 .collect()
72 }
73 Data::Struct(DataStruct { ref fields, .. }) => {
74 vec![VariantBinder::new(None, self.ident, fields, self.attrs)]
75 }
76 Data::Union(_) => unimplemented!("Binder for union type"),
77 }
78 }
79}
80
81#[derive(Debug, Clone)]
83pub struct VariantBinder<'a> {
84 enum_name: Option<&'a Ident>,
86 name: &'a Ident,
88 data: &'a Fields,
89 attrs: &'a [Attribute],
90}
91
92impl<'a> VariantBinder<'a> {
93 pub const fn new(
94 enum_name: Option<&'a Ident>,
95 name: &'a Ident,
96 data: &'a Fields,
97 attrs: &'a [Attribute],
98 ) -> Self {
99 VariantBinder {
100 enum_name,
101 name,
102 data,
103 attrs,
104 }
105 }
106
107 pub const fn variant_name(&self) -> &Ident {
108 self.name
109 }
110
111 pub const fn data(&self) -> &Fields {
112 self.data
113 }
114
115 pub const fn attrs(&self) -> &[Attribute] {
116 self.attrs
117 }
118
119 pub fn qual_path(&self) -> Path {
121 match self.enum_name {
122 Some(enum_name) => {
123 let vn = &self.name;
124
125 parse_quote!(#enum_name::#vn)
126 }
127 None => self.name.clone().into(),
128 }
129 }
130
131 pub fn bind(
134 &self,
135 prefix: &str,
136 by_ref: Option<Ref>,
137 mutability: Option<Mut>,
138 ) -> (Pat, Vec<BindedField<'a>>) {
139 let path = self.qual_path();
140
141 let (pat, bindings) = match *self.data {
142 Fields::Unit => {
143 let pat = Pat::Path(PatPath {
145 qself: None,
146 path,
147 attrs: Default::default(),
148 });
149
150 (pat, Vec::new())
152 }
153 Fields::Named(FieldsNamed {
154 named: ref fields,
155 brace_token,
156 }) => {
157 let mut bindings = Vec::new();
158
159 let fields = fields
160 .pairs()
161 .map(|e| {
162 let (t, p) = e.into_tuple();
163 Pair::new(t, p.cloned())
164 })
165 .enumerate()
166 .map(|(idx, f)| {
167 f.map_item(|f| {
168 let ident = f
169 .ident
170 .clone()
171 .expect("field of struct-like variants should have name");
172
173 let binded_ident =
174 Ident::new(&format!("{prefix}{ident}"), ident.span());
175 bindings.push(BindedField {
176 idx,
177 binded_ident: binded_ident.clone(),
178 field: f,
179 });
180 FieldPat {
181 attrs: f
182 .attrs
183 .iter()
184 .filter(|attr| is_attr_name(attr, "cfg"))
185 .cloned()
186 .collect(),
187 colon_token: f.colon_token,
188 member: Member::Named(ident),
189 pat: Box::new(Pat::Ident(PatIdent {
190 by_ref,
191 mutability,
192 ident: binded_ident,
193 subpat: None,
194 attrs: Default::default(),
195 })),
196 }
197 })
198 })
199 .collect();
200 let pat = Pat::Struct(PatStruct {
202 attrs: Default::default(),
203 qself: None,
204 path,
205 brace_token,
206 fields,
207 rest: None,
208 });
209 (pat, bindings)
210 }
211 Fields::Unnamed(FieldsUnnamed {
212 unnamed: ref fields,
213 paren_token,
214 }) => {
215 let mut bindings = Vec::new();
217
218 let pats = fields
219 .pairs()
220 .map(|e| {
221 let (t, p) = e.into_tuple();
222 Pair::new(t, p.cloned())
223 })
224 .enumerate()
225 .map(|(idx, f)| {
226 f.map_item(|f| {
227 let binded_ident = Ident::new(&format!("{prefix}{idx}"), def_site());
228
229 bindings.push(BindedField {
230 idx,
231 binded_ident: binded_ident.clone(),
232 field: f,
233 });
234
235 Pat::Ident(PatIdent {
236 by_ref,
237 mutability,
238 ident: binded_ident,
239 subpat: None,
240 attrs: Default::default(),
241 })
242 })
243 })
244 .collect();
245 let pat = Pat::TupleStruct(PatTupleStruct {
247 attrs: Default::default(),
248 qself: None,
249 path,
250 paren_token,
251 elems: pats,
252 });
253 (pat, bindings)
254 }
255 };
256
257 (pat, bindings)
270 }
271}
272
273#[derive(Debug, Clone)]
276pub struct BindedField<'a> {
277 binded_ident: Ident,
278 idx: usize,
279 field: &'a Field,
280}
281
282impl BindedField<'_> {
283 pub const fn idx(&self) -> usize {
284 self.idx
285 }
286
287 pub const fn name(&self) -> &Ident {
289 &self.binded_ident
290 }
291
292 pub const fn field(&self) -> &Field {
293 self.field
294 }
295}
296
297impl ToTokens for BindedField<'_> {
298 fn to_tokens(&self, t: &mut TokenStream) {
299 self.binded_ident.to_tokens(t)
300 }
301}