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