summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_udl/src/attributes.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/rust/uniffi_udl/src/attributes.rs199
1 files changed, 182 insertions, 17 deletions
diff --git a/third_party/rust/uniffi_udl/src/attributes.rs b/third_party/rust/uniffi_udl/src/attributes.rs
index f06b4f29c1..7bb05808c4 100644
--- a/third_party/rust/uniffi_udl/src/attributes.rs
+++ b/third_party/rust/uniffi_udl/src/attributes.rs
@@ -37,10 +37,29 @@ pub(super) enum Attribute {
kind: ExternalKind,
export: bool,
},
+ Rust {
+ kind: RustKind,
+ },
// Custom type on the scaffolding side
Custom,
// The interface described is implemented as a trait.
Trait,
+ // Modifies `Trait` to enable foreign implementations (callback interfaces)
+ WithForeign,
+ Async,
+ NonExhaustive,
+}
+
+// A type defined in Rust via procmacros but which should be available
+// in UDL.
+#[derive(Debug, Copy, Clone, Checksum)]
+pub(super) enum RustKind {
+ Object,
+ CallbackTrait,
+ Trait,
+ Record,
+ Enum,
+ CallbackInterface,
}
impl Attribute {
@@ -67,6 +86,9 @@ impl TryFrom<&weedle::attribute::ExtendedAttribute<'_>> for Attribute {
"Error" => Ok(Attribute::Error),
"Custom" => Ok(Attribute::Custom),
"Trait" => Ok(Attribute::Trait),
+ "WithForeign" => Ok(Attribute::WithForeign),
+ "Async" => Ok(Attribute::Async),
+ "NonExhaustive" => Ok(Attribute::NonExhaustive),
_ => anyhow::bail!("ExtendedAttributeNoArgs not supported: {:?}", (attr.0).0),
},
// Matches assignment-style attributes like ["Throws=Error"]
@@ -95,6 +117,19 @@ impl TryFrom<&weedle::attribute::ExtendedAttribute<'_>> for Attribute {
kind: ExternalKind::Interface,
export: true,
}),
+ "ExternalTrait" => Ok(Attribute::External {
+ crate_name: name_from_id_or_string(&identity.rhs),
+ kind: ExternalKind::Trait,
+ export: false,
+ }),
+ "ExternalTraitExport" => Ok(Attribute::External {
+ crate_name: name_from_id_or_string(&identity.rhs),
+ kind: ExternalKind::Trait,
+ export: true,
+ }),
+ "Rust" => Ok(Attribute::Rust {
+ kind: rust_kind_from_id_or_string(&identity.rhs)?,
+ }),
_ => anyhow::bail!(
"Attribute identity Identifier not supported: {:?}",
identity.lhs_identifier.0
@@ -130,6 +165,26 @@ fn name_from_id_or_string(nm: &weedle::attribute::IdentifierOrString<'_>) -> Str
}
}
+fn rust_kind_from_id_or_string(nm: &weedle::attribute::IdentifierOrString<'_>) -> Result<RustKind> {
+ Ok(match nm {
+ weedle::attribute::IdentifierOrString::String(str_lit) => match str_lit.0 {
+ // support names which match either procmacro or udl
+ "interface" => RustKind::Object,
+ "object" => RustKind::Object,
+ "record" => RustKind::Record,
+ "dictionary" => RustKind::Record,
+ "enum" => RustKind::Enum,
+ "trait" => RustKind::Trait,
+ "callback" => RustKind::CallbackInterface,
+ "trait_with_foreign" => RustKind::CallbackTrait,
+ _ => anyhow::bail!("Unknown `[Rust=]` kind {:?}", str_lit.0),
+ },
+ weedle::attribute::IdentifierOrString::Identifier(_) => {
+ anyhow::bail!("Expected string attribute value, got identifier")
+ }
+ })
+}
+
/// Parse a weedle `ExtendedAttributeList` into a list of `Attribute`s,
/// erroring out on duplicates.
fn parse_attributes<F>(
@@ -161,7 +216,6 @@ where
}
/// Attributes that can be attached to an `enum` definition in the UDL.
-/// There's only one case here: using `[Error]` to mark an enum as an error class.
#[derive(Debug, Clone, Checksum, Default)]
pub(super) struct EnumAttributes(Vec<Attribute>);
@@ -169,6 +223,12 @@ impl EnumAttributes {
pub fn contains_error_attr(&self) -> bool {
self.0.iter().any(|attr| attr.is_error())
}
+
+ pub fn contains_non_exhaustive_attr(&self) -> bool {
+ self.0
+ .iter()
+ .any(|attr| matches!(attr, Attribute::NonExhaustive))
+ }
}
impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for EnumAttributes {
@@ -178,6 +238,10 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for EnumAttributes {
) -> Result<Self, Self::Error> {
let attrs = parse_attributes(weedle_attributes, |attr| match attr {
Attribute::Error => Ok(()),
+ Attribute::NonExhaustive => Ok(()),
+ // Allow `[Enum]`, since we may be parsing an attribute list from an interface with the
+ // `[Enum]` attribute.
+ Attribute::Enum => Ok(()),
_ => bail!(format!("{attr:?} not supported for enums")),
})?;
Ok(Self(attrs))
@@ -196,8 +260,9 @@ impl<T: TryInto<EnumAttributes, Error = anyhow::Error>> TryFrom<Option<T>> for E
/// Represents UDL attributes that might appear on a function.
///
-/// This supports the `[Throws=ErrorName]` attribute for functions that
-/// can produce an error.
+/// This supports:
+/// * `[Throws=ErrorName]` attribute for functions that can produce an error.
+/// * `[Async] for async functions
#[derive(Debug, Clone, Checksum, Default)]
pub(super) struct FunctionAttributes(Vec<Attribute>);
@@ -210,6 +275,10 @@ impl FunctionAttributes {
_ => None,
})
}
+
+ pub(super) fn is_async(&self) -> bool {
+ self.0.iter().any(|attr| matches!(attr, Attribute::Async))
+ }
}
impl FromIterator<Attribute> for FunctionAttributes {
@@ -224,7 +293,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for FunctionAttribut
weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
) -> Result<Self, Self::Error> {
let attrs = parse_attributes(weedle_attributes, |attr| match attr {
- Attribute::Throws(_) => Ok(()),
+ Attribute::Throws(_) | Attribute::Async => Ok(()),
_ => bail!(format!("{attr:?} not supported for functions")),
})?;
Ok(Self(attrs))
@@ -294,12 +363,25 @@ impl InterfaceAttributes {
self.0.iter().any(|attr| attr.is_error())
}
- pub fn object_impl(&self) -> ObjectImpl {
- if self.0.iter().any(|attr| matches!(attr, Attribute::Trait)) {
- ObjectImpl::Trait
- } else {
- ObjectImpl::Struct
- }
+ pub fn contains_trait(&self) -> bool {
+ self.0.iter().any(|attr| matches!(attr, Attribute::Trait))
+ }
+
+ pub fn contains_with_foreign(&self) -> bool {
+ self.0
+ .iter()
+ .any(|attr| matches!(attr, Attribute::WithForeign))
+ }
+
+ pub fn object_impl(&self) -> Result<ObjectImpl> {
+ Ok(
+ match (self.contains_trait(), self.contains_with_foreign()) {
+ (true, true) => ObjectImpl::CallbackTrait,
+ (true, false) => ObjectImpl::Trait,
+ (false, false) => ObjectImpl::Struct,
+ (false, true) => bail!("WithForeign can't be specified without Trait"),
+ },
+ )
}
pub fn get_traits(&self) -> Vec<String> {
self.0
@@ -321,6 +403,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for InterfaceAttribu
Attribute::Enum => Ok(()),
Attribute::Error => Ok(()),
Attribute::Trait => Ok(()),
+ Attribute::WithForeign => Ok(()),
Attribute::Traits(_) => Ok(()),
_ => bail!(format!("{attr:?} not supported for interface definition")),
})?;
@@ -373,6 +456,10 @@ impl ConstructorAttributes {
_ => None,
})
}
+
+ pub(super) fn is_async(&self) -> bool {
+ self.0.iter().any(|attr| matches!(attr, Attribute::Async))
+ }
}
impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ConstructorAttributes {
@@ -383,6 +470,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ConstructorAttri
let attrs = parse_attributes(weedle_attributes, |attr| match attr {
Attribute::Throws(_) => Ok(()),
Attribute::Name(_) => Ok(()),
+ Attribute::Async => Ok(()),
_ => bail!(format!("{attr:?} not supported for constructors")),
})?;
Ok(Self(attrs))
@@ -406,6 +494,10 @@ impl MethodAttributes {
})
}
+ pub(super) fn is_async(&self) -> bool {
+ self.0.iter().any(|attr| matches!(attr, Attribute::Async))
+ }
+
pub(super) fn get_self_by_arc(&self) -> bool {
self.0
.iter()
@@ -425,8 +517,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for MethodAttributes
weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
) -> Result<Self, Self::Error> {
let attrs = parse_attributes(weedle_attributes, |attr| match attr {
- Attribute::SelfType(_) => Ok(()),
- Attribute::Throws(_) => Ok(()),
+ Attribute::SelfType(_) | Attribute::Throws(_) | Attribute::Async => Ok(()),
_ => bail!(format!("{attr:?} not supported for methods")),
})?;
Ok(Self(attrs))
@@ -498,10 +589,18 @@ impl TypedefAttributes {
})
}
+ pub(super) fn rust_kind(&self) -> Option<RustKind> {
+ self.0.iter().find_map(|attr| match attr {
+ Attribute::Rust { kind, .. } => Some(*kind),
+ _ => None,
+ })
+ }
+
pub(super) fn external_tagged(&self) -> Option<bool> {
// If it was "exported" via a proc-macro the FfiConverter was not tagged.
self.0.iter().find_map(|attr| match attr {
Attribute::External { export, .. } => Some(!*export),
+ Attribute::Rust { .. } => Some(false),
_ => None,
})
}
@@ -513,7 +612,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for TypedefAttribute
weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
) -> Result<Self, Self::Error> {
let attrs = parse_attributes(weedle_attributes, |attr| match attr {
- Attribute::External { .. } | Attribute::Custom => Ok(()),
+ Attribute::External { .. } | Attribute::Custom | Attribute::Rust { .. } => Ok(()),
_ => bail!(format!("{attr:?} not supported for typedefs")),
})?;
Ok(Self(attrs))
@@ -641,14 +740,22 @@ mod test {
}
#[test]
- fn test_throws_attribute() {
+ fn test_function_attributes() {
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap();
let attrs = FunctionAttributes::try_from(&node).unwrap();
assert!(matches!(attrs.get_throws_err(), Some("Error")));
+ assert!(!attrs.is_async());
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
let attrs = FunctionAttributes::try_from(&node).unwrap();
assert!(attrs.get_throws_err().is_none());
+ assert!(!attrs.is_async());
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Async]").unwrap();
+ let attrs = FunctionAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.get_throws_err(), Some("Error")));
+ assert!(attrs.is_async());
}
#[test]
@@ -673,22 +780,34 @@ mod test {
let attrs = MethodAttributes::try_from(&node).unwrap();
assert!(!attrs.get_self_by_arc());
assert!(matches!(attrs.get_throws_err(), Some("Error")));
+ assert!(!attrs.is_async());
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
let attrs = MethodAttributes::try_from(&node).unwrap();
assert!(!attrs.get_self_by_arc());
assert!(attrs.get_throws_err().is_none());
+ assert!(!attrs.is_async());
let (_, node) =
weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc, Throws=Error]").unwrap();
let attrs = MethodAttributes::try_from(&node).unwrap();
assert!(attrs.get_self_by_arc());
assert!(attrs.get_throws_err().is_some());
+ assert!(!attrs.is_async());
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc, Throws=Error, Async]")
+ .unwrap();
+ let attrs = MethodAttributes::try_from(&node).unwrap();
+ assert!(attrs.get_self_by_arc());
+ assert!(attrs.get_throws_err().is_some());
+ assert!(attrs.is_async());
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc]").unwrap();
let attrs = MethodAttributes::try_from(&node).unwrap();
assert!(attrs.get_self_by_arc());
assert!(attrs.get_throws_err().is_none());
+ assert!(!attrs.is_async());
}
#[test]
@@ -710,6 +829,11 @@ mod test {
let attrs = ConstructorAttributes::try_from(&node).unwrap();
assert!(matches!(attrs.get_throws_err(), Some("Error")));
assert!(matches!(attrs.get_name(), Some("MyFactory")));
+ assert!(!attrs.is_async());
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Async]").unwrap();
+ let attrs = ConstructorAttributes::try_from(&node).unwrap();
+ assert!(attrs.is_async());
}
#[test]
@@ -754,15 +878,24 @@ mod test {
fn test_trait_attribute() {
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap();
let attrs = InterfaceAttributes::try_from(&node).unwrap();
- assert_eq!(attrs.object_impl(), ObjectImpl::Trait);
+ assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::Trait);
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Trait, WithForeign]").unwrap();
+ let attrs = InterfaceAttributes::try_from(&node).unwrap();
+ assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::CallbackTrait);
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
let attrs = InterfaceAttributes::try_from(&node).unwrap();
- assert_eq!(attrs.object_impl(), ObjectImpl::Struct);
+ assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::Struct);
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[WithForeign]").unwrap();
+ let attrs = InterfaceAttributes::try_from(&node).unwrap();
+ assert!(attrs.object_impl().is_err())
}
#[test]
- fn test_enum_attribute() {
+ fn test_enum_attribute_on_interface() {
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum]").unwrap();
let attrs = InterfaceAttributes::try_from(&node).unwrap();
assert!(matches!(attrs.contains_enum_attr(), true));
@@ -783,6 +916,38 @@ mod test {
);
}
+ // Test parsing attributes for enum definitions
+ #[test]
+ fn test_enum_attributes() {
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Error, NonExhaustive]").unwrap();
+ let attrs = EnumAttributes::try_from(&node).unwrap();
+ assert!(attrs.contains_error_attr());
+ assert!(attrs.contains_non_exhaustive_attr());
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap();
+ let err = EnumAttributes::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "Trait not supported for enums");
+ }
+
+ // Test parsing attributes for interface definitions with the `[Enum]` attribute
+ #[test]
+ fn test_enum_attributes_from_interface() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum]").unwrap();
+ assert!(EnumAttributes::try_from(&node).is_ok());
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Enum, Error, NonExhaustive]")
+ .unwrap();
+ let attrs = EnumAttributes::try_from(&node).unwrap();
+ assert!(attrs.contains_error_attr());
+ assert!(attrs.contains_non_exhaustive_attr());
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum, Trait]").unwrap();
+ let err = EnumAttributes::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "Trait not supported for enums");
+ }
+
#[test]
fn test_other_attributes_not_supported_for_interfaces() {
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait, ByRef]").unwrap();