diff --git a/CHANGELOG.md b/CHANGELOG.md index f602f042e0..312007535d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ ## [[UnreleasedUniFFIVersion]] (backend crates: [[UnreleasedBackendVersion]]) - (_[[ReleaseDate]]_) +### What's changed + +- The async runtime can be specified for constructors/methods, this will override the runtime specified at the impl block level. + [All changes in [[UnreleasedUniFFIVersion]]](https://github.com/mozilla/uniffi-rs/compare/v0.27.0...HEAD). ## v0.27.0 (backend crates: v0.27.0) - (_2024-03-26_) diff --git a/fixtures/uitests/tests/ui/export_attrs.stderr b/fixtures/uitests/tests/ui/export_attrs.stderr index 43dcda3e5e..71a054d291 100644 --- a/fixtures/uitests/tests/ui/export_attrs.stderr +++ b/fixtures/uitests/tests/ui/export_attrs.stderr @@ -18,7 +18,7 @@ error: uniffi::export attribute `callback_interface` is not supported here. 14 | #[uniffi::export(callback_interface)] | ^^^^^^^^^^^^^^^^^^ -error: uniffi::export attribute `with_foreign` is not supported here. +error: attribute `with_foreign` is not supported here. --> tests/ui/export_attrs.rs:17:18 | 17 | #[uniffi::export(with_foreign)] @@ -42,13 +42,13 @@ error: attribute arguments are not currently recognized in this position 34 | #[uniffi(flat_error)] | ^^^^^^^^^^ -error: uniffi::constructor/method attribute `foo = bar` is not supported here. +error: attribute `foo = bar` is not supported here. --> tests/ui/export_attrs.rs:44:27 | 44 | #[uniffi::constructor(foo = bar)] | ^^^ -error: uniffi::constructor/method attribute `foo` is not supported here. +error: attribute `foo` is not supported here. --> tests/ui/export_attrs.rs:50:22 | 50 | #[uniffi::method(foo)] diff --git a/uniffi_macros/src/export.rs b/uniffi_macros/src/export.rs index 41657a639e..3843544b21 100644 --- a/uniffi_macros/src/export.rs +++ b/uniffi_macros/src/export.rs @@ -20,7 +20,7 @@ use self::{ }, }; use crate::util::{ident_to_string, mod_path}; -pub use attributes::{DefaultMap, ExportFnArgs, ExportedImplFnArgs}; +pub use attributes::{AsyncRuntime, DefaultMap, ExportFnArgs}; pub use callback_interface::ffi_converter_callback_interface_impl; // TODO(jplatte): Ensure no generics, … @@ -42,7 +42,7 @@ pub(crate) fn expand_export( match metadata { ExportItem::Function { sig, args } => { - gen_fn_scaffolding(sig, &args.async_runtime, udl_mode) + gen_fn_scaffolding(sig, args.async_runtime.as_ref(), udl_mode) } ExportItem::Impl { items, @@ -65,10 +65,14 @@ pub(crate) fn expand_export( .into_iter() .map(|item| match item { ImplItem::Constructor(sig) => { - gen_constructor_scaffolding(sig, &args.async_runtime, udl_mode) + let async_runtime = + sig.async_runtime.clone().or(args.async_runtime.clone()); + gen_constructor_scaffolding(sig, async_runtime.as_ref(), udl_mode) } ImplItem::Method(sig) => { - gen_method_scaffolding(sig, &args.async_runtime, udl_mode) + let async_runtime = + sig.async_runtime.clone().or(args.async_runtime.clone()); + gen_method_scaffolding(sig, async_runtime.as_ref(), udl_mode) } }) .collect::>()?; diff --git a/uniffi_macros/src/export/attributes.rs b/uniffi_macros/src/export/attributes.rs index be7e8902e4..c0d508e6c7 100644 --- a/uniffi_macros/src/export/attributes.rs +++ b/uniffi_macros/src/export/attributes.rs @@ -71,6 +71,9 @@ impl UniffiAttributeArgs for ExportTraitArgs { } } +/// Attribute arguments for function +/// +/// This includes top-level functions, constructors, and methods. #[derive(Clone, Default)] pub struct ExportFnArgs { pub(crate) async_runtime: Option, @@ -110,7 +113,7 @@ impl UniffiAttributeArgs for ExportFnArgs { } else { Err(syn::Error::new( input.span(), - format!("uniffi::export attribute `{input}` is not supported here."), + format!("attribute `{input}` is not supported here."), )) } } @@ -211,7 +214,7 @@ impl UniffiAttributeArgs for ExportStructArgs { } #[derive(Clone)] -pub(crate) enum AsyncRuntime { +pub enum AsyncRuntime { Tokio(LitStr), } @@ -236,57 +239,10 @@ impl ToTokens for AsyncRuntime { } } -/// Arguments for function inside an impl block -/// -/// This stores the parsed arguments for `uniffi::constructor` and `uniffi::method` -#[derive(Clone, Default)] -pub struct ExportedImplFnArgs { - pub(crate) name: Option, - pub(crate) defaults: DefaultMap, -} - -impl Parse for ExportedImplFnArgs { - fn parse(input: ParseStream<'_>) -> syn::Result { - parse_comma_separated(input) - } -} - -impl UniffiAttributeArgs for ExportedImplFnArgs { - fn parse_one(input: ParseStream<'_>) -> syn::Result { - let lookahead = input.lookahead1(); - if lookahead.peek(kw::name) { - let _: kw::name = input.parse()?; - let _: Token![=] = input.parse()?; - let name = Some(input.parse::()?.value()); - Ok(Self { - name, - ..Self::default() - }) - } else if lookahead.peek(kw::default) { - Ok(Self { - defaults: DefaultMap::parse(input)?, - ..Self::default() - }) - } else { - Err(syn::Error::new( - input.span(), - format!("uniffi::constructor/method attribute `{input}` is not supported here."), - )) - } - } - - fn merge(self, other: Self) -> syn::Result { - Ok(Self { - name: either_attribute_arg(self.name, other.name)?, - defaults: self.defaults.merge(other.defaults), - }) - } -} - #[derive(Default)] pub(super) struct ExportedImplFnAttributes { pub constructor: bool, - pub args: ExportedImplFnArgs, + pub args: ExportFnArgs, } impl ExportedImplFnAttributes { @@ -304,7 +260,7 @@ impl ExportedImplFnAttributes { ensure_no_path_args(fst)?; let args = match &attr.meta { - Meta::List(_) => attr.parse_args::()?, + Meta::List(_) => attr.parse_args::()?, _ => Default::default(), }; this.args = args; diff --git a/uniffi_macros/src/export/item.rs b/uniffi_macros/src/export/item.rs index da3c9455c8..990fc9fecd 100644 --- a/uniffi_macros/src/export/item.rs +++ b/uniffi_macros/src/export/item.rs @@ -8,8 +8,7 @@ use proc_macro2::{Ident, Span}; use quote::ToTokens; use super::attributes::{ - ExportFnArgs, ExportImplArgs, ExportStructArgs, ExportTraitArgs, ExportedImplFnArgs, - ExportedImplFnAttributes, + ExportFnArgs, ExportImplArgs, ExportStructArgs, ExportTraitArgs, ExportedImplFnAttributes, }; use crate::util::extract_docstring; use uniffi_meta::UniffiTraitDiscriminants; @@ -170,7 +169,7 @@ impl ExportItem { ImplItem::Method(FnSignature::new_trait_method( self_ident.clone(), tim.sig, - ExportedImplFnArgs::default(), + ExportFnArgs::default(), i as u32, docstring, )?) diff --git a/uniffi_macros/src/export/scaffolding.rs b/uniffi_macros/src/export/scaffolding.rs index fa7b61deca..58b8d83a35 100644 --- a/uniffi_macros/src/export/scaffolding.rs +++ b/uniffi_macros/src/export/scaffolding.rs @@ -11,7 +11,7 @@ use crate::fnsig::{FnKind, FnSignature}; pub(super) fn gen_fn_scaffolding( sig: FnSignature, - ar: &Option, + ar: Option<&AsyncRuntime>, udl_mode: bool, ) -> syn::Result { if sig.receiver.is_some() { @@ -41,7 +41,7 @@ pub(super) fn gen_fn_scaffolding( pub(super) fn gen_constructor_scaffolding( sig: FnSignature, - ar: &Option, + ar: Option<&AsyncRuntime>, udl_mode: bool, ) -> syn::Result { if sig.receiver.is_some() { @@ -63,7 +63,7 @@ pub(super) fn gen_constructor_scaffolding( pub(super) fn gen_method_scaffolding( sig: FnSignature, - ar: &Option, + ar: Option<&AsyncRuntime>, udl_mode: bool, ) -> syn::Result { let scaffolding_func = if sig.receiver.is_none() { @@ -210,7 +210,7 @@ impl ScaffoldingBits { /// `rust_fn` is the Rust function to call. pub(super) fn gen_ffi_function( sig: &FnSignature, - ar: &Option, + ar: Option<&AsyncRuntime>, udl_mode: bool, ) -> syn::Result { let ScaffoldingBits { diff --git a/uniffi_macros/src/export/trait_interface.rs b/uniffi_macros/src/export/trait_interface.rs index 83587ae386..841d50786b 100644 --- a/uniffi_macros/src/export/trait_interface.rs +++ b/uniffi_macros/src/export/trait_interface.rs @@ -84,7 +84,7 @@ pub(super) fn gen_trait_scaffolding( let impl_tokens: TokenStream = items .into_iter() .map(|item| match item { - ImplItem::Method(sig) => gen_method_scaffolding(sig, &None, udl_mode), + ImplItem::Method(sig) => gen_method_scaffolding(sig, None, udl_mode), _ => unreachable!("traits have no constructors"), }) .collect::>()?; diff --git a/uniffi_macros/src/export/utrait.rs b/uniffi_macros/src/export/utrait.rs index 9007ae2c56..7663348931 100644 --- a/uniffi_macros/src/export/utrait.rs +++ b/uniffi_macros/src/export/utrait.rs @@ -7,7 +7,7 @@ use quote::quote; use syn::ext::IdentExt; use super::gen_ffi_function; -use crate::export::ExportedImplFnArgs; +use crate::export::ExportFnArgs; use crate::fnsig::FnSignature; use crate::util::extract_docstring; use uniffi_meta::UniffiTraitDiscriminants; @@ -165,17 +165,17 @@ fn process_uniffi_trait_method( &FnSignature::new_method( self_ident.clone(), item.sig.clone(), - ExportedImplFnArgs::default(), + ExportFnArgs::default(), docstring.clone(), )?, - &None, + None, udl_mode, )?; // metadata for the method, which will be packed inside metadata for the trait. let method_meta = FnSignature::new_method( self_ident.clone(), item.sig, - ExportedImplFnArgs::default(), + ExportFnArgs::default(), docstring, )? .metadata_expr()?; diff --git a/uniffi_macros/src/fnsig.rs b/uniffi_macros/src/fnsig.rs index 9c59125207..712c0e9cd5 100644 --- a/uniffi_macros/src/fnsig.rs +++ b/uniffi_macros/src/fnsig.rs @@ -4,7 +4,7 @@ use crate::{ default::{default_value_metadata_calls, DefaultValue}, - export::{DefaultMap, ExportFnArgs, ExportedImplFnArgs}, + export::{AsyncRuntime, DefaultMap, ExportFnArgs}, util::{create_metadata_items, ident_to_string, mod_path, try_metadata_value_from_usize}, }; use proc_macro2::{Span, TokenStream}; @@ -20,6 +20,7 @@ pub(crate) struct FnSignature { // The foreign name for this function, usually == ident. pub name: String, pub is_async: bool, + pub async_runtime: Option, pub receiver: Option, pub args: Vec, pub return_ty: TokenStream, @@ -36,51 +37,38 @@ impl FnSignature { args: ExportFnArgs, docstring: String, ) -> syn::Result { - Self::new(FnKind::Function, sig, args.name, args.defaults, docstring) + Self::new(FnKind::Function, sig, args, docstring) } pub(crate) fn new_method( self_ident: Ident, sig: syn::Signature, - args: ExportedImplFnArgs, + args: ExportFnArgs, docstring: String, ) -> syn::Result { - Self::new( - FnKind::Method { self_ident }, - sig, - args.name, - args.defaults, - docstring, - ) + Self::new(FnKind::Method { self_ident }, sig, args, docstring) } pub(crate) fn new_constructor( self_ident: Ident, sig: syn::Signature, - args: ExportedImplFnArgs, + args: ExportFnArgs, docstring: String, ) -> syn::Result { - Self::new( - FnKind::Constructor { self_ident }, - sig, - args.name, - args.defaults, - docstring, - ) + Self::new(FnKind::Constructor { self_ident }, sig, args, docstring) } pub(crate) fn new_trait_method( self_ident: Ident, sig: syn::Signature, - args: ExportedImplFnArgs, + args: ExportFnArgs, index: u32, docstring: String, ) -> syn::Result { Self::new( FnKind::TraitMethod { self_ident, index }, sig, - args.name, - args.defaults, + args, docstring, ) } @@ -88,8 +76,7 @@ impl FnSignature { pub(crate) fn new( kind: FnKind, sig: syn::Signature, - name: Option, - mut defaults: DefaultMap, + mut export_fn_args: ExportFnArgs, docstring: String, ) -> syn::Result { let span = sig.span(); @@ -104,7 +91,7 @@ impl FnSignature { let mut input_iter = sig .inputs .into_iter() - .map(|a| Arg::new(a, &mut defaults)) + .map(|a| Arg::new(a, &mut export_fn_args.defaults)) .peekable(); let receiver = input_iter @@ -127,20 +114,30 @@ impl FnSignature { }) .collect::>>()?; - if let Some(ident) = defaults.idents().first() { + if let Some(ident) = export_fn_args.defaults.idents().first() { return Err(syn::Error::new( ident.span(), format!("Unknown default argument: {}", ident), )); } + if !is_async && export_fn_args.async_runtime.is_some() { + return Err(syn::Error::new( + export_fn_args.async_runtime.span(), + "Function not async".to_string(), + )); + } + Ok(Self { kind, span, mod_path: mod_path()?, - name: name.unwrap_or_else(|| ident_to_string(&ident)), + name: export_fn_args + .name + .unwrap_or_else(|| ident_to_string(&ident)), ident, is_async, + async_runtime: export_fn_args.async_runtime, receiver, args, return_ty: output,