glib_macros/
variant_derive.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use heck::ToKebabCase;
4use proc_macro2::TokenStream;
5use quote::{format_ident, quote};
6use syn::{Data, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Type};
7
8use crate::utils::crate_ident_new;
9
10pub fn impl_variant(input: DeriveInput) -> syn::Result<TokenStream> {
11    match input.data {
12        Data::Struct(data_struct) => Ok(derive_variant_for_struct(
13            input.ident,
14            input.generics,
15            data_struct,
16        )),
17        Data::Enum(data_enum) => {
18            let mode = get_enum_mode(&input.attrs)?;
19            let has_data = data_enum
20                .variants
21                .iter()
22                .any(|v| !matches!(v.fields, syn::Fields::Unit));
23            if has_data {
24                derive_variant_for_enum(input.ident, input.generics, data_enum, mode)
25            } else {
26                Ok(derive_variant_for_c_enum(
27                    input.ident,
28                    input.generics,
29                    data_enum,
30                    mode,
31                ))
32            }
33        }
34        Data::Union(..) => Err(syn::Error::new_spanned(
35            input,
36            "#[derive(glib::Variant)] is not available for unions.",
37        )),
38    }
39}
40
41fn derive_variant_for_struct(
42    ident: Ident,
43    generics: Generics,
44    data_struct: syn::DataStruct,
45) -> TokenStream {
46    let glib = crate_ident_new();
47    let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
48    let (static_variant_type, to_variant, from_variant) = match data_struct.fields {
49        Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
50            let types = unnamed
51                .into_pairs()
52                .map(|pair| pair.into_value())
53                .map(|field| field.ty)
54                .collect::<Vec<_>>();
55
56            let idents = (0..types.len()).map(syn::Index::from).collect::<Vec<_>>();
57            let idents_len = idents.len();
58
59            let static_variant_type = quote! {
60                impl #impl_generics #glib::variant::StaticVariantType for #ident #type_generics #where_clause {
61                    #[inline]
62                    fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> {
63                        static TYP: ::std::sync::OnceLock<#glib::VariantType> = ::std::sync::OnceLock::new();
64                        ::std::borrow::Cow::Borrowed(TYP.get_or_init(|| {
65
66                            let mut builder = #glib::GStringBuilder::new("(");
67
68                            #(
69                                {
70                                    let typ = <#types as #glib::variant::StaticVariantType>::static_variant_type();
71                                    builder.append(typ.as_str());
72                                }
73                            )*
74                            builder.append_c(')');
75
76                            #glib::VariantType::from_string(builder.into_string()).unwrap()
77                        }))
78                    }
79                }
80            };
81
82            let to_variant = quote! {
83                impl #impl_generics #glib::variant::ToVariant for #ident #type_generics #where_clause {
84                    fn to_variant(&self) -> #glib::Variant {
85                        #glib::Variant::tuple_from_iter(::std::array::IntoIter::<#glib::Variant, #idents_len>::new([
86                            #(
87                                #glib::variant::ToVariant::to_variant(&self.#idents)
88                            ),*
89                        ]))
90                    }
91                }
92
93                impl #impl_generics ::std::convert::From<#ident #type_generics> for #glib::Variant #where_clause {
94                    fn from(v: #ident #type_generics) -> #glib::Variant {
95                        #glib::Variant::tuple_from_iter(::std::array::IntoIter::<#glib::Variant, #idents_len>::new([
96                            #(
97                                <#glib::Variant as ::std::convert::From<_>>::from(v.#idents)
98                            ),*
99                        ]))
100                    }
101                }
102            };
103
104            let from_variant = quote! {
105                impl #impl_generics #glib::variant::FromVariant for #ident #type_generics #where_clause {
106                    fn from_variant(variant: &#glib::Variant) -> ::core::option::Option<Self> {
107                        if !variant.is_container() {
108                            return ::core::option::Option::None;
109                        }
110                        ::core::option::Option::Some(Self(
111                            #(
112                                match variant.try_child_get::<#types>(#idents) {
113                                    ::core::result::Result::Ok(::core::option::Option::Some(field)) => field,
114                                    _ => return ::core::option::Option::None,
115                                }
116                            ),*
117                        ))
118                    }
119                }
120            };
121
122            (static_variant_type, to_variant, from_variant)
123        }
124        Fields::Named(FieldsNamed { named, .. }) => {
125            let fields: Vec<(Ident, Type)> = named
126                .into_pairs()
127                .map(|pair| pair.into_value())
128                .map(|field| (field.ident.expect("Field ident is specified"), field.ty))
129                .collect();
130
131            let idents: Vec<_> = fields.iter().map(|(ident, _ty)| ident).collect();
132            let types: Vec<_> = fields.iter().map(|(_ident, ty)| ty).collect();
133            let counts = (0..types.len()).map(syn::Index::from).collect::<Vec<_>>();
134
135            let static_variant_type = quote! {
136                impl #impl_generics #glib::variant::StaticVariantType for #ident #type_generics #where_clause {
137                    #[inline]
138                    fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> {
139                        static TYP: ::std::sync::OnceLock<#glib::VariantType> = ::std::sync::OnceLock::new();
140                        ::std::borrow::Cow::Borrowed(TYP.get_or_init(|| unsafe {
141                            let ptr = #glib::ffi::g_string_sized_new(16);
142                            #glib::ffi::g_string_append_c(ptr, b'(' as _);
143
144                            #(
145                                {
146                                    let typ = <#types as #glib::variant::StaticVariantType>::static_variant_type();
147                                    #glib::ffi::g_string_append_len(
148                                        ptr,
149                                        typ.as_str().as_ptr() as *const _,
150                                        typ.as_str().len() as isize,
151                                    );
152                                }
153                            )*
154                            #glib::ffi::g_string_append_c(ptr, b')' as _);
155
156                            #glib::translate::from_glib_full(
157                                #glib::ffi::g_string_free(ptr, #glib::ffi::GFALSE) as *mut #glib::ffi::GVariantType
158                            )
159                        }))
160                    }
161                }
162            };
163
164            let to_variant = quote! {
165                impl #impl_generics #glib::variant::ToVariant for #ident #type_generics #where_clause {
166                    fn to_variant(&self) -> #glib::Variant {
167                        #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([
168                            #(
169                                #glib::variant::ToVariant::to_variant(&self.#idents)
170                            ),*
171                        ]))
172                    }
173                }
174
175                impl #impl_generics ::std::convert::From<#ident #type_generics> for #glib::Variant #where_clause {
176                    fn from(v: #ident #type_generics) -> #glib::Variant {
177                        #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([
178                            #(
179                                <#glib::Variant as ::std::convert::From<_>>::from(v.#idents)
180                            ),*
181                        ]))
182                    }
183                }
184            };
185
186            let from_variant = quote! {
187                impl #impl_generics #glib::variant::FromVariant for #ident #type_generics #where_clause {
188                    fn from_variant(variant: &#glib::Variant) -> ::core::option::Option<Self> {
189                        if !variant.is_container() {
190                            return ::core::option::Option::None;
191                        }
192                        ::core::option::Option::Some(Self {
193                            #(
194                                #idents: match variant.try_child_get::<#types>(#counts) {
195                                    ::core::result::Result::Ok(::core::option::Option::Some(field)) => field,
196                                    _ => return ::core::option::Option::None,
197                                }
198                            ),*
199                        })
200                    }
201                }
202            };
203
204            (static_variant_type, to_variant, from_variant)
205        }
206        Fields::Unit => {
207            let static_variant_type = quote! {
208                impl #impl_generics #glib::variant::StaticVariantType for #ident #type_generics #where_clause {
209                    #[inline]
210                    fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> {
211                        ::std::borrow::Cow::Borrowed(#glib::VariantTy::UNIT)
212                    }
213                }
214            };
215
216            let to_variant = quote! {
217                impl #impl_generics #glib::variant::ToVariant for #ident #type_generics #where_clause {
218                    #[inline]
219                    fn to_variant(&self) -> #glib::Variant {
220                        #glib::variant::ToVariant::to_variant(&())
221                    }
222                }
223
224                impl #impl_generics ::std::convert::From<#ident #type_generics> for #glib::Variant #where_clause {
225                    #[inline]
226                    fn from(v: #ident #type_generics) -> #glib::Variant {
227                        #glib::variant::ToVariant::to_variant(&())
228                    }
229                }
230            };
231
232            let from_variant = quote! {
233                impl #impl_generics #glib::variant::FromVariant for #ident #type_generics #where_clause {
234                    fn from_variant(variant: &#glib::Variant) -> ::core::option::Option<Self> {
235                        ::core::option::Option::Some(Self)
236                    }
237                }
238            };
239
240            (static_variant_type, to_variant, from_variant)
241        }
242    };
243
244    quote! {
245        #static_variant_type
246
247        #to_variant
248
249        #from_variant
250    }
251}
252
253enum EnumMode {
254    String,
255    Repr(Ident),
256    Enum { repr: bool },
257    Flags { repr: bool },
258}
259
260impl EnumMode {
261    fn tag_type(&self) -> char {
262        match self {
263            EnumMode::String => 's',
264            EnumMode::Repr(repr) => match repr.to_string().as_str() {
265                "i8" | "i16" => 'n',
266                "i32" => 'i',
267                "i64" => 'x',
268                "u8" => 'y',
269                "u16" => 'q',
270                "u32" => 'u',
271                "u64" => 't',
272                _ => unimplemented!(),
273            },
274            EnumMode::Enum { repr } => {
275                if *repr {
276                    'i'
277                } else {
278                    's'
279                }
280            }
281            EnumMode::Flags { repr } => {
282                if *repr {
283                    'u'
284                } else {
285                    's'
286                }
287            }
288        }
289    }
290}
291
292fn derive_variant_for_enum(
293    ident: Ident,
294    generics: Generics,
295    data_enum: syn::DataEnum,
296    mode: EnumMode,
297) -> syn::Result<TokenStream> {
298    let glib = crate_ident_new();
299    let static_variant_type = format!("({}v)", mode.tag_type());
300    let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
301
302    let to = data_enum.variants.iter().enumerate().map(|(index, v)| {
303        let ident = &v.ident;
304        let tag = match &mode {
305            EnumMode::String => {
306                let nick = ToKebabCase::to_kebab_case(ident.to_string().as_str());
307                quote! { #nick }
308            },
309            EnumMode::Repr(repr) => quote! { #index as #repr },
310            _ => unimplemented!(),
311        };
312        if !matches!(v.fields, syn::Fields::Unit) {
313            match &mode {
314                EnumMode::Enum { .. } =>
315                    return Err(syn::Error::new_spanned(v, "#[variant_enum(enum) only allowed with C-style enums using #[derive(glib::Enum)]")),
316                EnumMode::Flags { .. } =>
317                    return Err(syn::Error::new_spanned(v, "#[variant_enum(flags) only allowed with bitflags using #[glib::flags]")),
318                _ => (),
319            }
320        }
321        Ok(match &v.fields {
322            syn::Fields::Named(FieldsNamed { named, .. }) => {
323                let field_names = named.iter().map(|f| f.ident.as_ref().unwrap());
324                let field_names2 = field_names.clone();
325                quote! {
326                    Self::#ident { #(#field_names),* } => #glib::variant::ToVariant::to_variant(&(
327                        #tag,
328                        #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([
329                            #(#glib::variant::ToVariant::to_variant(&#field_names2)),*
330                        ]))
331                    ))
332                }
333            },
334            syn::Fields::Unnamed(FieldsUnnamed  { unnamed, .. }) => {
335                let field_names = unnamed.iter().enumerate().map(|(i, _)| {
336                    format_ident!("field{}", i)
337                });
338                let field_names2 = field_names.clone();
339                quote! {
340                    Self::#ident(#(#field_names),*) => #glib::variant::ToVariant::to_variant(&(
341                        #tag,
342                        #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([
343                            #(#glib::variant::ToVariant::to_variant(&#field_names2)),*
344                        ]))
345                    ))
346                }
347            },
348            syn::Fields::Unit => {
349                quote! {
350                    Self::#ident => #glib::variant::ToVariant::to_variant(&(
351                        #tag,
352                        #glib::variant::ToVariant::to_variant(&())
353                    ))
354                }
355            },
356        })
357    }).collect::<Result<Vec<_>, _>>()?;
358    let into = data_enum.variants.iter().enumerate().map(|(index, v)| {
359        let field_ident = &v.ident;
360        let tag = match &mode {
361            EnumMode::String => {
362                let nick = ToKebabCase::to_kebab_case(field_ident.to_string().as_str());
363                quote! { #nick }
364            }
365            EnumMode::Repr(repr) => quote! { #index as #repr },
366            _ => unimplemented!(),
367        };
368        match &v.fields {
369            syn::Fields::Named(FieldsNamed { named, .. }) => {
370                let field_names = named.iter().map(|f| f.ident.as_ref().unwrap());
371                let field_names2 = field_names.clone();
372                quote! {
373                    #ident::#field_ident { #(#field_names),* } => #glib::variant::ToVariant::to_variant(&(
374                        #tag,
375                        #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([
376                            #(<#glib::Variant as ::std::convert::From<_>>::from(#field_names2)),*
377                        ]))
378                    ))
379                }
380            }
381            syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
382                let field_names = unnamed
383                    .iter()
384                    .enumerate()
385                    .map(|(i, _)| format_ident!("field{}", i));
386                let field_names2 = field_names.clone();
387                quote! {
388                    #ident::#field_ident(#(#field_names),*) => #glib::variant::ToVariant::to_variant(&(
389                        #tag,
390                        #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([
391                            #(<#glib::Variant as ::std::convert::From<_>>::from(#field_names2)),*
392                        ]))
393                    ))
394                }
395            }
396            syn::Fields::Unit => {
397                quote! {
398                    #ident::#field_ident => #glib::variant::ToVariant::to_variant(&(
399                        #tag,
400                        #glib::variant::ToVariant::to_variant(&())
401                    ))
402                }
403            }
404        }
405    });
406    let from = data_enum.variants.iter().enumerate().map(|(index, v)| {
407        let ident = &v.ident;
408        let tag = match &mode {
409            EnumMode::String => {
410                let nick = ToKebabCase::to_kebab_case(ident.to_string().as_str());
411                quote! { #nick }
412            }
413            EnumMode::Repr(_) => quote! { #index },
414            _ => unimplemented!(),
415        };
416        match &v.fields {
417            syn::Fields::Named(FieldsNamed { named, .. }) => {
418                let fields = named.iter().enumerate().map(|(index, f)| {
419                    let name = f.ident.as_ref().unwrap();
420                    let repr = &f.ty;
421                    quote! {
422                        #name: <#repr as #glib::variant::FromVariant>::from_variant(
423                            &#glib::Variant::try_child_value(&value, #index)?
424                        )?
425                    }
426                });
427                quote! { #tag => ::core::option::Option::Some(Self::#ident { #(#fields),* }), }
428            }
429            syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
430                let indices = 0..unnamed.iter().count();
431                let repr = unnamed.iter().map(|f| &f.ty);
432                quote! {
433                    #tag => ::core::option::Option::Some(Self::#ident(
434                        #(
435                            <#repr as #glib::variant::FromVariant>::from_variant(
436                                &#glib::Variant::try_child_value(&value, #indices)?
437                            )?
438                        ),*
439                    )),
440                }
441            }
442            syn::Fields::Unit => {
443                quote! { #tag => ::core::option::Option::Some(Self::#ident), }
444            }
445        }
446    });
447
448    let (repr, tag_match) = match &mode {
449        EnumMode::String => (quote! { String }, quote! { tag.as_str() }),
450        EnumMode::Repr(repr) => (quote! { #repr }, quote! { tag as usize }),
451        _ => unimplemented!(),
452    };
453
454    Ok(quote! {
455        impl #impl_generics #glib::variant::StaticVariantType for #ident #type_generics #where_clause {
456            #[inline]
457            fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> {
458                ::std::borrow::Cow::Borrowed(
459                    unsafe {
460                        #glib::VariantTy::from_str_unchecked(#static_variant_type)
461                    }
462                )
463            }
464        }
465
466        impl #impl_generics #glib::variant::ToVariant for #ident #type_generics #where_clause {
467            fn to_variant(&self) -> #glib::Variant {
468                match self {
469                    #(#to),*
470                }
471            }
472        }
473
474        impl #impl_generics ::std::convert::From<#ident #type_generics> for #glib::Variant #where_clause {
475            fn from(v: #ident #type_generics) -> #glib::Variant {
476                match v {
477                    #(#into),*
478                }
479            }
480        }
481
482        impl #impl_generics #glib::variant::FromVariant for #ident #type_generics #where_clause {
483            fn from_variant(variant: &#glib::Variant) -> ::core::option::Option<Self> {
484                let (tag, value) = <(#repr, #glib::Variant) as #glib::variant::FromVariant>::from_variant(&variant)?;
485                if !#glib::VariantTy::is_tuple(#glib::Variant::type_(&value)) {
486                    return ::core::option::Option::None;
487                }
488                match #tag_match {
489                    #(#from)*
490                    _ => ::core::option::Option::None
491                }
492            }
493        }
494    })
495}
496
497fn derive_variant_for_c_enum(
498    ident: Ident,
499    generics: Generics,
500    data_enum: syn::DataEnum,
501    mode: EnumMode,
502) -> TokenStream {
503    let glib = crate_ident_new();
504    let static_variant_type = mode.tag_type().to_string();
505    let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
506
507    let (to_variant, from_variant) = match mode {
508        EnumMode::String => {
509            let idents = data_enum.variants.iter().map(|v| &v.ident);
510            let nicks = data_enum.variants.iter().map(|v| {
511                let nick = ToKebabCase::to_kebab_case(v.ident.to_string().as_str());
512                quote! { #nick }
513            });
514            let idents2 = idents.clone();
515            let nicks2 = nicks.clone();
516            (
517                quote! {
518                    #glib::variant::ToVariant::to_variant(match self {
519                        #(Self::#idents => #nicks),*
520                    })
521                },
522                quote! {
523                    let tag = #glib::Variant::str(&variant)?;
524                    match tag {
525                        #(#nicks2 => ::core::option::Option::Some(Self::#idents2),)*
526                        _ => ::core::option::Option::None
527                    }
528                },
529            )
530        }
531        EnumMode::Repr(repr) => {
532            let idents = data_enum.variants.iter().map(|v| &v.ident);
533            (
534                quote! {
535                    #glib::variant::ToVariant::to_variant(&(*self as #repr))
536                },
537                quote! {
538                    let value = <#repr as #glib::variant::FromVariant>::from_variant(&variant)?;
539                    #(if value == Self::#idents as #repr {
540                        return ::core::option::Option::Some(Self::#idents);
541                    })*
542                    ::core::option::Option::None
543                },
544            )
545        }
546        EnumMode::Enum { repr: true } => (
547            quote! {
548                #glib::variant::ToVariant::to_variant(&(*self as i32))
549            },
550            quote! {
551                let value = <i32 as #glib::variant::FromVariant>::from_variant(&variant)?;
552                unsafe { #glib::translate::try_from_glib(value) }.ok()
553            },
554        ),
555        EnumMode::Enum { repr: false } => (
556            quote! {
557                let enum_class = #glib::EnumClass::new::<Self>();
558                let value = <Self as #glib::translate::IntoGlib>::into_glib(*self);
559                let value = #glib::EnumClass::value(&enum_class, value);
560                let value = ::core::option::Option::unwrap(value);
561                let nick = #glib::EnumValue::nick(&value);
562                #glib::variant::ToVariant::to_variant(nick)
563            },
564            quote! {
565                let enum_class = #glib::EnumClass::new::<Self>();
566                let tag = #glib::Variant::str(&variant)?;
567                let value = #glib::EnumClass::value_by_nick(&enum_class, tag)?;
568                let value = #glib::EnumValue::value(&value);
569                unsafe { #glib::translate::try_from_glib(value) }.ok()
570            },
571        ),
572        EnumMode::Flags { repr: true } => (
573            quote! {
574                #glib::variant::ToVariant::to_variant(&self.bits())
575            },
576            quote! {
577                let value = <u32 as #glib::variant::FromVariant>::from_variant(&variant)?;
578                Self::from_bits(value)
579            },
580        ),
581        EnumMode::Flags { repr: false } => (
582            quote! {
583                let flags_class = #glib::FlagsClass::new::<Self>();
584                let value = <Self as #glib::translate::IntoGlib>::into_glib(*self);
585                let s = #glib::FlagsClass::to_nick_string(&flags_class, value);
586                #glib::variant::ToVariant::to_variant(&s)
587            },
588            quote! {
589                let flags_class = #glib::FlagsClass::new::<Self>();
590                let s = #glib::Variant::str(&variant)?;
591                let value = #glib::FlagsClass::from_nick_string(&flags_class, s).ok()?;
592                ::core::option::Option::Some(unsafe { #glib::translate::from_glib(value) })
593            },
594        ),
595    };
596
597    quote! {
598        impl #impl_generics #glib::variant::StaticVariantType for #ident #type_generics #where_clause {
599            #[inline]
600            fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> {
601                ::std::borrow::Cow::Borrowed(
602                    unsafe {
603                        #glib::VariantTy::from_str_unchecked(#static_variant_type)
604                    }
605                )
606            }
607        }
608
609        impl #impl_generics #glib::variant::ToVariant for #ident #type_generics #where_clause {
610            fn to_variant(&self) -> #glib::Variant {
611                #to_variant
612            }
613        }
614
615        impl #impl_generics ::std::convert::From<#ident #type_generics> for #glib::Variant #where_clause {
616            #[inline]
617            fn from(v: #ident #type_generics) -> #glib::Variant {
618                <#ident #type_generics as #glib::variant::ToVariant>::to_variant(&v)
619            }
620        }
621
622        impl #impl_generics #glib::variant::FromVariant for #ident #type_generics #where_clause {
623            fn from_variant(variant: &#glib::Variant) -> ::core::option::Option<Self> {
624                #from_variant
625            }
626        }
627    }
628}
629
630fn get_enum_mode(attrs: &[syn::Attribute]) -> syn::Result<EnumMode> {
631    let attr = attrs.iter().find(|a| a.path().is_ident("variant_enum"));
632
633    let Some(attr) = attr else {
634        return Ok(EnumMode::String);
635    };
636
637    let mut repr_attr = None;
638    let mut mode = EnumMode::String;
639    attr.parse_nested_meta(|meta| {
640        match meta.path.get_ident().map(|id| id.to_string()).as_deref() {
641            Some("repr") => {
642                repr_attr = Some(meta.path);
643                Ok(())
644            }
645            Some("enum") => {
646                mode = EnumMode::Enum { repr: false };
647                Ok(())
648            }
649            Some("flags") => {
650                mode = EnumMode::Flags { repr: false };
651                Ok(())
652            }
653            _ => Err(syn::Error::new_spanned(
654                meta.path,
655                "unknown type in #[variant_enum] attribute",
656            )),
657        }
658    })?;
659    Ok(match mode {
660        EnumMode::String if repr_attr.is_some() => {
661            let repr_attr = repr_attr.unwrap();
662            let repr = get_repr(attrs).ok_or_else(|| {
663                syn::Error::new_spanned(
664                    repr_attr,
665                    "Must have #[repr] attribute with one of i8, i16, i32, i64, u8, u16, u32, u64",
666                )
667            })?;
668            EnumMode::Repr(repr)
669        }
670        EnumMode::Enum { .. } => EnumMode::Enum {
671            repr: repr_attr.is_some(),
672        },
673        EnumMode::Flags { .. } => EnumMode::Flags {
674            repr: repr_attr.is_some(),
675        },
676        e => e,
677    })
678}
679
680fn get_repr(attrs: &[syn::Attribute]) -> Option<Ident> {
681    let attr = attrs.iter().find(|a| a.path().is_ident("repr"))?;
682    let mut repr_ty = None;
683    attr.parse_nested_meta(|meta| {
684        repr_ty = Some(meta.path.get_ident().unwrap().clone());
685        Ok(())
686    })
687    .unwrap();
688    match repr_ty.as_ref()?.to_string().as_str() {
689        "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" => Some(repr_ty?),
690        _ => None,
691    }
692}