1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use heck::{ToKebabCase, ToUpperCamelCase}; |
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 | Variant, Visibility, |
10 | }; |
11 | |
12 | use crate::utils::{crate_ident_new, parse_nested_meta_items, NestedMetaItem}; |
13 | |
14 | pub struct AttrInput { |
15 | pub enum_name: syn::LitStr, |
16 | } |
17 | struct FlagsDesc { |
18 | variant: Variant, |
19 | name: Option<String>, |
20 | nick: Option<String>, |
21 | skip: bool, |
22 | } |
23 | impl FlagsDesc { |
24 | fn from_attrs(variant: Variant, attrs: &[Attribute]) -> syn::Result<Self> { |
25 | let mut name: NestedMetaItem = NestedMetaItem::<syn::LitStr>::new(name:"name" ).value_required(); |
26 | let mut nick: NestedMetaItem = NestedMetaItem::<syn::LitStr>::new(name:"nick" ).value_required(); |
27 | let mut skip: NestedMetaItem = NestedMetaItem::<syn::LitBool>::new(name:"skip" ).value_optional(); |
28 | |
29 | parse_nested_meta_items(attrs, attr_name:"flags_value" , &mut [&mut name, &mut nick, &mut skip])?; |
30 | |
31 | Ok(Self { |
32 | variant, |
33 | name: name.value.map(|s: LitStr| s.value()), |
34 | nick: nick.value.map(|s: LitStr| s.value()), |
35 | skip: skip.found || skip.value.map(|b| b.value()).unwrap_or(default:false), |
36 | }) |
37 | } |
38 | } |
39 | |
40 | // Generate glib::gobject_ffi::GFlagsValue structs mapping the enum such as: |
41 | // glib::gobject_ffi::GFlagsValue { |
42 | // value: MyFlags::A.bits(), |
43 | // value_name: "The Name\0" as *const _ as *const _, |
44 | // value_nick: "nick\0" as *const _ as *const _, |
45 | // }, |
46 | fn gen_flags_values( |
47 | enum_name: &Ident, |
48 | enum_variants: &Punctuated<Variant, Comma>, |
49 | ) -> (TokenStream, usize) { |
50 | let crate_ident = crate_ident_new(); |
51 | |
52 | // start at one as GFlagsValue array is null-terminated |
53 | let mut n = 1; |
54 | let recurse = enum_variants |
55 | .iter() |
56 | .map(|v| FlagsDesc::from_attrs(v.clone(), &v.attrs).unwrap()) |
57 | .filter(|desc| !desc.skip) |
58 | .map(|desc| { |
59 | let v = desc.variant; |
60 | let name = &v.ident; |
61 | let mut value_name = name.to_string().to_upper_camel_case(); |
62 | let mut value_nick = name.to_string().to_kebab_case(); |
63 | |
64 | if let Some(n) = desc.name { |
65 | value_name = n; |
66 | } |
67 | if let Some(n) = desc.nick { |
68 | value_nick = n; |
69 | } |
70 | |
71 | let value_name = format!(" {value_name}\0" ); |
72 | let value_nick = format!(" {value_nick}\0" ); |
73 | |
74 | n += 1; |
75 | quote_spanned! {v.span()=> |
76 | #crate_ident::gobject_ffi::GFlagsValue { |
77 | value: #enum_name::#name.bits(), |
78 | value_name: #value_name as *const _ as *const _, |
79 | value_nick: #value_nick as *const _ as *const _, |
80 | }, |
81 | } |
82 | }); |
83 | ( |
84 | quote! { |
85 | #(#recurse)* |
86 | }, |
87 | n, |
88 | ) |
89 | } |
90 | |
91 | fn gen_bitflags( |
92 | enum_name: &Ident, |
93 | visibility: &Visibility, |
94 | enum_variants: &Punctuated<Variant, Comma>, |
95 | crate_ident: &TokenStream, |
96 | ) -> TokenStream { |
97 | let recurse: impl Iterator = enum_variants.iter().map(|v: &Variant| { |
98 | let name: &Ident = &v.ident; |
99 | let disc: &(Eq, Expr) = v.discriminant.as_ref().expect(msg:"missing discriminant" ); |
100 | let value: &Expr = &disc.1; |
101 | |
102 | quote_spanned! {v.span()=> |
103 | const #name = #value; |
104 | } |
105 | }); |
106 | |
107 | quote! { |
108 | #crate_ident::bitflags::bitflags! { |
109 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
110 | #visibility struct #enum_name: u32 { |
111 | #(#recurse)* |
112 | } |
113 | } |
114 | } |
115 | } |
116 | |
117 | pub fn impl_flags(attrs: AttrInput, input: &DeriveInput) -> TokenStream { |
118 | let gtype_name = attrs.enum_name.value(); |
119 | let name = &input.ident; |
120 | let visibility = &input.vis; |
121 | |
122 | let enum_variants = match input.data { |
123 | Data::Enum(ref e) => &e.variants, |
124 | _ => abort_call_site!("#[glib::flags] only supports enums" ), |
125 | }; |
126 | |
127 | let crate_ident = crate_ident_new(); |
128 | |
129 | let bitflags = gen_bitflags(name, visibility, enum_variants, &crate_ident); |
130 | let (flags_values, nb_flags_values) = gen_flags_values(name, enum_variants); |
131 | |
132 | quote! { |
133 | #bitflags |
134 | |
135 | impl #crate_ident::translate::IntoGlib for #name { |
136 | type GlibType = u32; |
137 | |
138 | #[inline] |
139 | fn into_glib(self) -> u32 { |
140 | self.bits() |
141 | } |
142 | } |
143 | |
144 | impl #crate_ident::translate::FromGlib<u32> for #name { |
145 | #[inline] |
146 | unsafe fn from_glib(value: u32) -> Self { |
147 | Self::from_bits_truncate(value) |
148 | } |
149 | } |
150 | |
151 | impl #crate_ident::value::ValueType for #name { |
152 | type Type = Self; |
153 | } |
154 | |
155 | unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name { |
156 | type Checker = #crate_ident::value::GenericValueTypeChecker<Self>; |
157 | |
158 | #[inline] |
159 | unsafe fn from_value(value: &'a #crate_ident::value::Value) -> Self { |
160 | #crate_ident::translate::from_glib(#crate_ident::gobject_ffi::g_value_get_flags( |
161 | #crate_ident::translate::ToGlibPtr::to_glib_none(value).0 |
162 | )) |
163 | } |
164 | } |
165 | |
166 | impl #crate_ident::value::ToValue for #name { |
167 | #[inline] |
168 | fn to_value(&self) -> #crate_ident::value::Value { |
169 | let mut value = #crate_ident::value::Value::for_value_type::<Self>(); |
170 | unsafe { |
171 | #crate_ident::gobject_ffi::g_value_set_flags( |
172 | #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, |
173 | #crate_ident::translate::IntoGlib::into_glib(*self) |
174 | ) |
175 | } |
176 | value |
177 | } |
178 | |
179 | #[inline] |
180 | fn value_type(&self) -> #crate_ident::Type { |
181 | <Self as #crate_ident::StaticType>::static_type() |
182 | } |
183 | } |
184 | |
185 | impl #crate_ident::HasParamSpec for #name { |
186 | type ParamSpec = #crate_ident::ParamSpecFlags; |
187 | type SetValue = Self; |
188 | type BuilderFn = fn(&::core::primitive::str) -> #crate_ident::ParamSpecFlagsBuilder<Self>; |
189 | |
190 | fn param_spec_builder() -> Self::BuilderFn { |
191 | |name| Self::ParamSpec::builder(name) |
192 | } |
193 | } |
194 | |
195 | impl ::std::convert::From<#name> for #crate_ident::Value { |
196 | #[inline] |
197 | fn from(v: #name) -> Self { |
198 | #crate_ident::value::ToValue::to_value(&v) |
199 | } |
200 | } |
201 | |
202 | impl #crate_ident::StaticType for #name { |
203 | #[inline] |
204 | fn static_type() -> #crate_ident::Type { |
205 | static ONCE: ::std::sync::Once = ::std::sync::Once::new(); |
206 | static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID; |
207 | |
208 | ONCE.call_once(|| { |
209 | static mut VALUES: [#crate_ident::gobject_ffi::GFlagsValue; #nb_flags_values] = [ |
210 | #flags_values |
211 | #crate_ident::gobject_ffi::GFlagsValue { |
212 | value: 0, |
213 | value_name: ::std::ptr::null(), |
214 | value_nick: ::std::ptr::null(), |
215 | }, |
216 | ]; |
217 | |
218 | let name = ::std::ffi::CString::new(#gtype_name).expect("CString::new failed" ); |
219 | unsafe { |
220 | let type_ = #crate_ident::gobject_ffi::g_flags_register_static(name.as_ptr(), VALUES.as_ptr()); |
221 | let type_: #crate_ident::Type = #crate_ident::translate::from_glib(type_); |
222 | assert!(type_.is_valid()); |
223 | TYPE = type_; |
224 | } |
225 | }); |
226 | |
227 | unsafe { |
228 | TYPE |
229 | } |
230 | } |
231 | } |
232 | } |
233 | } |
234 | |