/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; use syn::{visit_mut::VisitMut, Item, Type}; mod attributes; mod callback_interface; mod item; mod scaffolding; mod trait_interface; mod utrait; use self::{ item::{ExportItem, ImplItem}, scaffolding::{ gen_constructor_scaffolding, gen_ffi_function, gen_fn_scaffolding, gen_method_scaffolding, }, }; use crate::util::{ident_to_string, mod_path}; pub use attributes::{DefaultMap, ExportFnArgs, ExportedImplFnArgs}; pub use callback_interface::ffi_converter_callback_interface_impl; // TODO(jplatte): Ensure no generics, … // TODO(jplatte): Aggregate errors instead of short-circuiting, wherever possible pub(crate) fn expand_export( mut item: Item, all_args: proc_macro::TokenStream, udl_mode: bool, ) -> syn::Result { let mod_path = mod_path()?; // If the input is an `impl` block, rewrite any uses of the `Self` type // alias to the actual type, so we don't have to special-case it in the // metadata collection or scaffolding code generation (which generates // new functions outside of the `impl`). rewrite_self_type(&mut item); let metadata = ExportItem::new(item, all_args)?; match metadata { ExportItem::Function { sig, args } => { gen_fn_scaffolding(sig, &args.async_runtime, udl_mode) } ExportItem::Impl { items, self_ident, args, } => { if let Some(rt) = &args.async_runtime { if items .iter() .all(|item| !matches!(item, ImplItem::Method(sig) if sig.is_async)) { return Err(syn::Error::new_spanned( rt, "no async methods in this impl block", )); } } let item_tokens: TokenStream = items .into_iter() .map(|item| match item { ImplItem::Constructor(sig) => { gen_constructor_scaffolding(sig, &args.async_runtime, udl_mode) } ImplItem::Method(sig) => { gen_method_scaffolding(sig, &args.async_runtime, udl_mode) } }) .collect::>()?; Ok(quote_spanned! { self_ident.span() => #item_tokens }) } ExportItem::Trait { items, self_ident, with_foreign, callback_interface_only: false, docstring, args, } => trait_interface::gen_trait_scaffolding( &mod_path, args, self_ident, items, udl_mode, with_foreign, docstring, ), ExportItem::Trait { items, self_ident, callback_interface_only: true, docstring, .. } => { let trait_name = ident_to_string(&self_ident); let trait_impl_ident = callback_interface::trait_impl_ident(&trait_name); let trait_impl = callback_interface::trait_impl(&mod_path, &self_ident, &items) .unwrap_or_else(|e| e.into_compile_error()); let metadata_items = (!udl_mode).then(|| { let items = callback_interface::metadata_items(&self_ident, &items, &mod_path, docstring) .unwrap_or_else(|e| vec![e.into_compile_error()]); quote! { #(#items)* } }); let ffi_converter_tokens = ffi_converter_callback_interface_impl(&self_ident, &trait_impl_ident, udl_mode); Ok(quote! { #trait_impl #ffi_converter_tokens #metadata_items }) } ExportItem::Struct { self_ident, uniffi_traits, .. } => { assert!(!udl_mode); utrait::expand_uniffi_trait_export(self_ident, uniffi_traits) } } } /// Rewrite Self type alias usage in an impl block to the type itself. /// /// For example, /// /// ```ignore /// impl some::module::Foo { /// fn method( /// self: Arc, /// arg: Option>, /// ) -> Result { /// todo!() /// } /// } /// ``` /// /// will be rewritten to /// /// ```ignore /// impl some::module::Foo { /// fn method( /// self: Arc, /// arg: Option>, /// ) -> Result { /// todo!() /// } /// } /// ``` pub fn rewrite_self_type(item: &mut Item) { let item = match item { Item::Impl(i) => i, _ => return, }; struct RewriteSelfVisitor<'a>(&'a Type); impl<'a> VisitMut for RewriteSelfVisitor<'a> { fn visit_type_mut(&mut self, i: &mut Type) { match i { Type::Path(p) if p.qself.is_none() && p.path.is_ident("Self") => { *i = self.0.clone(); } _ => syn::visit_mut::visit_type_mut(self, i), } } } let mut visitor = RewriteSelfVisitor(&item.self_ty); for item in &mut item.items { visitor.visit_impl_item_mut(item); } }