1use proc_macro2::Span;
24use quote::ToTokens;
25use syn::Field;
26
27pub enum FieldKey {
45 Named(syn::Ident),
47 Index(syn::Index),
49}
50
51impl FieldKey {
52 pub fn is_named(&self) -> bool {
54 matches!(self, Self::Named(_))
55 }
56
57 pub fn is_index(&self) -> bool {
59 matches!(self, Self::Index(_))
60 }
61
62 pub fn as_named(&self) -> Option<&syn::Ident> {
64 match self {
65 Self::Named(ident) => Some(ident),
66 _ => None,
67 }
68 }
69
70 pub fn as_index(&self) -> Option<&syn::Index> {
72 match self {
73 Self::Index(index) => Some(index),
74 _ => None,
75 }
76 }
77}
78
79impl From<syn::Ident> for FieldKey {
80 fn from(ident: syn::Ident) -> Self {
81 Self::Named(ident)
82 }
83}
84
85impl From<syn::Index> for FieldKey {
86 fn from(index: syn::Index) -> Self {
87 Self::Index(index)
88 }
89}
90
91impl From<usize> for FieldKey {
92 fn from(index: usize) -> Self {
93 Self::Index(syn::Index {
94 index: index as u32,
95 span: Span::call_site(),
96 })
97 }
98}
99
100impl From<&str> for FieldKey {
101 fn from(name: &str) -> Self {
102 Self::Named(syn::Ident::new(name, Span::call_site()))
103 }
104}
105
106impl std::fmt::Display for FieldKey {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 match self {
109 Self::Named(ident) => write!(f, "{}", ident),
110 Self::Index(index) => write!(f, "{}", index.index),
111 }
112 }
113}
114
115impl ToTokens for FieldKey {
116 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
117 match self {
118 Self::Named(ident) => ident.to_tokens(tokens),
119 Self::Index(index) => index.to_tokens(tokens),
120 }
121 }
122}
123
124pub trait FieldsExt {
146 fn is_named(&self) -> bool;
148 fn is_unnamed(&self) -> bool;
150 fn is_unit(&self) -> bool;
152 fn as_named(&self) -> Option<&syn::FieldsNamed>;
154 fn as_unnamed(&self) -> Option<&syn::FieldsUnnamed>;
156 fn exists(&self, key: &FieldKey) -> bool;
158 fn get(&self, key: &FieldKey) -> Option<&Field>;
160 fn keyed(&self) -> impl Iterator<Item = (FieldKey, &Field)>;
175}
176
177impl FieldsExt for syn::Fields {
178 fn is_named(&self) -> bool {
179 matches!(self, Self::Named(_))
180 }
181
182 fn is_unnamed(&self) -> bool {
183 matches!(self, Self::Unnamed(_))
184 }
185
186 fn is_unit(&self) -> bool {
187 matches!(self, Self::Unit)
188 }
189
190 fn as_named(&self) -> Option<&syn::FieldsNamed> {
191 match self {
192 Self::Named(f) => Some(f),
193 _ => None,
194 }
195 }
196
197 fn as_unnamed(&self) -> Option<&syn::FieldsUnnamed> {
198 match self {
199 Self::Unnamed(f) => Some(f),
200 _ => None,
201 }
202 }
203
204 fn exists(&self, key: &FieldKey) -> bool {
205 self.get(key).is_some()
206 }
207
208 fn keyed(&self) -> impl Iterator<Item = (FieldKey, &Field)> {
209 let pairs: Vec<_> = match self {
210 Self::Named(f) => f
211 .named
212 .iter()
213 .map(|field| {
214 let ident = field.ident.clone().unwrap();
215 (FieldKey::Named(ident), field)
216 })
217 .collect(),
218 Self::Unnamed(f) => f
219 .unnamed
220 .iter()
221 .enumerate()
222 .map(|(i, field)| (FieldKey::from(i), field))
223 .collect(),
224 Self::Unit => Vec::new(),
225 };
226
227 pairs.into_iter()
228 }
229
230 fn get(&self, key: &FieldKey) -> Option<&Field> {
231 match key {
232 FieldKey::Named(ident) => {
233 let named = self.as_named()?;
234 named.named.iter().find(|f| f.ident.as_ref() == Some(ident))
235 }
236 FieldKey::Index(index) => {
237 let mut iter: Box<dyn Iterator<Item = &Field>> = match self {
238 Self::Named(f) => Box::new(f.named.iter()),
239 Self::Unnamed(f) => Box::new(f.unnamed.iter()),
240 Self::Unit => return None,
241 };
242 iter.nth(index.index as usize)
243 }
244 }
245 }
246}
247
248macro_rules! delegate_fields_ext {
249 ($ty:ty, $field:ident) => {
250 impl FieldsExt for $ty {
251 fn is_named(&self) -> bool {
252 self.$field.is_named()
253 }
254
255 fn is_unnamed(&self) -> bool {
256 self.$field.is_unnamed()
257 }
258
259 fn is_unit(&self) -> bool {
260 self.$field.is_unit()
261 }
262
263 fn as_named(&self) -> Option<&syn::FieldsNamed> {
264 self.$field.as_named()
265 }
266
267 fn as_unnamed(&self) -> Option<&syn::FieldsUnnamed> {
268 self.$field.as_unnamed()
269 }
270
271 fn exists(&self, key: &FieldKey) -> bool {
272 self.$field.exists(key)
273 }
274
275 fn keyed(&self) -> impl Iterator<Item = (FieldKey, &Field)> {
276 self.$field.keyed()
277 }
278
279 fn get(&self, key: &FieldKey) -> Option<&Field> {
280 self.$field.get(key)
281 }
282 }
283 };
284}
285
286delegate_fields_ext!(syn::ItemStruct, fields);
287delegate_fields_ext!(syn::DataStruct, fields);
288delegate_fields_ext!(syn::Variant, fields);
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 fn named_fields() -> syn::Fields {
295 let item: syn::ItemStruct = syn::parse_str("struct Foo { x: i32, y: String }").unwrap();
296 item.fields
297 }
298
299 fn unnamed_fields() -> syn::Fields {
300 let item: syn::ItemStruct = syn::parse_str("struct Foo(i32, String);").unwrap();
301 item.fields
302 }
303
304 fn unit_fields() -> syn::Fields {
305 let item: syn::ItemStruct = syn::parse_str("struct Foo;").unwrap();
306 item.fields
307 }
308
309 mod field_key {
310 use super::*;
311
312 #[test]
313 fn from_str() {
314 let key: FieldKey = "id".into();
315 assert!(key.is_named());
316 assert!(!key.is_index());
317 }
318
319 #[test]
320 fn from_usize() {
321 let key: FieldKey = 0usize.into();
322 assert!(key.is_index());
323 assert!(!key.is_named());
324 }
325
326 #[test]
327 fn as_named_some() {
328 let key: FieldKey = "id".into();
329 assert!(key.as_named().is_some());
330 }
331
332 #[test]
333 fn as_named_none() {
334 let key: FieldKey = 0usize.into();
335 assert!(key.as_named().is_none());
336 }
337
338 #[test]
339 fn as_index_some() {
340 let key: FieldKey = 0usize.into();
341 assert!(key.as_index().is_some());
342 }
343
344 #[test]
345 fn as_index_none() {
346 let key: FieldKey = "id".into();
347 assert!(key.as_index().is_none());
348 }
349
350 #[test]
351 fn display_named() {
352 let key: FieldKey = "id".into();
353 assert_eq!(key.to_string(), "id");
354 }
355
356 #[test]
357 fn display_index() {
358 let key: FieldKey = 3usize.into();
359 assert_eq!(key.to_string(), "3");
360 }
361 }
362
363 mod predicates {
364 use super::*;
365
366 #[test]
367 fn named_struct() {
368 let fields = named_fields();
369 assert!(fields.is_named());
370 assert!(!fields.is_unnamed());
371 assert!(!fields.is_unit());
372 }
373
374 #[test]
375 fn tuple_struct() {
376 let fields = unnamed_fields();
377 assert!(!fields.is_named());
378 assert!(fields.is_unnamed());
379 assert!(!fields.is_unit());
380 }
381
382 #[test]
383 fn unit_struct() {
384 let fields = unit_fields();
385 assert!(!fields.is_named());
386 assert!(!fields.is_unnamed());
387 assert!(fields.is_unit());
388 }
389 }
390
391 mod conversions {
392 use super::*;
393
394 #[test]
395 fn as_named_some() {
396 let fields = named_fields();
397 assert!(fields.as_named().is_some());
398 }
399
400 #[test]
401 fn as_named_none() {
402 let fields = unnamed_fields();
403 assert!(fields.as_named().is_none());
404 }
405
406 #[test]
407 fn as_unnamed_some() {
408 let fields = unnamed_fields();
409 assert!(fields.as_unnamed().is_some());
410 }
411
412 #[test]
413 fn as_unnamed_none() {
414 let fields = named_fields();
415 assert!(fields.as_unnamed().is_none());
416 }
417 }
418
419 mod get {
420 use super::*;
421
422 #[test]
423 fn by_name() {
424 let fields = named_fields();
425 let key: FieldKey = "x".into();
426 assert!(fields.get(&key).is_some());
427 }
428
429 #[test]
430 fn by_name_missing() {
431 let fields = named_fields();
432 let key: FieldKey = "z".into();
433 assert!(fields.get(&key).is_none());
434 }
435
436 #[test]
437 fn by_index_named() {
438 let fields = named_fields();
439 let key: FieldKey = 0usize.into();
440 assert!(fields.get(&key).is_some());
441 }
442
443 #[test]
444 fn by_index_unnamed() {
445 let fields = unnamed_fields();
446 let key: FieldKey = 1usize.into();
447 assert!(fields.get(&key).is_some());
448 }
449
450 #[test]
451 fn by_index_out_of_bounds() {
452 let fields = named_fields();
453 let key: FieldKey = 10usize.into();
454 assert!(fields.get(&key).is_none());
455 }
456
457 #[test]
458 fn on_unit() {
459 let fields = unit_fields();
460 let key: FieldKey = 0usize.into();
461 assert!(fields.get(&key).is_none());
462 }
463 }
464
465 mod exists {
466 use super::*;
467
468 #[test]
469 fn existing_field() {
470 let fields = named_fields();
471 let key: FieldKey = "x".into();
472 assert!(fields.exists(&key));
473 }
474
475 #[test]
476 fn missing_field() {
477 let fields = named_fields();
478 let key: FieldKey = "z".into();
479 assert!(!fields.exists(&key));
480 }
481 }
482
483 mod keyed {
484 use super::*;
485
486 #[test]
487 fn named_yields_named_keys() {
488 let fields = named_fields();
489 let pairs: Vec<_> = fields.keyed().collect();
490 assert_eq!(pairs.len(), 2);
491 assert!(pairs[0].0.is_named());
492 assert_eq!(pairs[0].0.to_string(), "x");
493 assert_eq!(pairs[1].0.to_string(), "y");
494 }
495
496 #[test]
497 fn unnamed_yields_index_keys() {
498 let fields = unnamed_fields();
499 let pairs: Vec<_> = fields.keyed().collect();
500 assert_eq!(pairs.len(), 2);
501 assert!(pairs[0].0.is_index());
502 assert_eq!(pairs[0].0.to_string(), "0");
503 assert_eq!(pairs[1].0.to_string(), "1");
504 }
505
506 #[test]
507 fn unit_yields_empty() {
508 let fields = unit_fields();
509 let pairs: Vec<_> = fields.keyed().collect();
510 assert!(pairs.is_empty());
511 }
512 }
513}