glib_macros/
shared_boxed_derive.rs1use 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 #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}