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 crate::fnsig::FnSignature;
6 use proc_macro::TokenStream;
7 use proc_macro2::{Ident, Span};
8 use quote::ToTokens;
9
10 use super::attributes::{
11 ExportFnArgs, ExportImplArgs, ExportStructArgs, ExportTraitArgs, ExportedImplFnArgs,
12 ExportedImplFnAttributes,
13 };
14 use crate::util::extract_docstring;
15 use uniffi_meta::UniffiTraitDiscriminants;
16
17 pub(super) enum ExportItem {
18 Function {
19 sig: FnSignature,
20 args: ExportFnArgs,
21 },
22 Impl {
23 self_ident: Ident,
24 items: Vec<ImplItem>,
25 args: ExportImplArgs,
26 },
27 Trait {
28 self_ident: Ident,
29 items: Vec<ImplItem>,
30 with_foreign: bool,
31 callback_interface_only: bool,
32 docstring: String,
33 args: ExportTraitArgs,
34 },
35 Struct {
36 self_ident: Ident,
37 uniffi_traits: Vec<UniffiTraitDiscriminants>,
38 },
39 }
40
41 impl ExportItem {
new(item: syn::Item, attr_args: TokenStream) -> syn::Result<Self>42 pub fn new(item: syn::Item, attr_args: TokenStream) -> syn::Result<Self> {
43 match item {
44 syn::Item::Fn(item) => {
45 let args: ExportFnArgs = syn::parse(attr_args)?;
46 let docstring = extract_docstring(&item.attrs)?;
47 let sig = FnSignature::new_function(item.sig, args.clone(), docstring)?;
48 Ok(Self::Function { sig, args })
49 }
50 syn::Item::Impl(item) => Self::from_impl(item, attr_args),
51 syn::Item::Trait(item) => Self::from_trait(item, attr_args),
52 syn::Item::Struct(item) => Self::from_struct(item, attr_args),
53 // FIXME: Support const / static?
54 _ => Err(syn::Error::new(
55 Span::call_site(),
56 "unsupported item: only functions and impl \
57 blocks may be annotated with this attribute",
58 )),
59 }
60 }
61
from_impl(item: syn::ItemImpl, attr_args: TokenStream) -> syn::Result<Self>62 pub fn from_impl(item: syn::ItemImpl, attr_args: TokenStream) -> syn::Result<Self> {
63 let args: ExportImplArgs = syn::parse(attr_args)?;
64 if !item.generics.params.is_empty() || item.generics.where_clause.is_some() {
65 return Err(syn::Error::new_spanned(
66 &item.generics,
67 "generic impls are not currently supported by uniffi::export",
68 ));
69 }
70
71 let type_path = type_as_type_path(&item.self_ty)?;
72
73 if type_path.qself.is_some() {
74 return Err(syn::Error::new_spanned(
75 type_path,
76 "qualified self types are not currently supported by uniffi::export",
77 ));
78 }
79
80 let self_ident = match type_path.path.get_ident() {
81 Some(id) => id,
82 None => {
83 return Err(syn::Error::new_spanned(
84 type_path,
85 "qualified paths in self-types are not currently supported by uniffi::export",
86 ));
87 }
88 };
89
90 let items = item
91 .items
92 .into_iter()
93 .map(|item| {
94 let impl_fn = match item {
95 syn::ImplItem::Fn(m) => m,
96 _ => {
97 return Err(syn::Error::new_spanned(
98 item,
99 "only fn's are supported in impl blocks annotated with uniffi::export",
100 ));
101 }
102 };
103
104 let docstring = extract_docstring(&impl_fn.attrs)?;
105 let attrs = ExportedImplFnAttributes::new(&impl_fn.attrs)?;
106 let item = if attrs.constructor {
107 ImplItem::Constructor(FnSignature::new_constructor(
108 self_ident.clone(),
109 impl_fn.sig,
110 attrs.args,
111 docstring,
112 )?)
113 } else {
114 ImplItem::Method(FnSignature::new_method(
115 self_ident.clone(),
116 impl_fn.sig,
117 attrs.args,
118 docstring,
119 )?)
120 };
121
122 Ok(item)
123 })
124 .collect::<syn::Result<_>>()?;
125
126 Ok(Self::Impl {
127 items,
128 self_ident: self_ident.to_owned(),
129 args,
130 })
131 }
132
from_trait(item: syn::ItemTrait, attr_args: TokenStream) -> syn::Result<Self>133 fn from_trait(item: syn::ItemTrait, attr_args: TokenStream) -> syn::Result<Self> {
134 let args: ExportTraitArgs = syn::parse(attr_args)?;
135 let with_foreign = args.callback_interface.is_some() || args.with_foreign.is_some();
136 let callback_interface_only = args.callback_interface.is_some();
137
138 if !item.generics.params.is_empty() || item.generics.where_clause.is_some() {
139 return Err(syn::Error::new_spanned(
140 &item.generics,
141 "generic impls are not currently supported by uniffi::export",
142 ));
143 }
144
145 let self_ident = item.ident.to_owned();
146 let docstring = extract_docstring(&item.attrs)?;
147 let items = item
148 .items
149 .into_iter()
150 .enumerate()
151 .map(|(i, item)| {
152 let tim = match item {
153 syn::TraitItem::Fn(tim) => tim,
154 _ => {
155 return Err(syn::Error::new_spanned(
156 item,
157 "only fn's are supported in traits annotated with uniffi::export",
158 ));
159 }
160 };
161
162 let docstring = extract_docstring(&tim.attrs)?;
163 let attrs = ExportedImplFnAttributes::new(&tim.attrs)?;
164 let item = if attrs.constructor {
165 return Err(syn::Error::new_spanned(
166 tim,
167 "exported traits can not have constructors",
168 ));
169 } else {
170 ImplItem::Method(FnSignature::new_trait_method(
171 self_ident.clone(),
172 tim.sig,
173 ExportedImplFnArgs::default(),
174 i as u32,
175 docstring,
176 )?)
177 };
178
179 Ok(item)
180 })
181 .collect::<syn::Result<_>>()?;
182
183 Ok(Self::Trait {
184 items,
185 self_ident,
186 with_foreign,
187 callback_interface_only,
188 docstring,
189 args,
190 })
191 }
192
from_struct(item: syn::ItemStruct, attr_args: TokenStream) -> syn::Result<Self>193 fn from_struct(item: syn::ItemStruct, attr_args: TokenStream) -> syn::Result<Self> {
194 let args: ExportStructArgs = syn::parse(attr_args)?;
195 let uniffi_traits: Vec<UniffiTraitDiscriminants> = args.traits.into_iter().collect();
196 if uniffi_traits.is_empty() {
197 Err(syn::Error::new(Span::call_site(),
198 "uniffi::export on a struct must supply a builtin trait name. Did you mean `#[derive(uniffi::Object)]`?"
199 ))
200 } else {
201 Ok(Self::Struct {
202 self_ident: item.ident,
203 uniffi_traits,
204 })
205 }
206 }
207 }
208
209 pub(super) enum ImplItem {
210 Constructor(FnSignature),
211 Method(FnSignature),
212 }
213
type_as_type_path(ty: &syn::Type) -> syn::Result<&syn::TypePath>214 fn type_as_type_path(ty: &syn::Type) -> syn::Result<&syn::TypePath> {
215 match ty {
216 syn::Type::Group(g) => type_as_type_path(&g.elem),
217 syn::Type::Paren(p) => type_as_type_path(&p.elem),
218 syn::Type::Path(p) => Ok(p),
219 _ => Err(type_not_supported(ty)),
220 }
221 }
222
type_not_supported(ty: &impl ToTokens) -> syn::Error223 fn type_not_supported(ty: &impl ToTokens) -> syn::Error {
224 syn::Error::new_spanned(
225 ty,
226 "this type is not currently supported by uniffi::export in this position",
227 )
228 }
229