1 // Take a look at the license at the top of the repository in the LICENSE file.
2 
3 use heck::{CamelCase, KebabCase};
4 use proc_macro2::TokenStream;
5 use proc_macro_error::abort_call_site;
6 use quote::{quote, quote_spanned};
7 use syn::{
8     punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DeriveInput, Ident,
9     LitStr, Variant, Visibility,
10 };
11 
12 use crate::utils::{
13     crate_ident_new, find_attribute_meta, find_nested_meta, parse_item_attributes, ItemAttribute,
14 };
15 
16 // Flag is not registered if it has the #[gflags(skip)] meta
attribute_has_skip(attrs: &[Attribute]) -> bool17 fn attribute_has_skip(attrs: &[Attribute]) -> bool {
18     let meta = find_attribute_meta(attrs, "gflags").unwrap();
19 
20     match meta {
21         None => false,
22         Some(meta) => find_nested_meta(&meta, "skip").is_some(),
23     }
24 }
25 
26 // Generate glib::gobject_ffi::GFlagsValue structs mapping the enum such as:
27 //     glib::gobject_ffi::GFlagsValue {
28 //         value: MyFlags::A.bits(),
29 //         value_name: "The Name\0" as *const _ as *const _,
30 //         value_nick: "nick\0" as *const _ as *const _,
31 //     },
gen_gflags_values( enum_name: &Ident, enum_variants: &Punctuated<Variant, Comma>, ) -> (TokenStream, usize)32 fn gen_gflags_values(
33     enum_name: &Ident,
34     enum_variants: &Punctuated<Variant, Comma>,
35 ) -> (TokenStream, usize) {
36     let crate_ident = crate_ident_new();
37 
38     // start at one as GFlagsValue array is null-terminated
39     let mut n = 1;
40     let recurse = enum_variants.iter().filter(|v| { !attribute_has_skip(&v.attrs) } ).map(|v| {
41         let name = &v.ident;
42         let mut value_name = name.to_string().to_camel_case();
43         let mut value_nick = name.to_string().to_kebab_case();
44 
45         let attrs = parse_item_attributes("gflags", &v.attrs);
46         let attrs = match attrs {
47             Ok(attrs) => attrs,
48             Err(e) => abort_call_site!(
49                 "{}: gflags enum supports only the following optional attributes: #[gflags(name = \"The Name\", nick = \"the-nick\")] or #[gflags(skip)]",
50                 e
51             ),
52         };
53 
54         attrs.into_iter().for_each(|attr|
55             match attr {
56                 ItemAttribute::Name(n) => value_name = n,
57                 ItemAttribute::Nick(n) => value_nick = n,
58             }
59         );
60 
61         let value_name = format!("{}\0", value_name);
62         let value_nick = format!("{}\0", value_nick);
63 
64         n += 1;
65         quote_spanned! {v.span()=>
66             #crate_ident::gobject_ffi::GFlagsValue {
67                 value: #enum_name::#name.bits(),
68                 value_name: #value_name as *const _ as *const _,
69                 value_nick: #value_nick as *const _ as *const _,
70             },
71         }
72     });
73     (
74         quote! {
75             #(#recurse)*
76         },
77         n,
78     )
79 }
80 
gen_bitflags( enum_name: &Ident, visibility: &Visibility, enum_variants: &Punctuated<Variant, Comma>, crate_ident: &TokenStream, ) -> TokenStream81 fn gen_bitflags(
82     enum_name: &Ident,
83     visibility: &Visibility,
84     enum_variants: &Punctuated<Variant, Comma>,
85     crate_ident: &TokenStream,
86 ) -> TokenStream {
87     let recurse = enum_variants.iter().map(|v| {
88         let name = &v.ident;
89         let disc = v.discriminant.as_ref().expect("missing discriminant");
90         let value = &disc.1;
91 
92         quote_spanned! {v.span()=>
93             const #name = #value;
94         }
95     });
96 
97     quote! {
98         #crate_ident::bitflags::bitflags! {
99             #visibility struct #enum_name: u32 {
100                 #(#recurse)*
101             }
102         }
103     }
104 }
105 
impl_gflags(input: &DeriveInput, gtype_name: &LitStr) -> TokenStream106 pub fn impl_gflags(input: &DeriveInput, gtype_name: &LitStr) -> TokenStream {
107     let visibility = &input.vis;
108     let name = &input.ident;
109     let crate_ident = crate_ident_new();
110 
111     let enum_variants = match input.data {
112         Data::Enum(ref e) => &e.variants,
113         _ => abort_call_site!("gflags only supports enums"),
114     };
115 
116     let bitflags = gen_bitflags(name, visibility, enum_variants, &crate_ident);
117     let (gflags_values, nb_gflags_values) = gen_gflags_values(name, enum_variants);
118 
119     quote! {
120         #bitflags
121 
122         impl #crate_ident::translate::IntoGlib for #name {
123             type GlibType = u32;
124 
125             fn into_glib(self) -> u32 {
126                 self.bits()
127             }
128         }
129 
130         impl #crate_ident::translate::FromGlib<u32> for #name {
131             unsafe fn from_glib(value: u32) -> Self {
132                 Self::from_bits_truncate(value)
133             }
134         }
135 
136         impl #crate_ident::value::ValueType for #name {
137             type Type = Self;
138         }
139 
140         unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name {
141             type Checker = #crate_ident::value::GenericValueTypeChecker<Self>;
142 
143             unsafe fn from_value(value: &'a #crate_ident::value::Value) -> Self {
144                 #crate_ident::translate::from_glib(#crate_ident::gobject_ffi::g_value_get_flags(
145                     #crate_ident::translate::ToGlibPtr::to_glib_none(value).0
146                 ))
147             }
148         }
149 
150         impl #crate_ident::value::ToValue for #name {
151             fn to_value(&self) -> #crate_ident::value::Value {
152                 let mut value = #crate_ident::value::Value::for_value_type::<Self>();
153                 unsafe {
154                     #crate_ident::gobject_ffi::g_value_set_flags(
155                         #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
156                         #crate_ident::translate::IntoGlib::into_glib(*self)
157                     )
158                 }
159                 value
160             }
161 
162             fn value_type(&self) -> #crate_ident::Type {
163                 <Self as #crate_ident::StaticType>::static_type()
164             }
165         }
166 
167         impl #crate_ident::StaticType for #name {
168             fn static_type() -> #crate_ident::Type {
169                 static ONCE: std::sync::Once = std::sync::Once::new();
170                 static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID;
171 
172                 ONCE.call_once(|| {
173                     static mut VALUES: [#crate_ident::gobject_ffi::GFlagsValue; #nb_gflags_values] = [
174                         #gflags_values
175                         #crate_ident::gobject_ffi::GFlagsValue {
176                             value: 0,
177                             value_name: std::ptr::null(),
178                             value_nick: std::ptr::null(),
179                         },
180                     ];
181 
182                     let name = std::ffi::CString::new(#gtype_name).expect("CString::new failed");
183                     unsafe {
184                         let type_ = #crate_ident::gobject_ffi::g_flags_register_static(name.as_ptr(), VALUES.as_ptr());
185                         TYPE = #crate_ident::translate::from_glib(type_);
186                     }
187                 });
188 
189                 unsafe {
190                     assert!(TYPE.is_valid());
191                     TYPE
192                 }
193             }
194         }
195     }
196 }
197