1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use proc_macro2::{Ident, TokenStream};
6 use quote::quote;
7 use syn::ext::IdentExt;
8 
9 use super::gen_ffi_function;
10 use crate::export::ExportedImplFnArgs;
11 use crate::fnsig::FnSignature;
12 use crate::util::extract_docstring;
13 use uniffi_meta::UniffiTraitDiscriminants;
14 
expand_uniffi_trait_export( self_ident: Ident, uniffi_traits: Vec<UniffiTraitDiscriminants>, ) -> syn::Result<TokenStream>15 pub(crate) fn expand_uniffi_trait_export(
16     self_ident: Ident,
17     uniffi_traits: Vec<UniffiTraitDiscriminants>,
18 ) -> syn::Result<TokenStream> {
19     let udl_mode = false;
20     let mut impl_items = Vec::new();
21     let mut global_items = Vec::new();
22     for trait_id in uniffi_traits {
23         match trait_id {
24             UniffiTraitDiscriminants::Debug => {
25                 let method = quote! {
26                     fn uniffi_trait_debug(&self) -> String {
27                         ::uniffi::deps::static_assertions::assert_impl_all!(#self_ident: ::std::fmt::Debug);
28                         format!("{:?}", self)
29                     }
30                 };
31                 let (ffi_func, method_meta) =
32                     process_uniffi_trait_method(&method, &self_ident, udl_mode)?;
33                 // metadata for the trait - which includes metadata for the method.
34                 let discr = UniffiTraitDiscriminants::Debug as u8;
35                 let trait_meta = crate::util::create_metadata_items(
36                     "uniffi_trait",
37                     &format!("{}_Debug", self_ident.unraw()),
38                     quote! {
39                         ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::UNIFFI_TRAIT)
40                         .concat_value(#discr)
41                         .concat(#method_meta)
42                     },
43                     None,
44                 );
45                 impl_items.push(method);
46                 global_items.push(ffi_func);
47                 global_items.push(trait_meta);
48             }
49             UniffiTraitDiscriminants::Display => {
50                 let method = quote! {
51                     fn uniffi_trait_display(&self) -> String {
52                         ::uniffi::deps::static_assertions::assert_impl_all!(#self_ident: ::std::fmt::Display);
53                         format!("{}", self)
54                     }
55                 };
56                 let (ffi_func, method_meta) =
57                     process_uniffi_trait_method(&method, &self_ident, udl_mode)?;
58                 // metadata for the trait - which includes metadata for the method.
59                 let discr = UniffiTraitDiscriminants::Display as u8;
60                 let trait_meta = crate::util::create_metadata_items(
61                     "uniffi_trait",
62                     &format!("{}_Display", self_ident.unraw()),
63                     quote! {
64                         ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::UNIFFI_TRAIT)
65                         .concat_value(#discr)
66                         .concat(#method_meta)
67                     },
68                     None,
69                 );
70                 impl_items.push(method);
71                 global_items.push(ffi_func);
72                 global_items.push(trait_meta);
73             }
74             UniffiTraitDiscriminants::Hash => {
75                 let method = quote! {
76                     fn uniffi_trait_hash(&self) -> u64 {
77                         use ::std::hash::{Hash, Hasher};
78                         ::uniffi::deps::static_assertions::assert_impl_all!(#self_ident: Hash);
79                         let mut s = ::std::collections::hash_map::DefaultHasher::new();
80                         Hash::hash(self, &mut s);
81                         s.finish()
82                     }
83                 };
84                 let (ffi_func, method_meta) =
85                     process_uniffi_trait_method(&method, &self_ident, udl_mode)?;
86                 // metadata for the trait - which includes metadata for the hash method.
87                 let discr = UniffiTraitDiscriminants::Hash as u8;
88                 let trait_meta = crate::util::create_metadata_items(
89                     "uniffi_trait",
90                     &format!("{}_Hash", self_ident.unraw()),
91                     quote! {
92                         ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::UNIFFI_TRAIT)
93                         .concat_value(#discr)
94                         .concat(#method_meta)
95                     },
96                     None,
97                 );
98                 impl_items.push(method);
99                 global_items.push(ffi_func);
100                 global_items.push(trait_meta);
101             }
102             UniffiTraitDiscriminants::Eq => {
103                 let method_eq = quote! {
104                     fn uniffi_trait_eq_eq(&self, other: &#self_ident) -> bool {
105                         use ::std::cmp::PartialEq;
106                         uniffi::deps::static_assertions::assert_impl_all!(#self_ident: PartialEq); // This object has a trait method which requires `PartialEq` be implemented.
107                         PartialEq::eq(self, other)
108                     }
109                 };
110                 let method_ne = quote! {
111                     fn uniffi_trait_eq_ne(&self, other: &#self_ident) -> bool {
112                         use ::std::cmp::PartialEq;
113                         uniffi::deps::static_assertions::assert_impl_all!(#self_ident: PartialEq); // This object has a trait method which requires `PartialEq` be implemented.
114                         PartialEq::ne(self, other)
115                     }
116                 };
117                 let (ffi_func_eq, method_meta_eq) =
118                     process_uniffi_trait_method(&method_eq, &self_ident, udl_mode)?;
119                 let (ffi_func_ne, method_meta_ne) =
120                     process_uniffi_trait_method(&method_ne, &self_ident, udl_mode)?;
121                 // metadata for the trait itself.
122                 let discr = UniffiTraitDiscriminants::Eq as u8;
123                 let trait_meta = crate::util::create_metadata_items(
124                     "uniffi_trait",
125                     &format!("{}_Eq", self_ident.unraw()),
126                     quote! {
127                         ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::UNIFFI_TRAIT)
128                         .concat_value(#discr)
129                         .concat(#method_meta_eq)
130                         .concat(#method_meta_ne)
131                     },
132                     None,
133                 );
134                 impl_items.push(method_eq);
135                 impl_items.push(method_ne);
136                 global_items.push(ffi_func_eq);
137                 global_items.push(ffi_func_ne);
138                 global_items.push(trait_meta);
139             }
140         }
141     }
142     Ok(quote! {
143         #[doc(hidden)]
144         impl #self_ident {
145             #(#impl_items)*
146         }
147         #(#global_items)*
148     })
149 }
150 
process_uniffi_trait_method( method: &TokenStream, self_ident: &Ident, udl_mode: bool, ) -> syn::Result<(TokenStream, TokenStream)>151 fn process_uniffi_trait_method(
152     method: &TokenStream,
153     self_ident: &Ident,
154     udl_mode: bool,
155 ) -> syn::Result<(TokenStream, TokenStream)> {
156     let item = syn::parse(method.clone().into())?;
157 
158     let syn::Item::Fn(item) = item else {
159         unreachable!()
160     };
161 
162     let docstring = extract_docstring(&item.attrs)?;
163 
164     let ffi_func = gen_ffi_function(
165         &FnSignature::new_method(
166             self_ident.clone(),
167             item.sig.clone(),
168             ExportedImplFnArgs::default(),
169             docstring.clone(),
170         )?,
171         &None,
172         udl_mode,
173     )?;
174     // metadata for the method, which will be packed inside metadata for the trait.
175     let method_meta = FnSignature::new_method(
176         self_ident.clone(),
177         item.sig,
178         ExportedImplFnArgs::default(),
179         docstring,
180     )?
181     .metadata_expr()?;
182     Ok((ffi_func, method_meta))
183 }
184