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