glib_macros/
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::{crate_ident_new, parse_nested_meta_items, NestedMetaItem};
7
8fn gen_option_to_ptr() -> TokenStream {
9    quote! {
10        match s {
11            ::core::option::Option::Some(s) => ::std::boxed::Box::into_raw(::std::boxed::Box::new(s.clone())),
12            ::core::option::Option::None => ::std::ptr::null_mut(),
13        };
14    }
15}
16
17fn gen_impl_from_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream {
18    quote! {
19        unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name {
20            type Checker = #crate_ident::value::GenericValueTypeOrNoneChecker<Self>;
21
22            #[inline]
23            unsafe fn from_value(value: &'a #crate_ident::Value) -> Self {
24                let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0);
25                debug_assert!(!ptr.is_null());
26                *::std::boxed::Box::from_raw(ptr as *mut #name)
27            }
28        }
29
30        unsafe impl<'a> #crate_ident::value::FromValue<'a> for &'a #name {
31            type Checker = #crate_ident::value::GenericValueTypeOrNoneChecker<Self>;
32
33            #[inline]
34            unsafe fn from_value(value: &'a #crate_ident::Value) -> Self {
35                let ptr = #crate_ident::gobject_ffi::g_value_get_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0);
36                debug_assert!(!ptr.is_null());
37                &*(ptr as *mut #name)
38            }
39        }
40    }
41}
42
43fn gen_impl_from_value(name: &Ident, crate_ident: &TokenStream) -> TokenStream {
44    quote! {
45        unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name {
46            type Checker = #crate_ident::value::GenericValueTypeChecker<Self>;
47
48            #[inline]
49            unsafe fn from_value(value: &'a #crate_ident::Value) -> Self {
50                let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0);
51                debug_assert!(!ptr.is_null());
52                *::std::boxed::Box::from_raw(ptr as *mut #name)
53            }
54        }
55
56        unsafe impl<'a> #crate_ident::value::FromValue<'a> for &'a #name {
57            type Checker = #crate_ident::value::GenericValueTypeChecker<Self>;
58
59            #[inline]
60            unsafe fn from_value(value: &'a #crate_ident::Value) -> Self {
61                let ptr = #crate_ident::gobject_ffi::g_value_get_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0);
62                debug_assert!(!ptr.is_null());
63                &*(ptr as *mut #name)
64            }
65        }
66    }
67}
68
69fn gen_impl_to_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream {
70    let option_to_ptr = gen_option_to_ptr();
71
72    quote! {
73        impl #crate_ident::value::ToValueOptional for #name {
74            #[inline]
75            fn to_value_optional(s: ::core::option::Option<&Self>) -> #crate_ident::Value {
76                let mut value = #crate_ident::Value::for_value_type::<Self>();
77                unsafe {
78                    let ptr: *mut #name = #option_to_ptr;
79                    #crate_ident::gobject_ffi::g_value_take_boxed(
80                        #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
81                        ptr as *mut _
82                    );
83                }
84
85                value
86            }
87        }
88
89        impl #crate_ident::value::ValueTypeOptional for #name { }
90    }
91}
92
93pub fn impl_boxed(input: &syn::DeriveInput) -> syn::Result<TokenStream> {
94    let name = &input.ident;
95
96    let mut gtype_name = NestedMetaItem::<syn::LitStr>::new("name")
97        .required()
98        .value_required();
99    let mut nullable = NestedMetaItem::<syn::LitBool>::new("nullable").value_optional();
100    let mut allow_name_conflict =
101        NestedMetaItem::<syn::LitBool>::new("allow_name_conflict").value_optional();
102
103    let found = parse_nested_meta_items(
104        &input.attrs,
105        "boxed_type",
106        &mut [&mut gtype_name, &mut nullable, &mut allow_name_conflict],
107    )?;
108
109    if found.is_none() {
110        return Err(syn::Error::new_spanned(
111            input,
112            "#[derive(glib::Boxed)] requires #[boxed_type(name = \"BoxedTypeName\")]",
113        ));
114    }
115
116    let gtype_name = gtype_name.value.unwrap();
117    let nullable = nullable.found || nullable.value.map(|b| b.value()).unwrap_or(false);
118    let allow_name_conflict = allow_name_conflict.found
119        || allow_name_conflict
120            .value
121            .map(|b| b.value())
122            .unwrap_or(false);
123
124    let crate_ident = crate_ident_new();
125
126    let impl_from_value = if !nullable {
127        gen_impl_from_value(name, &crate_ident)
128    } else {
129        gen_impl_from_value_optional(name, &crate_ident)
130    };
131    let impl_to_value_optional = if nullable {
132        gen_impl_to_value_optional(name, &crate_ident)
133    } else {
134        quote! {}
135    };
136
137    Ok(quote! {
138        impl #crate_ident::subclass::boxed::BoxedType for #name {
139            const NAME: &'static ::core::primitive::str = #gtype_name;
140            const ALLOW_NAME_CONFLICT: bool = #allow_name_conflict;
141        }
142
143        impl #crate_ident::prelude::StaticType for #name {
144            #[inline]
145            fn static_type() -> #crate_ident::Type {
146                static TYPE: ::std::sync::OnceLock<#crate_ident::Type> = ::std::sync::OnceLock::new();
147                *TYPE.get_or_init(|| {
148                    #crate_ident::subclass::register_boxed_type::<#name>()
149                })
150            }
151        }
152
153        impl #crate_ident::value::ValueType for #name {
154            type Type = #name;
155        }
156
157        impl #crate_ident::value::ToValue for #name {
158            #[inline]
159            fn to_value(&self) -> #crate_ident::Value {
160                unsafe {
161                    let ptr: *mut #name = ::std::boxed::Box::into_raw(::std::boxed::Box::new(self.clone()));
162                    let mut value = #crate_ident::Value::from_type_unchecked(<#name as #crate_ident::prelude::StaticType>::static_type());
163                    #crate_ident::gobject_ffi::g_value_take_boxed(
164                        #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
165                        ptr as *mut _
166                    );
167                    value
168                }
169            }
170
171            #[inline]
172            fn value_type(&self) -> #crate_ident::Type {
173                <#name as #crate_ident::prelude::StaticType>::static_type()
174            }
175        }
176
177        impl ::std::convert::From<#name> for #crate_ident::Value {
178            #[inline]
179            fn from(v: #name) -> Self {
180                unsafe {
181                    let mut value = #crate_ident::Value::from_type_unchecked(<#name as #crate_ident::prelude::StaticType>::static_type());
182                    #crate_ident::gobject_ffi::g_value_take_boxed(
183                        #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
184                        #crate_ident::translate::IntoGlibPtr::<*mut #name>::into_glib_ptr(v) as *mut _,
185                    );
186                    value
187                }
188            }
189        }
190
191        #impl_to_value_optional
192
193        #impl_from_value
194
195        unsafe impl #crate_ident::translate::TransparentType for #name {
196            type GlibType = #name;
197        }
198
199        impl #crate_ident::translate::GlibPtrDefault for #name {
200            type GlibType = *mut #name;
201        }
202
203        impl #crate_ident::translate::FromGlibPtrBorrow<*const #name> for #name {
204            #[inline]
205            unsafe fn from_glib_borrow(ptr: *const #name) -> #crate_ident::translate::Borrowed<Self> {
206                #crate_ident::translate::FromGlibPtrBorrow::from_glib_borrow(ptr as *mut _)
207            }
208        }
209
210        impl #crate_ident::translate::FromGlibPtrBorrow<*mut #name> for #name {
211            #[inline]
212            unsafe fn from_glib_borrow(ptr: *mut #name) -> #crate_ident::translate::Borrowed<Self> {
213                debug_assert!(!ptr.is_null());
214
215                #crate_ident::translate::Borrowed::new(std::ptr::read(ptr))
216            }
217        }
218
219        impl #crate_ident::translate::FromGlibPtrNone<*const #name> for #name {
220            #[inline]
221            unsafe fn from_glib_none(ptr: *const #name) -> Self {
222                debug_assert!(!ptr.is_null());
223                (&*ptr).clone()
224            }
225        }
226
227        impl #crate_ident::translate::FromGlibPtrNone<*mut #name> for #name {
228            #[inline]
229            unsafe fn from_glib_none(ptr: *mut #name) -> Self {
230                #crate_ident::translate::FromGlibPtrNone::from_glib_none(ptr as *const _)
231            }
232        }
233
234        impl #crate_ident::translate::FromGlibPtrFull<*mut #name> for #name {
235            #[inline]
236            unsafe fn from_glib_full(ptr: *mut #name) -> Self {
237                debug_assert!(!ptr.is_null());
238                *::std::boxed::Box::from_raw(ptr)
239            }
240        }
241
242        impl #crate_ident::translate::IntoGlibPtr<*mut #name> for #name {
243            #[inline]
244            unsafe fn into_glib_ptr(self) -> *mut #name {
245                ::std::boxed::Box::into_raw(::std::boxed::Box::new(self)) as *mut _
246            }
247        }
248
249        impl<'a> #crate_ident::translate::ToGlibPtr<'a, *const #name> for #name {
250            type Storage = std::marker::PhantomData<&'a Self>;
251
252            #[inline]
253            fn to_glib_none(&'a self) -> #crate_ident::translate::Stash<'a, *const #name, Self> {
254                #crate_ident::translate::Stash(self as *const #name, std::marker::PhantomData)
255            }
256
257            #[inline]
258            fn to_glib_full(&self) -> *const #name {
259                ::std::boxed::Box::into_raw(::std::boxed::Box::new(self.clone()))
260            }
261        }
262
263        impl<'a> #crate_ident::translate::ToGlibPtr<'a, *mut #name> for #name {
264            type Storage = std::marker::PhantomData<&'a Self>;
265
266            #[inline]
267            fn to_glib_none(&'a self) -> #crate_ident::translate::Stash<'a, *mut #name, Self> {
268                #crate_ident::translate::Stash(self as *const #name as *mut _, std::marker::PhantomData)
269            }
270
271            #[inline]
272            fn to_glib_full(&self) -> *mut #name {
273                ::std::boxed::Box::into_raw(::std::boxed::Box::new(self.clone())) as *mut _
274            }
275        }
276
277        impl #crate_ident::prelude::HasParamSpec for #name {
278            type ParamSpec = #crate_ident::ParamSpecBoxed;
279            type SetValue = Self;
280            type BuilderFn = fn(&::core::primitive::str) -> #crate_ident::ParamSpecBoxedBuilder<Self>;
281
282            fn param_spec_builder() -> Self::BuilderFn {
283                |name| Self::ParamSpec::builder(name)
284            }
285        }
286    })
287}