summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_macros/src/export/attributes.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/rust/uniffi_macros/src/export/attributes.rs306
1 files changed, 268 insertions, 38 deletions
diff --git a/third_party/rust/uniffi_macros/src/export/attributes.rs b/third_party/rust/uniffi_macros/src/export/attributes.rs
index c3edcd5920..be7e8902e4 100644
--- a/third_party/rust/uniffi_macros/src/export/attributes.rs
+++ b/third_party/rust/uniffi_macros/src/export/attributes.rs
@@ -1,31 +1,33 @@
-use crate::util::{either_attribute_arg, kw, parse_comma_separated, UniffiAttributeArgs};
+use std::collections::{HashMap, HashSet};
+
+use crate::{
+ default::DefaultValue,
+ util::{either_attribute_arg, kw, parse_comma_separated, UniffiAttributeArgs},
+};
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{
+ parenthesized,
parse::{Parse, ParseStream},
- Attribute, LitStr, Meta, PathArguments, PathSegment, Token,
+ Attribute, Ident, LitStr, Meta, PathArguments, PathSegment, Token,
};
+use uniffi_meta::UniffiTraitDiscriminants;
#[derive(Default)]
-pub struct ExportAttributeArguments {
+pub struct ExportTraitArgs {
pub(crate) async_runtime: Option<AsyncRuntime>,
pub(crate) callback_interface: Option<kw::callback_interface>,
- pub(crate) constructor: Option<kw::constructor>,
- // tried to make this a vec but that got messy quickly...
- pub(crate) trait_debug: Option<kw::Debug>,
- pub(crate) trait_display: Option<kw::Display>,
- pub(crate) trait_hash: Option<kw::Hash>,
- pub(crate) trait_eq: Option<kw::Eq>,
+ pub(crate) with_foreign: Option<kw::with_foreign>,
}
-impl Parse for ExportAttributeArguments {
+impl Parse for ExportTraitArgs {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
parse_comma_separated(input)
}
}
-impl UniffiAttributeArgs for ExportAttributeArguments {
+impl UniffiAttributeArgs for ExportTraitArgs {
fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::async_runtime) {
@@ -40,52 +42,175 @@ impl UniffiAttributeArgs for ExportAttributeArguments {
callback_interface: input.parse()?,
..Self::default()
})
- } else if lookahead.peek(kw::constructor) {
+ } else if lookahead.peek(kw::with_foreign) {
Ok(Self {
- constructor: input.parse()?,
+ with_foreign: input.parse()?,
..Self::default()
})
- } else if lookahead.peek(kw::Debug) {
+ } else {
+ Ok(Self::default())
+ }
+ }
+
+ fn merge(self, other: Self) -> syn::Result<Self> {
+ let merged = Self {
+ async_runtime: either_attribute_arg(self.async_runtime, other.async_runtime)?,
+ callback_interface: either_attribute_arg(
+ self.callback_interface,
+ other.callback_interface,
+ )?,
+ with_foreign: either_attribute_arg(self.with_foreign, other.with_foreign)?,
+ };
+ if merged.callback_interface.is_some() && merged.with_foreign.is_some() {
+ return Err(syn::Error::new(
+ merged.callback_interface.unwrap().span,
+ "`callback_interface` and `with_foreign` are mutually exclusive",
+ ));
+ }
+ Ok(merged)
+ }
+}
+
+#[derive(Clone, Default)]
+pub struct ExportFnArgs {
+ pub(crate) async_runtime: Option<AsyncRuntime>,
+ pub(crate) name: Option<String>,
+ pub(crate) defaults: DefaultMap,
+}
+
+impl Parse for ExportFnArgs {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ parse_comma_separated(input)
+ }
+}
+
+impl UniffiAttributeArgs for ExportFnArgs {
+ fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
+ let lookahead = input.lookahead1();
+ if lookahead.peek(kw::async_runtime) {
+ let _: kw::async_runtime = input.parse()?;
+ let _: Token![=] = input.parse()?;
Ok(Self {
- trait_debug: input.parse()?,
+ async_runtime: Some(input.parse()?),
..Self::default()
})
- } else if lookahead.peek(kw::Display) {
+ } else if lookahead.peek(kw::name) {
+ let _: kw::name = input.parse()?;
+ let _: Token![=] = input.parse()?;
+ let name = Some(input.parse::<LitStr>()?.value());
Ok(Self {
- trait_display: input.parse()?,
+ name,
..Self::default()
})
- } else if lookahead.peek(kw::Hash) {
+ } else if lookahead.peek(kw::default) {
Ok(Self {
- trait_hash: input.parse()?,
+ defaults: DefaultMap::parse(input)?,
..Self::default()
})
- } else if lookahead.peek(kw::Eq) {
+ } else {
+ Err(syn::Error::new(
+ input.span(),
+ format!("uniffi::export attribute `{input}` is not supported here."),
+ ))
+ }
+ }
+
+ fn merge(self, other: Self) -> syn::Result<Self> {
+ Ok(Self {
+ async_runtime: either_attribute_arg(self.async_runtime, other.async_runtime)?,
+ name: either_attribute_arg(self.name, other.name)?,
+ defaults: self.defaults.merge(other.defaults),
+ })
+ }
+}
+
+#[derive(Default)]
+pub struct ExportImplArgs {
+ pub(crate) async_runtime: Option<AsyncRuntime>,
+}
+
+impl Parse for ExportImplArgs {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ parse_comma_separated(input)
+ }
+}
+
+impl UniffiAttributeArgs for ExportImplArgs {
+ fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
+ let lookahead = input.lookahead1();
+ if lookahead.peek(kw::async_runtime) {
+ let _: kw::async_runtime = input.parse()?;
+ let _: Token![=] = input.parse()?;
Ok(Self {
- trait_eq: input.parse()?,
- ..Self::default()
+ async_runtime: Some(input.parse()?),
})
} else {
- Ok(Self::default())
+ Err(syn::Error::new(
+ input.span(),
+ format!("uniffi::export attribute `{input}` is not supported here."),
+ ))
}
}
fn merge(self, other: Self) -> syn::Result<Self> {
Ok(Self {
async_runtime: either_attribute_arg(self.async_runtime, other.async_runtime)?,
- callback_interface: either_attribute_arg(
- self.callback_interface,
- other.callback_interface,
- )?,
- constructor: either_attribute_arg(self.constructor, other.constructor)?,
- trait_debug: either_attribute_arg(self.trait_debug, other.trait_debug)?,
- trait_display: either_attribute_arg(self.trait_display, other.trait_display)?,
- trait_hash: either_attribute_arg(self.trait_hash, other.trait_hash)?,
- trait_eq: either_attribute_arg(self.trait_eq, other.trait_eq)?,
})
}
}
+#[derive(Default)]
+pub struct ExportStructArgs {
+ pub(crate) traits: HashSet<UniffiTraitDiscriminants>,
+}
+
+impl Parse for ExportStructArgs {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ parse_comma_separated(input)
+ }
+}
+
+impl UniffiAttributeArgs for ExportStructArgs {
+ fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
+ let lookahead = input.lookahead1();
+ if lookahead.peek(kw::Debug) {
+ input.parse::<Option<kw::Debug>>()?;
+ Ok(Self {
+ traits: HashSet::from([UniffiTraitDiscriminants::Debug]),
+ })
+ } else if lookahead.peek(kw::Display) {
+ input.parse::<Option<kw::Display>>()?;
+ Ok(Self {
+ traits: HashSet::from([UniffiTraitDiscriminants::Display]),
+ })
+ } else if lookahead.peek(kw::Hash) {
+ input.parse::<Option<kw::Hash>>()?;
+ Ok(Self {
+ traits: HashSet::from([UniffiTraitDiscriminants::Hash]),
+ })
+ } else if lookahead.peek(kw::Eq) {
+ input.parse::<Option<kw::Eq>>()?;
+ Ok(Self {
+ traits: HashSet::from([UniffiTraitDiscriminants::Eq]),
+ })
+ } else {
+ Err(syn::Error::new(
+ input.span(),
+ format!(
+ "uniffi::export struct attributes must be builtin trait names; `{input}` is invalid"
+ ),
+ ))
+ }
+ }
+
+ fn merge(self, other: Self) -> syn::Result<Self> {
+ let mut traits = self.traits;
+ traits.extend(other.traits);
+ Ok(Self { traits })
+ }
+}
+
+#[derive(Clone)]
pub(crate) enum AsyncRuntime {
Tokio(LitStr),
}
@@ -111,9 +236,57 @@ 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<String>,
+ pub(crate) defaults: DefaultMap,
+}
+
+impl Parse for ExportedImplFnArgs {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ parse_comma_separated(input)
+ }
+}
+
+impl UniffiAttributeArgs for ExportedImplFnArgs {
+ fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
+ let lookahead = input.lookahead1();
+ if lookahead.peek(kw::name) {
+ let _: kw::name = input.parse()?;
+ let _: Token![=] = input.parse()?;
+ let name = Some(input.parse::<LitStr>()?.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<Self> {
+ 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,
}
impl ExportedImplFnAttributes {
@@ -130,12 +303,11 @@ impl ExportedImplFnAttributes {
}
ensure_no_path_args(fst)?;
- if let Meta::List(_) | Meta::NameValue(_) = &attr.meta {
- return Err(syn::Error::new_spanned(
- &attr.meta,
- "attribute arguments are not currently recognized in this position",
- ));
- }
+ let args = match &attr.meta {
+ Meta::List(_) => attr.parse_args::<ExportedImplFnArgs>()?,
+ _ => Default::default(),
+ };
+ this.args = args;
if segs.len() != 2 {
return Err(syn::Error::new_spanned(
@@ -156,6 +328,14 @@ impl ExportedImplFnAttributes {
}
this.constructor = true;
}
+ "method" => {
+ if this.constructor {
+ return Err(syn::Error::new_spanned(
+ attr,
+ "confused constructor/method attributes",
+ ));
+ }
+ }
_ => return Err(syn::Error::new_spanned(snd, "unknown uniffi attribute")),
}
}
@@ -171,3 +351,53 @@ fn ensure_no_path_args(seg: &PathSegment) -> syn::Result<()> {
Err(syn::Error::new_spanned(&seg.arguments, "unexpected syntax"))
}
}
+
+/// Maps arguments to defaults for functions
+#[derive(Clone, Default)]
+pub struct DefaultMap {
+ map: HashMap<Ident, DefaultValue>,
+}
+
+impl DefaultMap {
+ pub fn merge(self, other: Self) -> Self {
+ let mut map = self.map;
+ map.extend(other.map);
+ Self { map }
+ }
+
+ pub fn remove(&mut self, ident: &Ident) -> Option<DefaultValue> {
+ self.map.remove(ident)
+ }
+
+ pub fn idents(&self) -> Vec<&Ident> {
+ self.map.keys().collect()
+ }
+}
+
+impl Parse for DefaultMap {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let _: kw::default = input.parse()?;
+ let content;
+ let _ = parenthesized!(content in input);
+ let pairs = content.parse_terminated(DefaultPair::parse, Token![,])?;
+ Ok(Self {
+ map: pairs.into_iter().map(|p| (p.name, p.value)).collect(),
+ })
+ }
+}
+
+pub struct DefaultPair {
+ pub name: Ident,
+ pub eq_token: Token![=],
+ pub value: DefaultValue,
+}
+
+impl Parse for DefaultPair {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ Ok(Self {
+ name: input.parse()?,
+ eq_token: input.parse()?,
+ value: input.parse()?,
+ })
+ }
+}