Revision control

Copy as Markdown

Other Tools

/* 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,
},
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 {
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),
"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"]
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,
}),
"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
),
}
}
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(),
}
}
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>(
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.
#[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())
}
pub fn contains_non_exhaustive_attr(&self) -> bool {
self.0
.iter()
.any(|attr| matches!(attr, Attribute::NonExhaustive))
}
}
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(()),
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))
}
}
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:
/// * `[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>);
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,
})
}
pub(super) fn is_async(&self) -> bool {
self.0.iter().any(|attr| matches!(attr, Attribute::Async))
}
}
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(_) | Attribute::Async => 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 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
.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::WithForeign => 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,
})
}
pub(super) fn is_async(&self) -> bool {
self.0.iter().any(|attr| matches!(attr, Attribute::Async))
}
}
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(()),
Attribute::Async => 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 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()
.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(_) | Attribute::Throws(_) | Attribute::Async => 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 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,
})
}
}
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 | Attribute::Rust { .. } => 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_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]
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")));
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]
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")));
assert!(!attrs.is_async());
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Async]").unwrap();
let attrs = ConstructorAttributes::try_from(&node).unwrap();
assert!(attrs.is_async());
}
#[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!(attrs.by_ref());
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
let attrs = ArgumentAttributes::try_from(&node).unwrap();
assert!(!attrs.by_ref());
}
#[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().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().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_on_interface() {
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum]").unwrap();
let attrs = InterfaceAttributes::try_from(&node).unwrap();
assert!(attrs.contains_enum_attr());
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
let attrs = InterfaceAttributes::try_from(&node).unwrap();
assert!(!attrs.contains_enum_attr());
let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap();
let attrs = InterfaceAttributes::try_from(&node).unwrap();
assert!(!attrs.contains_enum_attr());
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 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();
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");
}
}