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 fn span(&self) -> proc_macro2::Span;
177}
178
179impl FieldsExt for syn::Fields {
180 fn is_named(&self) -> bool {
181 matches!(self, Self::Named(_))
182 }
183
184 fn is_unnamed(&self) -> bool {
185 matches!(self, Self::Unnamed(_))
186 }
187
188 fn is_unit(&self) -> bool {
189 matches!(self, Self::Unit)
190 }
191
192 fn as_named(&self) -> Option<&syn::FieldsNamed> {
193 match self {
194 Self::Named(f) => Some(f),
195 _ => None,
196 }
197 }
198
199 fn as_unnamed(&self) -> Option<&syn::FieldsUnnamed> {
200 match self {
201 Self::Unnamed(f) => Some(f),
202 _ => None,
203 }
204 }
205
206 fn exists(&self, key: &FieldKey) -> bool {
207 self.get(key).is_some()
208 }
209
210 fn keyed(&self) -> impl Iterator<Item = (FieldKey, &Field)> {
211 let pairs: Vec<_> = match self {
212 Self::Named(f) => f
213 .named
214 .iter()
215 .map(|field| {
216 let ident = field.ident.clone().unwrap();
217 (FieldKey::Named(ident), field)
218 })
219 .collect(),
220 Self::Unnamed(f) => f
221 .unnamed
222 .iter()
223 .enumerate()
224 .map(|(i, field)| (FieldKey::from(i), field))
225 .collect(),
226 Self::Unit => Vec::new(),
227 };
228
229 pairs.into_iter()
230 }
231
232 fn get(&self, key: &FieldKey) -> Option<&Field> {
233 match key {
234 FieldKey::Named(ident) => {
235 let named = self.as_named()?;
236 named.named.iter().find(|f| f.ident.as_ref() == Some(ident))
237 }
238 FieldKey::Index(index) => {
239 let mut iter: Box<dyn Iterator<Item = &Field>> = match self {
240 Self::Named(f) => Box::new(f.named.iter()),
241 Self::Unnamed(f) => Box::new(f.unnamed.iter()),
242 Self::Unit => return None,
243 };
244 iter.nth(index.index as usize)
245 }
246 }
247 }
248
249 fn span(&self) -> proc_macro2::Span {
250 use syn::spanned::Spanned;
251 Spanned::span(self)
252 }
253}
254
255macro_rules! delegate_fields_ext {
256 ($ty:ty, $field:ident) => {
257 impl FieldsExt for $ty {
258 fn is_named(&self) -> bool {
259 self.$field.is_named()
260 }
261
262 fn is_unnamed(&self) -> bool {
263 self.$field.is_unnamed()
264 }
265
266 fn is_unit(&self) -> bool {
267 self.$field.is_unit()
268 }
269
270 fn as_named(&self) -> Option<&syn::FieldsNamed> {
271 self.$field.as_named()
272 }
273
274 fn as_unnamed(&self) -> Option<&syn::FieldsUnnamed> {
275 self.$field.as_unnamed()
276 }
277
278 fn exists(&self, key: &FieldKey) -> bool {
279 self.$field.exists(key)
280 }
281
282 fn keyed(&self) -> impl Iterator<Item = (FieldKey, &Field)> {
283 self.$field.keyed()
284 }
285
286 fn get(&self, key: &FieldKey) -> Option<&Field> {
287 self.$field.get(key)
288 }
289
290 fn span(&self) -> proc_macro2::Span {
291 use syn::spanned::Spanned;
292 Spanned::span(&self.$field)
293 }
294 }
295 };
296}
297
298delegate_fields_ext!(syn::ItemStruct, fields);
299delegate_fields_ext!(syn::DataStruct, fields);
300delegate_fields_ext!(syn::Variant, fields);
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 fn named_fields() -> syn::Fields {
307 let item: syn::ItemStruct = syn::parse_str("struct Foo { x: i32, y: String }").unwrap();
308 item.fields
309 }
310
311 fn unnamed_fields() -> syn::Fields {
312 let item: syn::ItemStruct = syn::parse_str("struct Foo(i32, String);").unwrap();
313 item.fields
314 }
315
316 fn unit_fields() -> syn::Fields {
317 let item: syn::ItemStruct = syn::parse_str("struct Foo;").unwrap();
318 item.fields
319 }
320
321 mod field_key {
322 use super::*;
323
324 #[test]
325 fn from_str() {
326 let key: FieldKey = "id".into();
327 assert!(key.is_named());
328 assert!(!key.is_index());
329 }
330
331 #[test]
332 fn from_usize() {
333 let key: FieldKey = 0usize.into();
334 assert!(key.is_index());
335 assert!(!key.is_named());
336 }
337
338 #[test]
339 fn as_named_some() {
340 let key: FieldKey = "id".into();
341 assert!(key.as_named().is_some());
342 }
343
344 #[test]
345 fn as_named_none() {
346 let key: FieldKey = 0usize.into();
347 assert!(key.as_named().is_none());
348 }
349
350 #[test]
351 fn as_index_some() {
352 let key: FieldKey = 0usize.into();
353 assert!(key.as_index().is_some());
354 }
355
356 #[test]
357 fn as_index_none() {
358 let key: FieldKey = "id".into();
359 assert!(key.as_index().is_none());
360 }
361
362 #[test]
363 fn display_named() {
364 let key: FieldKey = "id".into();
365 assert_eq!(key.to_string(), "id");
366 }
367
368 #[test]
369 fn display_index() {
370 let key: FieldKey = 3usize.into();
371 assert_eq!(key.to_string(), "3");
372 }
373 }
374
375 mod predicates {
376 use super::*;
377
378 #[test]
379 fn named_struct() {
380 let fields = named_fields();
381 assert!(fields.is_named());
382 assert!(!fields.is_unnamed());
383 assert!(!fields.is_unit());
384 }
385
386 #[test]
387 fn tuple_struct() {
388 let fields = unnamed_fields();
389 assert!(!fields.is_named());
390 assert!(fields.is_unnamed());
391 assert!(!fields.is_unit());
392 }
393
394 #[test]
395 fn unit_struct() {
396 let fields = unit_fields();
397 assert!(!fields.is_named());
398 assert!(!fields.is_unnamed());
399 assert!(fields.is_unit());
400 }
401 }
402
403 mod conversions {
404 use super::*;
405
406 #[test]
407 fn as_named_some() {
408 let fields = named_fields();
409 assert!(fields.as_named().is_some());
410 }
411
412 #[test]
413 fn as_named_none() {
414 let fields = unnamed_fields();
415 assert!(fields.as_named().is_none());
416 }
417
418 #[test]
419 fn as_unnamed_some() {
420 let fields = unnamed_fields();
421 assert!(fields.as_unnamed().is_some());
422 }
423
424 #[test]
425 fn as_unnamed_none() {
426 let fields = named_fields();
427 assert!(fields.as_unnamed().is_none());
428 }
429 }
430
431 mod get {
432 use super::*;
433
434 #[test]
435 fn by_name() {
436 let fields = named_fields();
437 let key: FieldKey = "x".into();
438 assert!(fields.get(&key).is_some());
439 }
440
441 #[test]
442 fn by_name_missing() {
443 let fields = named_fields();
444 let key: FieldKey = "z".into();
445 assert!(fields.get(&key).is_none());
446 }
447
448 #[test]
449 fn by_index_named() {
450 let fields = named_fields();
451 let key: FieldKey = 0usize.into();
452 assert!(fields.get(&key).is_some());
453 }
454
455 #[test]
456 fn by_index_unnamed() {
457 let fields = unnamed_fields();
458 let key: FieldKey = 1usize.into();
459 assert!(fields.get(&key).is_some());
460 }
461
462 #[test]
463 fn by_index_out_of_bounds() {
464 let fields = named_fields();
465 let key: FieldKey = 10usize.into();
466 assert!(fields.get(&key).is_none());
467 }
468
469 #[test]
470 fn on_unit() {
471 let fields = unit_fields();
472 let key: FieldKey = 0usize.into();
473 assert!(fields.get(&key).is_none());
474 }
475 }
476
477 mod exists {
478 use super::*;
479
480 #[test]
481 fn existing_field() {
482 let fields = named_fields();
483 let key: FieldKey = "x".into();
484 assert!(fields.exists(&key));
485 }
486
487 #[test]
488 fn missing_field() {
489 let fields = named_fields();
490 let key: FieldKey = "z".into();
491 assert!(!fields.exists(&key));
492 }
493 }
494
495 mod keyed {
496 use super::*;
497
498 #[test]
499 fn named_yields_named_keys() {
500 let fields = named_fields();
501 let pairs: Vec<_> = fields.keyed().collect();
502 assert_eq!(pairs.len(), 2);
503 assert!(pairs[0].0.is_named());
504 assert_eq!(pairs[0].0.to_string(), "x");
505 assert_eq!(pairs[1].0.to_string(), "y");
506 }
507
508 #[test]
509 fn unnamed_yields_index_keys() {
510 let fields = unnamed_fields();
511 let pairs: Vec<_> = fields.keyed().collect();
512 assert_eq!(pairs.len(), 2);
513 assert!(pairs[0].0.is_index());
514 assert_eq!(pairs[0].0.to_string(), "0");
515 assert_eq!(pairs[1].0.to_string(), "1");
516 }
517
518 #[test]
519 fn unit_yields_empty() {
520 let fields = unit_fields();
521 let pairs: Vec<_> = fields.keyed().collect();
522 assert!(pairs.is_empty());
523 }
524 }
525}