glib_macros/
shared_boxed_derive.rs

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