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_impl_to_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream9 fn 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 fn to_value_optional(s: Option<&Self>) -> #crate_ident::Value {
15 let mut value = #crate_ident::Value::for_value_type::<Self>();
16 unsafe {
17 let ptr = match s {
18 Some(s) => #refcounted_type_prefix::into_raw(s.0.clone()),
19 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 }
33
gen_impl_from_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream34 fn gen_impl_from_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream {
35 let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident);
36
37 quote! {
38 unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name {
39 type Checker = #crate_ident::value::GenericValueTypeOrNoneChecker<Self>;
40
41 unsafe fn from_value(value: &'a #crate_ident::Value) -> Self {
42 let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0);
43 assert!(!ptr.is_null());
44 #name(#refcounted_type_prefix::from_raw(ptr as *mut _))
45 }
46 }
47 }
48 }
49
gen_impl_from_value(name: &Ident, crate_ident: &TokenStream) -> TokenStream50 fn gen_impl_from_value(name: &Ident, crate_ident: &TokenStream) -> TokenStream {
51 let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident);
52
53 quote! {
54 unsafe impl<'a> #crate_ident::value::FromValue<'a> for #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_dup_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0);
59 assert!(!ptr.is_null());
60 #name(#refcounted_type_prefix::from_raw(ptr as *mut _))
61 }
62 }
63 }
64 }
65
refcounted_type(input: &syn::DeriveInput) -> Option<&syn::TypePath>66 fn refcounted_type(input: &syn::DeriveInput) -> Option<&syn::TypePath> {
67 let fields = match &input.data {
68 syn::Data::Struct(s) => &s.fields,
69 _ => return None,
70 };
71
72 let unnamed = match fields {
73 syn::Fields::Unnamed(u) if u.unnamed.len() == 1 => &u.unnamed[0],
74 _ => return None,
75 };
76
77 let refcounted = match &unnamed.ty {
78 syn::Type::Path(p) => p,
79 _ => return None,
80 };
81
82 Some(refcounted)
83 }
84
refcounted_type_prefix(name: &Ident, crate_ident: &TokenStream) -> proc_macro2::TokenStream85 fn refcounted_type_prefix(name: &Ident, crate_ident: &TokenStream) -> proc_macro2::TokenStream {
86 quote! {
87 <<#name as #crate_ident::subclass::shared::SharedType>::RefCountedType as #crate_ident::subclass::shared::RefCounted>
88 }
89 }
90
impl_gshared_boxed(input: &syn::DeriveInput) -> proc_macro2::TokenStream91 pub fn impl_gshared_boxed(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
92 let refcounted_type = match refcounted_type(input) {
93 Some(p) => p,
94 _ => abort_call_site!("derive(GSharedBoxed) requires struct MyStruct(T: RefCounted)"),
95 };
96
97 let name = &input.ident;
98 let gtype_name = match parse_type_name(input, "gshared_boxed") {
99 Ok(v) => v,
100 Err(e) => abort_call_site!(
101 "{}: derive(GSharedBoxed) requires #[gshared_boxed(type_name = \"SharedTypeName\")]",
102 e
103 ),
104 };
105
106 let meta = find_attribute_meta(&input.attrs, "gshared_boxed")
107 .unwrap()
108 .unwrap();
109 let nullable = find_nested_meta(&meta, "nullable").is_some();
110 let crate_ident = crate_ident_new();
111 let refcounted_type_prefix = refcounted_type_prefix(name, &crate_ident);
112
113 let impl_from_value = if !nullable {
114 gen_impl_from_value(name, &crate_ident)
115 } else {
116 gen_impl_from_value_optional(name, &crate_ident)
117 };
118
119 let impl_to_value_optional = if nullable {
120 gen_impl_to_value_optional(name, &crate_ident)
121 } else {
122 quote! {}
123 };
124
125 quote! {
126 impl #crate_ident::subclass::shared::SharedType for #name {
127 const NAME: &'static str = #gtype_name;
128
129 type RefCountedType = #refcounted_type;
130
131 fn from_refcounted(this: Self::RefCountedType) -> Self {
132 Self(this)
133 }
134
135 fn into_refcounted(self) -> Self::RefCountedType {
136 self.0
137 }
138 }
139
140 impl #crate_ident::StaticType for #name {
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::shared::register_shared_type::<#name>();
147 unsafe {
148 TYPE_ = type_;
149 }
150 });
151
152 unsafe { TYPE_ }
153 }
154 }
155
156 impl #crate_ident::value::ValueType for #name {
157 type Type = #name;
158 }
159
160 impl #crate_ident::value::ToValue for #name {
161 fn to_value(&self) -> #crate_ident::Value {
162 unsafe {
163 let ptr = #refcounted_type_prefix::into_raw(self.0.clone());
164 let mut value = #crate_ident::Value::from_type(<#name as #crate_ident::StaticType>::static_type());
165 #crate_ident::gobject_ffi::g_value_take_boxed(
166 #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
167 ptr as *mut _
168 );
169 value
170 }
171 }
172
173 fn value_type(&self) -> #crate_ident::Type {
174 <#name as #crate_ident::StaticType>::static_type()
175 }
176 }
177
178 #impl_to_value_optional
179
180 #impl_from_value
181 }
182 }
183