1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use proc_macro2::{Ident, TokenStream}; |
4 | use proc_macro_error::abort_call_site; |
5 | use quote::quote; |
6 | |
7 | use crate::utils::{crate_ident_new, parse_nested_meta_items, NestedMetaItem}; |
8 | |
9 | fn gen_impl_to_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream { |
10 | let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident); |
11 | |
12 | quote! { |
13 | impl #crate_ident::value::ToValueOptional for #name { |
14 | #[inline] |
15 | fn to_value_optional(s: ::core::option::Option<&Self>) -> #crate_ident::Value { |
16 | let mut value = #crate_ident::Value::for_value_type::<Self>(); |
17 | unsafe { |
18 | let ptr = match s { |
19 | ::core::option::Option::Some(s) => #refcounted_type_prefix::into_raw(s.0.clone()), |
20 | ::core::option::Option::None => ::std::ptr::null(), |
21 | }; |
22 | |
23 | #crate_ident::gobject_ffi::g_value_take_boxed( |
24 | #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, |
25 | ptr as *mut _ |
26 | ); |
27 | } |
28 | |
29 | value |
30 | } |
31 | } |
32 | |
33 | impl #crate_ident::value::ValueTypeOptional for #name { } |
34 | } |
35 | } |
36 | |
37 | fn gen_impl_from_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream { |
38 | let refcounted_type_prefix: TokenStream = refcounted_type_prefix(name, crate_ident); |
39 | |
40 | quote! { |
41 | unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name { |
42 | type Checker = #crate_ident::value::GenericValueTypeOrNoneChecker<Self>; |
43 | |
44 | #[inline] |
45 | unsafe fn from_value(value: &'a #crate_ident::Value) -> Self { |
46 | let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0); |
47 | debug_assert!(!ptr.is_null()); |
48 | #name(#refcounted_type_prefix::from_raw(ptr as *mut _)) |
49 | } |
50 | } |
51 | } |
52 | } |
53 | |
54 | fn gen_impl_from_value(name: &Ident, crate_ident: &TokenStream) -> TokenStream { |
55 | let refcounted_type_prefix: TokenStream = refcounted_type_prefix(name, crate_ident); |
56 | |
57 | quote! { |
58 | unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name { |
59 | type Checker = #crate_ident::value::GenericValueTypeChecker<Self>; |
60 | |
61 | #[inline] |
62 | unsafe fn from_value(value: &'a #crate_ident::Value) -> Self { |
63 | let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0); |
64 | debug_assert!(!ptr.is_null()); |
65 | #name(#refcounted_type_prefix::from_raw(ptr as *mut _)) |
66 | } |
67 | } |
68 | } |
69 | } |
70 | |
71 | fn refcounted_type(input: &syn::DeriveInput) -> Option<&syn::TypePath> { |
72 | let fields: &Fields = match &input.data { |
73 | syn::Data::Struct(s: &DataStruct) => &s.fields, |
74 | _ => return None, |
75 | }; |
76 | |
77 | let unnamed: &Field = match fields { |
78 | syn::Fields::Unnamed(u: &FieldsUnnamed) if u.unnamed.len() == 1 => &u.unnamed[0], |
79 | _ => return None, |
80 | }; |
81 | |
82 | let refcounted: &TypePath = match &unnamed.ty { |
83 | syn::Type::Path(p: &TypePath) => p, |
84 | _ => return None, |
85 | }; |
86 | |
87 | Some(refcounted) |
88 | } |
89 | |
90 | fn refcounted_type_prefix(name: &Ident, crate_ident: &TokenStream) -> proc_macro2::TokenStream { |
91 | quote! { |
92 | <<#name as #crate_ident::subclass::shared::SharedType>::RefCountedType as #crate_ident::subclass::shared::RefCounted> |
93 | } |
94 | } |
95 | |
96 | pub fn impl_shared_boxed(input: &syn::DeriveInput) -> proc_macro2::TokenStream { |
97 | let name = &input.ident; |
98 | |
99 | let refcounted_type = match refcounted_type(input) { |
100 | Some(p) => p, |
101 | _ => { |
102 | abort_call_site!("#[derive(glib::SharedBoxed)] requires struct MyStruct(T: RefCounted)" ) |
103 | } |
104 | }; |
105 | |
106 | let mut gtype_name = NestedMetaItem::<syn::LitStr>::new("name" ) |
107 | .required() |
108 | .value_required(); |
109 | let mut nullable = NestedMetaItem::<syn::LitBool>::new("nullable" ).value_optional(); |
110 | |
111 | let found = parse_nested_meta_items( |
112 | &input.attrs, |
113 | "shared_boxed_type" , |
114 | &mut [&mut gtype_name, &mut nullable], |
115 | ); |
116 | |
117 | match found { |
118 | Ok(None) => { |
119 | abort_call_site!( |
120 | "#[derive(glib::SharedBoxed)] requires #[shared_boxed_type(name = \"SharedBoxedTypeName \")]" |
121 | ) |
122 | } |
123 | Err(e) => return e.to_compile_error(), |
124 | _ => (), |
125 | }; |
126 | |
127 | let gtype_name = gtype_name.value.unwrap(); |
128 | let nullable = nullable.found || nullable.value.map(|b| b.value()).unwrap_or(false); |
129 | |
130 | let crate_ident = crate_ident_new(); |
131 | let refcounted_type_prefix = refcounted_type_prefix(name, &crate_ident); |
132 | |
133 | let impl_from_value = if !nullable { |
134 | gen_impl_from_value(name, &crate_ident) |
135 | } else { |
136 | gen_impl_from_value_optional(name, &crate_ident) |
137 | }; |
138 | |
139 | let impl_to_value_optional = if nullable { |
140 | gen_impl_to_value_optional(name, &crate_ident) |
141 | } else { |
142 | quote! {} |
143 | }; |
144 | |
145 | quote! { |
146 | impl #crate_ident::subclass::shared::SharedType for #name { |
147 | const NAME: &'static ::core::primitive::str = #gtype_name; |
148 | |
149 | type RefCountedType = #refcounted_type; |
150 | |
151 | #[inline] |
152 | fn from_refcounted(this: Self::RefCountedType) -> Self { |
153 | Self(this) |
154 | } |
155 | |
156 | #[inline] |
157 | fn into_refcounted(self) -> Self::RefCountedType { |
158 | self.0 |
159 | } |
160 | } |
161 | |
162 | impl #crate_ident::StaticType for #name { |
163 | #[inline] |
164 | fn static_type() -> #crate_ident::Type { |
165 | static ONCE: ::std::sync::Once = ::std::sync::Once::new(); |
166 | static mut TYPE_: #crate_ident::Type = #crate_ident::Type::INVALID; |
167 | |
168 | ONCE.call_once(|| { |
169 | let type_ = #crate_ident::subclass::shared::register_shared_type::<#name>(); |
170 | unsafe { |
171 | TYPE_ = type_; |
172 | } |
173 | }); |
174 | |
175 | unsafe { TYPE_ } |
176 | } |
177 | } |
178 | |
179 | impl #crate_ident::value::ValueType for #name { |
180 | type Type = #name; |
181 | } |
182 | |
183 | impl #crate_ident::value::ToValue for #name { |
184 | #[inline] |
185 | fn to_value(&self) -> #crate_ident::Value { |
186 | unsafe { |
187 | let ptr = #refcounted_type_prefix::into_raw(self.0.clone()); |
188 | let mut value = #crate_ident::Value::from_type_unchecked(<#name as #crate_ident::StaticType>::static_type()); |
189 | #crate_ident::gobject_ffi::g_value_take_boxed( |
190 | #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, |
191 | ptr as *mut _ |
192 | ); |
193 | value |
194 | } |
195 | } |
196 | |
197 | #[inline] |
198 | fn value_type(&self) -> #crate_ident::Type { |
199 | <#name as #crate_ident::StaticType>::static_type() |
200 | } |
201 | } |
202 | |
203 | impl ::std::convert::From<#name> for #crate_ident::Value { |
204 | #[inline] |
205 | fn from(v: #name) -> Self { |
206 | unsafe { |
207 | let mut value = #crate_ident::Value::from_type_unchecked(<#name as #crate_ident::StaticType>::static_type()); |
208 | #crate_ident::gobject_ffi::g_value_take_boxed( |
209 | #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, |
210 | #crate_ident::translate::IntoGlibPtr::<*mut #refcounted_type_prefix::InnerType>::into_glib_ptr(v) as *mut _, |
211 | ); |
212 | value |
213 | } |
214 | } |
215 | } |
216 | |
217 | #impl_to_value_optional |
218 | |
219 | #impl_from_value |
220 | |
221 | impl #crate_ident::translate::GlibPtrDefault for #name { |
222 | type GlibType = *mut #refcounted_type_prefix::InnerType; |
223 | } |
224 | |
225 | impl #crate_ident::translate::FromGlibPtrBorrow<*const #refcounted_type_prefix::InnerType> for #name { |
226 | #[inline] |
227 | unsafe fn from_glib_borrow(ptr: *const #refcounted_type_prefix::InnerType) -> #crate_ident::translate::Borrowed<Self> { |
228 | debug_assert!(!ptr.is_null()); |
229 | |
230 | // from_raw is taking ownership of the raw pointer here, but wrapping its result |
231 | // in Borrowed::new ensures that it won't be deallocated when it will go out of |
232 | // scope, so the pointer will still be valid afterwards |
233 | #crate_ident::translate::Borrowed::new(#name(#refcounted_type_prefix::from_raw(ptr))) |
234 | } |
235 | } |
236 | |
237 | impl #crate_ident::translate::FromGlibPtrBorrow<*mut #refcounted_type_prefix::InnerType> for #name { |
238 | #[inline] |
239 | unsafe fn from_glib_borrow(ptr: *mut #refcounted_type_prefix::InnerType) -> #crate_ident::translate::Borrowed<Self> { |
240 | #crate_ident::translate::FromGlibPtrBorrow::from_glib_borrow(ptr as *const _) |
241 | } |
242 | } |
243 | |
244 | |
245 | impl #crate_ident::translate::FromGlibPtrNone<*const #refcounted_type_prefix::InnerType> for #name { |
246 | #[inline] |
247 | unsafe fn from_glib_none(ptr: *const #refcounted_type_prefix::InnerType) -> Self { |
248 | let ptr = #refcounted_type_prefix::ref_(ptr); |
249 | #name(#refcounted_type_prefix::from_raw(ptr)) |
250 | } |
251 | } |
252 | |
253 | impl #crate_ident::translate::FromGlibPtrNone<*mut #refcounted_type_prefix::InnerType> for #name { |
254 | #[inline] |
255 | unsafe fn from_glib_none(ptr: *mut #refcounted_type_prefix::InnerType) -> Self { |
256 | #crate_ident::translate::FromGlibPtrNone::from_glib_none(ptr as *const _) |
257 | } |
258 | } |
259 | |
260 | impl #crate_ident::translate::FromGlibPtrFull<*mut #refcounted_type_prefix::InnerType> for #name { |
261 | #[inline] |
262 | unsafe fn from_glib_full(ptr: *mut #refcounted_type_prefix::InnerType) -> Self { |
263 | #name(#refcounted_type_prefix::from_raw(ptr)) |
264 | } |
265 | } |
266 | |
267 | impl #crate_ident::translate::IntoGlibPtr<*mut #refcounted_type_prefix::InnerType> for #name { |
268 | #[inline] |
269 | unsafe fn into_glib_ptr(self) -> *mut #refcounted_type_prefix::InnerType { |
270 | let r = <Self as #crate_ident::subclass::shared::SharedType>::into_refcounted(self); |
271 | #refcounted_type_prefix::into_raw(r) as *mut _ |
272 | } |
273 | } |
274 | |
275 | impl<'a> #crate_ident::translate::ToGlibPtr<'a, *const #refcounted_type_prefix::InnerType> for #name { |
276 | type Storage = std::marker::PhantomData<&'a Self>; |
277 | |
278 | #[inline] |
279 | fn to_glib_none(&'a self) -> #crate_ident::translate::Stash<'a, *const #refcounted_type_prefix::InnerType, Self> { |
280 | unsafe { |
281 | #crate_ident::translate::Stash(#refcounted_type_prefix::as_ptr(&self.0), std::marker::PhantomData) |
282 | } |
283 | } |
284 | |
285 | #[inline] |
286 | fn to_glib_full(&self) -> *const #refcounted_type_prefix::InnerType { |
287 | let r = <#name as #crate_ident::subclass::shared::SharedType>::into_refcounted(self.clone()); |
288 | unsafe { |
289 | #refcounted_type_prefix::into_raw(r) |
290 | } |
291 | } |
292 | } |
293 | |
294 | impl<'a> #crate_ident::translate::ToGlibPtr<'a, *mut #refcounted_type_prefix::InnerType> for #name { |
295 | type Storage = std::marker::PhantomData<&'a Self>; |
296 | |
297 | #[inline] |
298 | fn to_glib_none(&'a self) -> #crate_ident::translate::Stash<'a, *mut #refcounted_type_prefix::InnerType, Self> { |
299 | unsafe { |
300 | #crate_ident::translate::Stash(#refcounted_type_prefix::as_ptr(&self.0) as *mut _, std::marker::PhantomData) |
301 | } |
302 | } |
303 | |
304 | #[inline] |
305 | fn to_glib_full(&self) -> *mut #refcounted_type_prefix::InnerType { |
306 | let r = <#name as #crate_ident::subclass::shared::SharedType>::into_refcounted(self.clone()); |
307 | unsafe { |
308 | #refcounted_type_prefix::into_raw(r) as *mut _ |
309 | } |
310 | } |
311 | } |
312 | |
313 | impl #crate_ident::HasParamSpec for #name { |
314 | type ParamSpec = #crate_ident::ParamSpecBoxed; |
315 | type SetValue = Self; |
316 | type BuilderFn = fn(&::core::primitive::str) -> #crate_ident::ParamSpecBoxedBuilder<Self>; |
317 | |
318 | fn param_spec_builder() -> Self::BuilderFn { |
319 | |name| Self::ParamSpec::builder(name) |
320 | } |
321 | } |
322 | } |
323 | } |
324 | |