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_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
37fn 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
54fn 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
71fn 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
90fn 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
96pub 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