summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_udl/src/attributes.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/uniffi_udl/src/attributes.rs')
-rw-r--r--third_party/rust/uniffi_udl/src/attributes.rs839
1 files changed, 839 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_udl/src/attributes.rs b/third_party/rust/uniffi_udl/src/attributes.rs
new file mode 100644
index 0000000000..f06b4f29c1
--- /dev/null
+++ b/third_party/rust/uniffi_udl/src/attributes.rs
@@ -0,0 +1,839 @@
+/* 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/. */
+
+//! # Attribute definitions for a `InterfaceCollector`.
+//!
+//! This module provides some conveniences for working with attribute definitions
+//! from WebIDL. When encountering a weedle `ExtendedAttribute` node, use `TryFrom`
+//! to convert it into an [`Attribute`] representing one of the attributes that we
+//! support. You can also use the [`parse_attributes`] function to parse an
+//! `ExtendedAttributeList` into a vec of same.
+//!
+//! We only support a small number of attributes, so it's manageable to have them
+//! all handled by a single abstraction. This might need to be refactored in future
+//! if we grow significantly more complicated attribute handling.
+
+use anyhow::{bail, Result};
+use uniffi_meta::{Checksum, ExternalKind, ObjectImpl};
+
+/// Represents an attribute parsed from UDL, like `[ByRef]` or `[Throws]`.
+///
+/// This is a convenience enum for parsing UDL attributes and erroring out if we encounter
+/// any unsupported ones. These don't convert directly into parts of a `InterfaceCollector`, but
+/// may influence the properties of things like functions and arguments.
+#[derive(Debug, Clone, Checksum)]
+pub(super) enum Attribute {
+ ByRef,
+ Enum,
+ Error,
+ Name(String),
+ SelfType(SelfType),
+ Throws(String),
+ Traits(Vec<String>),
+ // `[External="crate_name"]` - We can `use crate_name::...` for the type.
+ External {
+ crate_name: String,
+ kind: ExternalKind,
+ export: bool,
+ },
+ // Custom type on the scaffolding side
+ Custom,
+ // The interface described is implemented as a trait.
+ Trait,
+}
+
+impl Attribute {
+ pub fn is_error(&self) -> bool {
+ matches!(self, Attribute::Error)
+ }
+ pub fn is_enum(&self) -> bool {
+ matches!(self, Attribute::Enum)
+ }
+}
+
+/// Convert a weedle `ExtendedAttribute` into an `Attribute` for a `InterfaceCollector` member,
+/// or error out if the attribute is not supported.
+impl TryFrom<&weedle::attribute::ExtendedAttribute<'_>> for Attribute {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attribute: &weedle::attribute::ExtendedAttribute<'_>,
+ ) -> Result<Self, anyhow::Error> {
+ match weedle_attribute {
+ // Matches plain named attributes like "[ByRef"].
+ weedle::attribute::ExtendedAttribute::NoArgs(attr) => match (attr.0).0 {
+ "ByRef" => Ok(Attribute::ByRef),
+ "Enum" => Ok(Attribute::Enum),
+ "Error" => Ok(Attribute::Error),
+ "Custom" => Ok(Attribute::Custom),
+ "Trait" => Ok(Attribute::Trait),
+ _ => anyhow::bail!("ExtendedAttributeNoArgs not supported: {:?}", (attr.0).0),
+ },
+ // Matches assignment-style attributes like ["Throws=Error"]
+ weedle::attribute::ExtendedAttribute::Ident(identity) => {
+ match identity.lhs_identifier.0 {
+ "Name" => Ok(Attribute::Name(name_from_id_or_string(&identity.rhs))),
+ "Throws" => Ok(Attribute::Throws(name_from_id_or_string(&identity.rhs))),
+ "Self" => Ok(Attribute::SelfType(SelfType::try_from(&identity.rhs)?)),
+ "External" => Ok(Attribute::External {
+ crate_name: name_from_id_or_string(&identity.rhs),
+ kind: ExternalKind::DataClass,
+ export: false,
+ }),
+ "ExternalExport" => Ok(Attribute::External {
+ crate_name: name_from_id_or_string(&identity.rhs),
+ kind: ExternalKind::DataClass,
+ export: true,
+ }),
+ "ExternalInterface" => Ok(Attribute::External {
+ crate_name: name_from_id_or_string(&identity.rhs),
+ kind: ExternalKind::Interface,
+ export: false,
+ }),
+ "ExternalInterfaceExport" => Ok(Attribute::External {
+ crate_name: name_from_id_or_string(&identity.rhs),
+ kind: ExternalKind::Interface,
+ export: true,
+ }),
+ _ => anyhow::bail!(
+ "Attribute identity Identifier not supported: {:?}",
+ identity.lhs_identifier.0
+ ),
+ }
+ }
+ weedle::attribute::ExtendedAttribute::IdentList(attr_list) => {
+ match attr_list.identifier.0 {
+ "Traits" => Ok(Attribute::Traits(
+ attr_list
+ .list
+ .body
+ .list
+ .iter()
+ .map(|i| i.0.to_string())
+ .collect(),
+ )),
+ _ => anyhow::bail!(
+ "Attribute identity list not supported: {:?}",
+ attr_list.identifier.0
+ ),
+ }
+ }
+ _ => anyhow::bail!("Attribute not supported: {:?}", weedle_attribute),
+ }
+ }
+}
+
+fn name_from_id_or_string(nm: &weedle::attribute::IdentifierOrString<'_>) -> String {
+ match nm {
+ weedle::attribute::IdentifierOrString::Identifier(identifier) => identifier.0.to_string(),
+ weedle::attribute::IdentifierOrString::String(str_lit) => str_lit.0.to_string(),
+ }
+}
+
+/// Parse a weedle `ExtendedAttributeList` into a list of `Attribute`s,
+/// erroring out on duplicates.
+fn parse_attributes<F>(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ validator: F,
+) -> Result<Vec<Attribute>>
+where
+ F: Fn(&Attribute) -> Result<()>,
+{
+ let attrs = &weedle_attributes.body.list;
+
+ let mut hash_set = std::collections::HashSet::new();
+ for attr in attrs {
+ if !hash_set.insert(attr) {
+ anyhow::bail!("Duplicated ExtendedAttribute: {:?}", attr);
+ }
+ }
+
+ let attrs = attrs
+ .iter()
+ .map(Attribute::try_from)
+ .collect::<Result<Vec<_>, _>>()?;
+
+ for attr in &attrs {
+ validator(attr)?;
+ }
+
+ Ok(attrs)
+}
+
+/// 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>);
+
+impl EnumAttributes {
+ pub fn contains_error_attr(&self) -> bool {
+ self.0.iter().any(|attr| attr.is_error())
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for EnumAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::Error => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for enums")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<EnumAttributes, Error = anyhow::Error>> TryFrom<Option<T>> for EnumAttributes {
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+/// Represents UDL attributes that might appear on a function.
+///
+/// This supports the `[Throws=ErrorName]` attribute for functions that
+/// can produce an error.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct FunctionAttributes(Vec<Attribute>);
+
+impl FunctionAttributes {
+ pub(super) fn get_throws_err(&self) -> Option<&str> {
+ self.0.iter().find_map(|attr| match attr {
+ // This will hopefully return a helpful compilation error
+ // if the error is not defined.
+ Attribute::Throws(inner) => Some(inner.as_ref()),
+ _ => None,
+ })
+ }
+}
+
+impl FromIterator<Attribute> for FunctionAttributes {
+ fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
+ Self(Vec::from_iter(iter))
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for FunctionAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::Throws(_) => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for functions")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<FunctionAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
+ for FunctionAttributes
+{
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+/// Represents UDL attributes that might appear on a function argument.
+///
+/// This supports the `[ByRef]` attribute for arguments that should be passed
+/// by reference in the generated Rust scaffolding.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct ArgumentAttributes(Vec<Attribute>);
+
+impl ArgumentAttributes {
+ pub fn by_ref(&self) -> bool {
+ self.0.iter().any(|attr| matches!(attr, Attribute::ByRef))
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ArgumentAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::ByRef => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for arguments")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<ArgumentAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
+ for ArgumentAttributes
+{
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+/// Represents UDL attributes that might appear on an `interface` definition.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct InterfaceAttributes(Vec<Attribute>);
+
+impl InterfaceAttributes {
+ pub fn contains_enum_attr(&self) -> bool {
+ self.0.iter().any(|attr| attr.is_enum())
+ }
+
+ pub fn contains_error_attr(&self) -> bool {
+ 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 get_traits(&self) -> Vec<String> {
+ self.0
+ .iter()
+ .find_map(|attr| match attr {
+ Attribute::Traits(inner) => Some(inner.clone()),
+ _ => None,
+ })
+ .unwrap_or_default()
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for InterfaceAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::Enum => Ok(()),
+ Attribute::Error => Ok(()),
+ Attribute::Trait => Ok(()),
+ Attribute::Traits(_) => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for interface definition")),
+ })?;
+ if attrs.iter().any(|a| matches!(a, Attribute::Enum)) && attrs.len() != 1 {
+ // If `[Enum]` is specified it must be the only attribute.
+ bail!("conflicting attributes on interface definition");
+ }
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<InterfaceAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
+ for InterfaceAttributes
+{
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+/// Represents UDL attributes that might appear on a constructor.
+///
+/// This supports the `[Throws=ErrorName]` attribute for constructors that can produce
+/// an error, and the `[Name=MethodName]` for non-default constructors.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct ConstructorAttributes(Vec<Attribute>);
+
+impl FromIterator<Attribute> for ConstructorAttributes {
+ fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
+ Self(Vec::from_iter(iter))
+ }
+}
+
+impl ConstructorAttributes {
+ pub(super) fn get_throws_err(&self) -> Option<&str> {
+ self.0.iter().find_map(|attr| match attr {
+ // This will hopefully return a helpful compilation error
+ // if the error is not defined.
+ Attribute::Throws(inner) => Some(inner.as_ref()),
+ _ => None,
+ })
+ }
+
+ pub(super) fn get_name(&self) -> Option<&str> {
+ self.0.iter().find_map(|attr| match attr {
+ Attribute::Name(inner) => Some(inner.as_ref()),
+ _ => None,
+ })
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ConstructorAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::Throws(_) => Ok(()),
+ Attribute::Name(_) => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for constructors")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+/// Represents UDL attributes that might appear on a method.
+///
+/// This supports the `[Throws=ErrorName]` attribute for methods that can produce
+/// an error, and the `[Self=ByArc]` attribute for methods that take `Arc<Self>` as receiver.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct MethodAttributes(Vec<Attribute>);
+
+impl MethodAttributes {
+ pub(super) fn get_throws_err(&self) -> Option<&str> {
+ self.0.iter().find_map(|attr| match attr {
+ // This will hopefully return a helpful compilation error
+ // if the error is not defined.
+ Attribute::Throws(inner) => Some(inner.as_ref()),
+ _ => None,
+ })
+ }
+
+ pub(super) fn get_self_by_arc(&self) -> bool {
+ self.0
+ .iter()
+ .any(|attr| matches!(attr, Attribute::SelfType(SelfType::ByArc)))
+ }
+}
+
+impl FromIterator<Attribute> for MethodAttributes {
+ fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
+ Self(Vec::from_iter(iter))
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for MethodAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::SelfType(_) => Ok(()),
+ Attribute::Throws(_) => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for methods")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<MethodAttributes, Error = anyhow::Error>> TryFrom<Option<T>> for MethodAttributes {
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+/// Represents the different possible types of method call receiver.
+///
+/// Actually we only support one of these right now, `[Self=ByArc]`.
+/// We might add more in future, e.g. a `[Self=ByRef]` if there are cases
+/// where we need to force the receiver to be taken by reference.
+#[derive(Debug, Clone, Checksum)]
+pub(super) enum SelfType {
+ ByArc, // Method receiver is `Arc<Self>`.
+}
+
+impl TryFrom<&weedle::attribute::IdentifierOrString<'_>> for SelfType {
+ type Error = anyhow::Error;
+ fn try_from(nm: &weedle::attribute::IdentifierOrString<'_>) -> Result<Self, Self::Error> {
+ Ok(match nm {
+ weedle::attribute::IdentifierOrString::Identifier(identifier) => match identifier.0 {
+ "ByArc" => SelfType::ByArc,
+ _ => bail!("Unsupported Self Type: {:?}", identifier.0),
+ },
+ weedle::attribute::IdentifierOrString::String(_) => {
+ bail!("Unsupported Self Type: {:?}", nm)
+ }
+ })
+ }
+}
+
+/// Represents UDL attributes that might appear on a typedef
+///
+/// This supports the `[External="crate_name"]` and `[Custom]` attributes for types.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct TypedefAttributes(Vec<Attribute>);
+
+impl TypedefAttributes {
+ pub(super) fn get_crate_name(&self) -> String {
+ self.0
+ .iter()
+ .find_map(|attr| match attr {
+ Attribute::External { crate_name, .. } => Some(crate_name.clone()),
+ _ => None,
+ })
+ .expect("must have a crate name")
+ }
+
+ pub(super) fn is_custom(&self) -> bool {
+ self.0
+ .iter()
+ .any(|attr| matches!(attr, Attribute::Custom { .. }))
+ }
+
+ pub(super) fn external_kind(&self) -> Option<ExternalKind> {
+ self.0.iter().find_map(|attr| match attr {
+ Attribute::External { 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),
+ _ => None,
+ })
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for TypedefAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::External { .. } | Attribute::Custom => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for typedefs")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<TypedefAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
+ for TypedefAttributes
+{
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use weedle::Parse;
+
+ #[test]
+ fn test_byref() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("ByRef").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::ByRef));
+ Ok(())
+ }
+
+ #[test]
+ fn test_enum() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Enum").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::Enum));
+ assert!(attr.is_enum());
+ Ok(())
+ }
+
+ #[test]
+ fn test_error() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Error").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::Error));
+ assert!(attr.is_error());
+ Ok(())
+ }
+
+ #[test]
+ fn test_name() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Name=Value").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::Name(nm) if nm == "Value"));
+
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Name").unwrap();
+ let err = Attribute::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "ExtendedAttributeNoArgs not supported: \"Name\""
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_selftype() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Self=ByArc").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::SelfType(SelfType::ByArc)));
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Self=ByMistake").unwrap();
+ let err = Attribute::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "Unsupported Self Type: \"ByMistake\"");
+ Ok(())
+ }
+
+ #[test]
+ fn test_trait() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Trait").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::Trait));
+ Ok(())
+ }
+
+ #[test]
+ fn test_throws() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Throws=Name").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::Throws(nm) if nm == "Name"));
+
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Throws").unwrap();
+ let err = Attribute::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "ExtendedAttributeNoArgs not supported: \"Throws\""
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_unsupported() {
+ let (_, node) =
+ weedle::attribute::ExtendedAttribute::parse("UnsupportedAttribute").unwrap();
+ let err = Attribute::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "ExtendedAttributeNoArgs not supported: \"UnsupportedAttribute\""
+ );
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttribute::parse("Unsupported=Attribute").unwrap();
+ let err = Attribute::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Attribute identity Identifier not supported: \"Unsupported\""
+ );
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_enums() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Error, ByRef]").unwrap();
+ let err = EnumAttributes::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "ByRef not supported for enums");
+ }
+
+ #[test]
+ fn test_throws_attribute() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap();
+ let attrs = FunctionAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.get_throws_err(), Some("Error")));
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
+ let attrs = FunctionAttributes::try_from(&node).unwrap();
+ assert!(attrs.get_throws_err().is_none());
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_functions() {
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, ByRef]").unwrap();
+ let err = FunctionAttributes::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "ByRef not supported for functions");
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Self=ByArc]").unwrap();
+ let err = FunctionAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "SelfType(ByArc) not supported for functions"
+ );
+ }
+
+ #[test]
+ fn test_method_attributes() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap();
+ let attrs = MethodAttributes::try_from(&node).unwrap();
+ assert!(!attrs.get_self_by_arc());
+ assert!(matches!(attrs.get_throws_err(), Some("Error")));
+
+ 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());
+
+ 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());
+
+ 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());
+ }
+
+ #[test]
+ fn test_constructor_attributes() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap();
+ let attrs = ConstructorAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.get_throws_err(), Some("Error")));
+ assert!(attrs.get_name().is_none());
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Name=MyFactory]").unwrap();
+ let attrs = ConstructorAttributes::try_from(&node).unwrap();
+ assert!(attrs.get_throws_err().is_none());
+ assert!(matches!(attrs.get_name(), Some("MyFactory")));
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Name=MyFactory]")
+ .unwrap();
+ let attrs = ConstructorAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.get_throws_err(), Some("Error")));
+ assert!(matches!(attrs.get_name(), Some("MyFactory")));
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_constructors() {
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, ByRef]").unwrap();
+ let err = ConstructorAttributes::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "ByRef not supported for constructors");
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Self=ByArc]").unwrap();
+ let err = ConstructorAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "SelfType(ByArc) not supported for constructors"
+ );
+ }
+
+ #[test]
+ fn test_byref_attribute() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[ByRef]").unwrap();
+ let attrs = ArgumentAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.by_ref(), true));
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
+ let attrs = ArgumentAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.by_ref(), false));
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_arguments() {
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, ByRef]").unwrap();
+ let err = ArgumentAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Throws(\"Error\") not supported for arguments"
+ );
+ }
+
+ #[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);
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
+ let attrs = InterfaceAttributes::try_from(&node).unwrap();
+ assert_eq!(attrs.object_impl(), ObjectImpl::Struct);
+ }
+
+ #[test]
+ fn test_enum_attribute() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum]").unwrap();
+ let attrs = InterfaceAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.contains_enum_attr(), true));
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
+ let attrs = InterfaceAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.contains_enum_attr(), false));
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap();
+ let attrs = InterfaceAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.contains_enum_attr(), false));
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait, Enum]").unwrap();
+ let err = InterfaceAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "conflicting attributes on interface definition"
+ );
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_interfaces() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait, ByRef]").unwrap();
+ let err = InterfaceAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "ByRef not supported for interface definition"
+ );
+ }
+
+ #[test]
+ fn test_typedef_attribute() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Custom]").unwrap();
+ let attrs = TypedefAttributes::try_from(&node).unwrap();
+ assert!(attrs.is_custom());
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[External=crate_name]").unwrap();
+ let attrs = TypedefAttributes::try_from(&node).unwrap();
+ assert!(!attrs.is_custom());
+ assert_eq!(attrs.get_crate_name(), "crate_name");
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[ExternalInterface=crate_name ]")
+ .unwrap();
+ let attrs = TypedefAttributes::try_from(&node).unwrap();
+ assert!(!attrs.is_custom());
+ assert_eq!(attrs.get_crate_name(), "crate_name");
+ }
+
+ #[test]
+ fn test_typedef_attributes_malformed() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Custom=foo]").unwrap();
+ let err = TypedefAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Attribute identity Identifier not supported: \"Custom\""
+ );
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[External]").unwrap();
+ let err = TypedefAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "ExtendedAttributeNoArgs not supported: \"External\""
+ );
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_typedef() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[ByRef]").unwrap();
+ let err = TypedefAttributes::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "ByRef not supported for typedefs");
+ }
+}