1 // Take a look at the license at the top of the repository in the LICENSE file.
2 
3 use proc_macro2::{Ident, TokenStream};
4 use proc_macro_error::abort_call_site;
5 use quote::quote;
6 
7 use crate::utils::{crate_ident_new, find_attribute_meta, find_nested_meta, parse_type_name};
8 
gen_option_to_ptr() -> TokenStream9 fn gen_option_to_ptr() -> TokenStream {
10     quote! {
11         match s {
12             Some(s) => Box::into_raw(Box::new(s.clone())),
13             None => std::ptr::null_mut(),
14         };
15     }
16 }
17 
gen_impl_from_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream18 fn 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             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                 assert!(!ptr.is_null());
26                 *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             unsafe fn from_value(value: &'a #crate_ident::Value) -> Self {
34                 let ptr = #crate_ident::gobject_ffi::g_value_get_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0);
35                 assert!(!ptr.is_null());
36                 &*(ptr as *mut #name)
37             }
38         }
39     }
40 }
41 
gen_impl_from_value(name: &Ident, crate_ident: &TokenStream) -> TokenStream42 fn gen_impl_from_value(name: &Ident, crate_ident: &TokenStream) -> TokenStream {
43     quote! {
44         unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name {
45             type Checker = #crate_ident::value::GenericValueTypeChecker<Self>;
46 
47             unsafe fn from_value(value: &'a #crate_ident::Value) -> Self {
48                 let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0);
49                 assert!(!ptr.is_null());
50                 *Box::from_raw(ptr as *mut #name)
51             }
52         }
53 
54         unsafe impl<'a> #crate_ident::value::FromValue<'a> for &'a #name {
55             type Checker = #crate_ident::value::GenericValueTypeChecker<Self>;
56 
57             unsafe fn from_value(value: &'a #crate_ident::Value) -> Self {
58                 let ptr = #crate_ident::gobject_ffi::g_value_get_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0);
59                 assert!(!ptr.is_null());
60                 &*(ptr as *mut #name)
61             }
62         }
63     }
64 }
65 
gen_impl_to_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream66 fn gen_impl_to_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream {
67     let option_to_ptr = gen_option_to_ptr();
68 
69     quote! {
70         impl #crate_ident::value::ToValueOptional for #name {
71             fn to_value_optional(s: Option<&Self>) -> #crate_ident::Value {
72                 let mut value = #crate_ident::Value::for_value_type::<Self>();
73                 unsafe {
74                     let ptr: *mut #name = #option_to_ptr;
75                     #crate_ident::gobject_ffi::g_value_take_boxed(
76                         #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
77                         ptr as *mut _
78                     );
79                 }
80 
81                 value
82             }
83         }
84     }
85 }
86 
impl_gboxed(input: &syn::DeriveInput) -> TokenStream87 pub fn impl_gboxed(input: &syn::DeriveInput) -> TokenStream {
88     let name = &input.ident;
89 
90     let gtype_name = match parse_type_name(input, "gboxed") {
91         Ok(v) => v,
92         Err(e) => abort_call_site!(
93             "{}: derive(GBoxed) requires #[gboxed(type_name = \"BoxedTypeName\")]",
94             e
95         ),
96     };
97 
98     let crate_ident = crate_ident_new();
99 
100     let meta = find_attribute_meta(&input.attrs, "gboxed")
101         .unwrap()
102         .unwrap();
103     let nullable = find_nested_meta(&meta, "nullable").is_some();
104 
105     let impl_from_value = if !nullable {
106         gen_impl_from_value(name, &crate_ident)
107     } else {
108         gen_impl_from_value_optional(name, &crate_ident)
109     };
110     let impl_to_value_optional = if nullable {
111         gen_impl_to_value_optional(name, &crate_ident)
112     } else {
113         quote! {}
114     };
115 
116     quote! {
117         impl #crate_ident::subclass::boxed::BoxedType for #name {
118             const NAME: &'static str = #gtype_name;
119         }
120 
121         impl #crate_ident::StaticType for #name {
122             fn static_type() -> #crate_ident::Type {
123                 static ONCE: ::std::sync::Once = ::std::sync::Once::new();
124                 static mut TYPE_: #crate_ident::Type = #crate_ident::Type::INVALID;
125 
126                 ONCE.call_once(|| {
127                     let type_ = #crate_ident::subclass::register_boxed_type::<#name>();
128                     unsafe {
129                         TYPE_ = type_;
130                     }
131                 });
132 
133                 unsafe {
134                     assert!(TYPE_.is_valid());
135                     TYPE_
136                 }
137             }
138         }
139 
140         impl #crate_ident::value::ValueType for #name {
141             type Type = #name;
142         }
143 
144         impl #crate_ident::value::ToValue for #name {
145             fn to_value(&self) -> #crate_ident::Value {
146                 unsafe {
147                     let ptr: *mut #name = Box::into_raw(Box::new(self.clone()));
148                     let mut value = #crate_ident::Value::from_type(<#name as #crate_ident::StaticType>::static_type());
149                     #crate_ident::gobject_ffi::g_value_take_boxed(
150                         #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
151                         ptr as *mut _
152                     );
153                     value
154                 }
155             }
156 
157             fn value_type(&self) -> #crate_ident::Type {
158                 <#name as #crate_ident::StaticType>::static_type()
159             }
160         }
161 
162         #impl_to_value_optional
163 
164         #impl_from_value
165     }
166 }
167