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::{punctuated::Punctuated, spanned::Spanned, token::Comma, Data, Ident, Variant}; |
8 | |
9 | use crate::utils::{crate_ident_new, gen_enum_from_glib, parse_nested_meta_items, NestedMetaItem}; |
10 | |
11 | // Generate glib::gobject_ffi::GEnumValue structs mapping the enum such as: |
12 | // glib::gobject_ffi::GEnumValue { |
13 | // value: Animal::Goat as i32, |
14 | // value_name: "Goat\0" as *const _ as *const _, |
15 | // value_nick: "goat\0" as *const _ as *const _, |
16 | // }, |
17 | fn gen_enum_values( |
18 | enum_name: &Ident, |
19 | enum_variants: &Punctuated<Variant, Comma>, |
20 | ) -> (TokenStream, usize) { |
21 | let crate_ident = crate_ident_new(); |
22 | |
23 | // start at one as GEnumValue array is null-terminated |
24 | let mut n = 1; |
25 | let recurse = enum_variants.iter().map(|v| { |
26 | let name = &v.ident; |
27 | let mut value_name = name.to_string().to_upper_camel_case(); |
28 | let mut value_nick = name.to_string().to_kebab_case(); |
29 | |
30 | let mut name_attr = NestedMetaItem::<syn::LitStr>::new("name" ).value_required(); |
31 | let mut nick = NestedMetaItem::<syn::LitStr>::new("nick" ).value_required(); |
32 | |
33 | let found = |
34 | parse_nested_meta_items(&v.attrs, "enum_value" , &mut [&mut name_attr, &mut nick]); |
35 | if let Err(e) = found { |
36 | return e.to_compile_error(); |
37 | } |
38 | |
39 | value_name = name_attr.value.map(|s| s.value()).unwrap_or(value_name); |
40 | value_nick = nick.value.map(|s| s.value()).unwrap_or(value_nick); |
41 | |
42 | let value_name = format!(" {value_name}\0" ); |
43 | let value_nick = format!(" {value_nick}\0" ); |
44 | |
45 | n += 1; |
46 | quote_spanned! {v.span()=> |
47 | #crate_ident::gobject_ffi::GEnumValue { |
48 | value: #enum_name::#name as i32, |
49 | value_name: #value_name as *const _ as *const _, |
50 | value_nick: #value_nick as *const _ as *const _, |
51 | }, |
52 | } |
53 | }); |
54 | ( |
55 | quote! { |
56 | #(#recurse)* |
57 | }, |
58 | n, |
59 | ) |
60 | } |
61 | |
62 | pub fn impl_enum(input: &syn::DeriveInput) -> TokenStream { |
63 | let name = &input.ident; |
64 | |
65 | let enum_variants = match input.data { |
66 | Data::Enum(ref e) => &e.variants, |
67 | _ => abort_call_site!("#[derive(glib::Enum)] only supports enums" ), |
68 | }; |
69 | |
70 | let mut gtype_name = NestedMetaItem::<syn::LitStr>::new("name" ) |
71 | .required() |
72 | .value_required(); |
73 | let found = parse_nested_meta_items(&input.attrs, "enum_type" , &mut [&mut gtype_name]); |
74 | |
75 | match found { |
76 | Ok(None) => { |
77 | abort_call_site!("#[derive(glib::Enum)] requires #[enum_type(name = \"EnumTypeName \")]" ) |
78 | } |
79 | Err(e) => return e.to_compile_error(), |
80 | Ok(attr) => attr, |
81 | }; |
82 | let gtype_name = gtype_name.value.unwrap(); |
83 | let from_glib = gen_enum_from_glib(name, enum_variants); |
84 | let (enum_values, nb_enum_values) = gen_enum_values(name, enum_variants); |
85 | |
86 | let crate_ident = crate_ident_new(); |
87 | |
88 | quote! { |
89 | impl #crate_ident::translate::IntoGlib for #name { |
90 | type GlibType = i32; |
91 | |
92 | #[inline] |
93 | fn into_glib(self) -> i32 { |
94 | self as i32 |
95 | } |
96 | } |
97 | |
98 | impl #crate_ident::translate::TryFromGlib<i32> for #name { |
99 | type Error = i32; |
100 | |
101 | #[inline] |
102 | unsafe fn try_from_glib(value: i32) -> ::core::result::Result<Self, i32> { |
103 | let from_glib = || { |
104 | #from_glib |
105 | }; |
106 | |
107 | from_glib().ok_or(value) |
108 | } |
109 | } |
110 | |
111 | impl #crate_ident::translate::FromGlib<i32> for #name { |
112 | #[inline] |
113 | unsafe fn from_glib(value: i32) -> Self { |
114 | use #crate_ident::translate::TryFromGlib; |
115 | |
116 | Self::try_from_glib(value).unwrap() |
117 | } |
118 | } |
119 | |
120 | impl #crate_ident::value::ValueType for #name { |
121 | type Type = Self; |
122 | } |
123 | |
124 | unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name { |
125 | type Checker = #crate_ident::value::GenericValueTypeChecker<Self>; |
126 | |
127 | #[inline] |
128 | unsafe fn from_value(value: &'a #crate_ident::value::Value) -> Self { |
129 | #crate_ident::translate::from_glib(#crate_ident::gobject_ffi::g_value_get_enum( |
130 | #crate_ident::translate::ToGlibPtr::to_glib_none(value).0 |
131 | )) |
132 | } |
133 | } |
134 | |
135 | impl #crate_ident::value::ToValue for #name { |
136 | #[inline] |
137 | fn to_value(&self) -> #crate_ident::value::Value { |
138 | let mut value = #crate_ident::value::Value::for_value_type::<Self>(); |
139 | unsafe { |
140 | #crate_ident::gobject_ffi::g_value_set_enum( |
141 | #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, |
142 | #crate_ident::translate::IntoGlib::into_glib(*self) |
143 | ) |
144 | } |
145 | value |
146 | } |
147 | |
148 | #[inline] |
149 | fn value_type(&self) -> #crate_ident::Type { |
150 | <Self as #crate_ident::StaticType>::static_type() |
151 | } |
152 | } |
153 | |
154 | impl ::std::convert::From<#name> for #crate_ident::Value { |
155 | #[inline] |
156 | fn from(v: #name) -> Self { |
157 | #crate_ident::value::ToValue::to_value(&v) |
158 | } |
159 | } |
160 | |
161 | impl #crate_ident::StaticType for #name { |
162 | #[inline] |
163 | fn static_type() -> #crate_ident::Type { |
164 | static ONCE: ::std::sync::Once = ::std::sync::Once::new(); |
165 | static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID; |
166 | |
167 | ONCE.call_once(|| { |
168 | static mut VALUES: [#crate_ident::gobject_ffi::GEnumValue; #nb_enum_values] = [ |
169 | #enum_values |
170 | #crate_ident::gobject_ffi::GEnumValue { |
171 | value: 0, |
172 | value_name: ::std::ptr::null(), |
173 | value_nick: ::std::ptr::null(), |
174 | }, |
175 | ]; |
176 | |
177 | let name = ::std::ffi::CString::new(#gtype_name).expect("CString::new failed" ); |
178 | unsafe { |
179 | let type_ = #crate_ident::gobject_ffi::g_enum_register_static(name.as_ptr(), VALUES.as_ptr()); |
180 | let type_: #crate_ident::Type = #crate_ident::translate::from_glib(type_); |
181 | assert!(type_.is_valid()); |
182 | TYPE = type_; |
183 | } |
184 | }); |
185 | |
186 | unsafe { |
187 | TYPE |
188 | } |
189 | } |
190 | } |
191 | |
192 | impl #crate_ident::HasParamSpec for #name { |
193 | type ParamSpec = #crate_ident::ParamSpecEnum; |
194 | type SetValue = Self; |
195 | type BuilderFn = fn(&::core::primitive::str, Self) -> #crate_ident::ParamSpecEnumBuilder<Self>; |
196 | |
197 | fn param_spec_builder() -> Self::BuilderFn { |
198 | |name, default_value| Self::ParamSpec::builder_with_default(name, default_value) |
199 | } |
200 | } |
201 | } |
202 | } |
203 | |