1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use heck::{ToKebabCase, ToUpperCamelCase};
4use proc_macro2::TokenStream;
5use proc_macro_error::abort_call_site;
6use quote::{quote, quote_spanned};
7use syn::{
8 punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DeriveInput, Ident,
9 Variant, Visibility,
10};
11
12use crate::utils::{crate_ident_new, parse_nested_meta_items, NestedMetaItem};
13
14pub struct AttrInput {
15 pub enum_name: syn::LitStr,
16}
17struct FlagsDesc {
18 variant: Variant,
19 name: Option<String>,
20 nick: Option<String>,
21 skip: bool,
22}
23impl 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// },
46fn 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
91fn 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
117pub 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