uniffi_udl/
attributes.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5//! # Attribute definitions for a `InterfaceCollector`.
6//!
7//! This module provides some conveniences for working with attribute definitions
8//! from WebIDL. When encountering a weedle `ExtendedAttribute` node, use `TryFrom`
9//! to convert it into an [`Attribute`] representing one of the attributes that we
10//! support. You can also use the [`parse_attributes`] function to parse an
11//! `ExtendedAttributeList` into a vec of same.
12//!
13//! We only support a small number of attributes, so it's manageable to have them
14//! all handled by a single abstraction. This might need to be refactored in future
15//! if we grow significantly more complicated attribute handling.
16
17use anyhow::{bail, Result};
18use uniffi_meta::{Checksum, ObjectImpl};
19
20/// Represents an attribute parsed from UDL, like `[ByRef]` or `[Throws]`.
21///
22/// This is a convenience enum for parsing UDL attributes and erroring out if we encounter
23/// any unsupported ones. These don't convert directly into parts of a `InterfaceCollector`, but
24/// may influence the properties of things like functions and arguments.
25#[derive(Debug, Clone, Checksum)]
26pub(super) enum Attribute {
27    ByRef,
28    Enum,
29    Error,
30    Name(String),
31    SelfType(SelfType),
32    Throws(String),
33    Traits(Vec<String>),
34    // `[External="crate_name"]` - We can `use crate_name::...` for the type.
35    External { crate_name: String },
36    Remote,
37    // Custom type on the scaffolding side
38    Custom { crate_name: Option<String> },
39    // The interface described is implemented as a trait.
40    Trait,
41    // Modifies `Trait` to enable foreign implementations (callback interfaces)
42    WithForeign,
43    Async,
44    NonExhaustive,
45}
46
47impl Attribute {
48    pub fn is_error(&self) -> bool {
49        matches!(self, Attribute::Error)
50    }
51    pub fn is_enum(&self) -> bool {
52        matches!(self, Attribute::Enum)
53    }
54}
55
56/// Convert a weedle `ExtendedAttribute` into an `Attribute` for a `InterfaceCollector` member,
57/// or error out if the attribute is not supported.
58impl TryFrom<&weedle::attribute::ExtendedAttribute<'_>> for Attribute {
59    type Error = anyhow::Error;
60    fn try_from(
61        weedle_attribute: &weedle::attribute::ExtendedAttribute<'_>,
62    ) -> Result<Self, anyhow::Error> {
63        match weedle_attribute {
64            // Matches plain named attributes like "[ByRef"].
65            weedle::attribute::ExtendedAttribute::NoArgs(attr) => match (attr.0).0 {
66                "ByRef" => Ok(Attribute::ByRef),
67                "Enum" => Ok(Attribute::Enum),
68                "Error" => Ok(Attribute::Error),
69                "Custom" => Ok(Attribute::Custom { crate_name: None }),
70                "Trait" => Ok(Attribute::Trait),
71                "WithForeign" => Ok(Attribute::WithForeign),
72                "Async" => Ok(Attribute::Async),
73                "NonExhaustive" => Ok(Attribute::NonExhaustive),
74                "Remote" => Ok(Attribute::Remote),
75                _ => anyhow::bail!("ExtendedAttributeNoArgs not supported: {:?}", (attr.0).0),
76            },
77            // Matches assignment-style attributes like ["Throws=Error"]
78            weedle::attribute::ExtendedAttribute::Ident(identity) => {
79                match identity.lhs_identifier.0 {
80                    "Name" => Ok(Attribute::Name(name_from_id_or_string(&identity.rhs))),
81                    "Throws" => Ok(Attribute::Throws(name_from_id_or_string(&identity.rhs))),
82                    "Self" => Ok(Attribute::SelfType(SelfType::try_from(&identity.rhs)?)),
83                    "External" => Ok(Attribute::External {
84                        crate_name: name_from_id_or_string(&identity.rhs),
85                    }),
86                    "Custom" => Ok(Attribute::Custom {
87                        crate_name: Some(name_from_id_or_string(&identity.rhs)),
88                    }),
89                    _ => anyhow::bail!(
90                        "Attribute identity Identifier not supported: {:?}",
91                        identity.lhs_identifier.0
92                    ),
93                }
94            }
95            weedle::attribute::ExtendedAttribute::IdentList(attr_list) => {
96                match attr_list.identifier.0 {
97                    "Traits" => Ok(Attribute::Traits(
98                        attr_list
99                            .list
100                            .body
101                            .list
102                            .iter()
103                            .map(|i| i.0.to_string())
104                            .collect(),
105                    )),
106                    _ => anyhow::bail!(
107                        "Attribute identity list not supported: {:?}",
108                        attr_list.identifier.0
109                    ),
110                }
111            }
112            _ => anyhow::bail!("Attribute not supported: {:?}", weedle_attribute),
113        }
114    }
115}
116
117fn name_from_id_or_string(nm: &weedle::attribute::IdentifierOrString<'_>) -> String {
118    match nm {
119        weedle::attribute::IdentifierOrString::Identifier(identifier) => identifier.0.to_string(),
120        weedle::attribute::IdentifierOrString::String(str_lit) => str_lit.0.to_string(),
121    }
122}
123
124/// Parse a weedle `ExtendedAttributeList` into a list of `Attribute`s,
125/// erroring out on duplicates.
126fn parse_attributes<F>(
127    weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
128    validator: F,
129) -> Result<Vec<Attribute>>
130where
131    F: Fn(&Attribute) -> Result<()>,
132{
133    let attrs = &weedle_attributes.body.list;
134
135    let mut hash_set = std::collections::HashSet::new();
136    for attr in attrs {
137        if !hash_set.insert(attr) {
138            anyhow::bail!("Duplicated ExtendedAttribute: {:?}", attr);
139        }
140    }
141
142    let attrs = attrs
143        .iter()
144        .map(Attribute::try_from)
145        .collect::<Result<Vec<_>, _>>()?;
146
147    for attr in &attrs {
148        validator(attr)?;
149    }
150
151    Ok(attrs)
152}
153
154/// Attributes that can be attached to an `dictionary` definition in the UDL.
155#[derive(Debug, Clone, Checksum, Default)]
156pub(super) struct DictionaryAttributes(Vec<Attribute>);
157
158impl DictionaryAttributes {
159    pub fn contains_remote(&self) -> bool {
160        self.0.iter().any(|attr| matches!(attr, Attribute::Remote))
161    }
162
163    pub fn get_uniffi_traits(&self) -> Vec<String> {
164        self.0
165            .iter()
166            .find_map(|attr| match attr {
167                Attribute::Traits(inner) => Some(inner.clone()),
168                _ => None,
169            })
170            .unwrap_or_default()
171    }
172}
173
174impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for DictionaryAttributes {
175    type Error = anyhow::Error;
176    fn try_from(
177        weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
178    ) -> Result<Self, Self::Error> {
179        let attrs = parse_attributes(weedle_attributes, |attr| match attr {
180            Attribute::Remote => Ok(()),
181            Attribute::Traits(_) => Ok(()),
182            _ => bail!(format!("{attr:?} not supported for dictionaries")),
183        })?;
184        Ok(Self(attrs))
185    }
186}
187
188impl<T: TryInto<DictionaryAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
189    for DictionaryAttributes
190{
191    type Error = anyhow::Error;
192    fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
193        match value {
194            None => Ok(Default::default()),
195            Some(v) => v.try_into(),
196        }
197    }
198}
199
200/// Attributes that can be attached to an `enum` definition in the UDL.
201#[derive(Debug, Clone, Checksum, Default)]
202pub(super) struct EnumAttributes(Vec<Attribute>);
203
204impl EnumAttributes {
205    pub fn contains_error_attr(&self) -> bool {
206        self.0.iter().any(|attr| attr.is_error())
207    }
208
209    pub fn contains_non_exhaustive_attr(&self) -> bool {
210        self.0
211            .iter()
212            .any(|attr| matches!(attr, Attribute::NonExhaustive))
213    }
214
215    pub fn contains_remote(&self) -> bool {
216        self.0.iter().any(|attr| matches!(attr, Attribute::Remote))
217    }
218
219    pub fn get_uniffi_traits(&self) -> Vec<String> {
220        self.0
221            .iter()
222            .find_map(|attr| match attr {
223                Attribute::Traits(inner) => Some(inner.clone()),
224                _ => None,
225            })
226            .unwrap_or_default()
227    }
228}
229
230impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for EnumAttributes {
231    type Error = anyhow::Error;
232    fn try_from(
233        weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
234    ) -> Result<Self, Self::Error> {
235        let attrs = parse_attributes(weedle_attributes, |attr| match attr {
236            Attribute::Error => Ok(()),
237            Attribute::NonExhaustive => Ok(()),
238            Attribute::Remote => Ok(()),
239            // Allow `[Enum]`, since we may be parsing an attribute list from an interface with the
240            // `[Enum]` attribute.
241            Attribute::Enum => Ok(()),
242            Attribute::Traits(_) => Ok(()),
243            _ => bail!(format!("{attr:?} not supported for enums")),
244        })?;
245        Ok(Self(attrs))
246    }
247}
248
249impl<T: TryInto<EnumAttributes, Error = anyhow::Error>> TryFrom<Option<T>> for EnumAttributes {
250    type Error = anyhow::Error;
251    fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
252        match value {
253            None => Ok(Default::default()),
254            Some(v) => v.try_into(),
255        }
256    }
257}
258
259/// Represents UDL attributes that might appear on a function.
260///
261/// This supports:
262///   * `[Throws=ErrorName]` attribute for functions that can produce an error.
263///   * `[Async] for async functions
264#[derive(Debug, Clone, Checksum, Default)]
265pub(super) struct FunctionAttributes(Vec<Attribute>);
266
267impl FunctionAttributes {
268    pub(super) fn get_throws_err(&self) -> Option<&str> {
269        self.0.iter().find_map(|attr| match attr {
270            // This will hopefully return a helpful compilation error
271            // if the error is not defined.
272            Attribute::Throws(inner) => Some(inner.as_ref()),
273            _ => None,
274        })
275    }
276
277    pub(super) fn is_async(&self) -> bool {
278        self.0.iter().any(|attr| matches!(attr, Attribute::Async))
279    }
280}
281
282impl FromIterator<Attribute> for FunctionAttributes {
283    fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
284        Self(Vec::from_iter(iter))
285    }
286}
287
288impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for FunctionAttributes {
289    type Error = anyhow::Error;
290    fn try_from(
291        weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
292    ) -> Result<Self, Self::Error> {
293        let attrs = parse_attributes(weedle_attributes, |attr| match attr {
294            Attribute::Throws(_) | Attribute::Async => Ok(()),
295            _ => bail!(format!("{attr:?} not supported for functions")),
296        })?;
297        Ok(Self(attrs))
298    }
299}
300
301impl<T: TryInto<FunctionAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
302    for FunctionAttributes
303{
304    type Error = anyhow::Error;
305    fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
306        match value {
307            None => Ok(Default::default()),
308            Some(v) => v.try_into(),
309        }
310    }
311}
312
313/// Represents UDL attributes that might appear on a function argument.
314///
315/// This supports the `[ByRef]` attribute for arguments that should be passed
316/// by reference in the generated Rust scaffolding.
317#[derive(Debug, Clone, Checksum, Default)]
318pub(super) struct ArgumentAttributes(Vec<Attribute>);
319
320impl ArgumentAttributes {
321    pub fn by_ref(&self) -> bool {
322        self.0.iter().any(|attr| matches!(attr, Attribute::ByRef))
323    }
324}
325
326impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ArgumentAttributes {
327    type Error = anyhow::Error;
328    fn try_from(
329        weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
330    ) -> Result<Self, Self::Error> {
331        let attrs = parse_attributes(weedle_attributes, |attr| match attr {
332            Attribute::ByRef => Ok(()),
333            _ => bail!(format!("{attr:?} not supported for arguments")),
334        })?;
335        Ok(Self(attrs))
336    }
337}
338
339impl<T: TryInto<ArgumentAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
340    for ArgumentAttributes
341{
342    type Error = anyhow::Error;
343    fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
344        match value {
345            None => Ok(Default::default()),
346            Some(v) => v.try_into(),
347        }
348    }
349}
350
351/// Represents UDL attributes that might appear on an `interface` definition.
352#[derive(Debug, Clone, Checksum, Default)]
353pub(super) struct InterfaceAttributes(Vec<Attribute>);
354
355impl InterfaceAttributes {
356    pub fn contains_enum_attr(&self) -> bool {
357        self.0.iter().any(|attr| attr.is_enum())
358    }
359
360    pub fn contains_error_attr(&self) -> bool {
361        self.0.iter().any(|attr| attr.is_error())
362    }
363
364    pub fn contains_trait(&self) -> bool {
365        self.0.iter().any(|attr| matches!(attr, Attribute::Trait))
366    }
367
368    pub fn contains_remote(&self) -> bool {
369        self.0.iter().any(|attr| matches!(attr, Attribute::Remote))
370    }
371
372    pub fn contains_with_foreign(&self) -> bool {
373        self.0
374            .iter()
375            .any(|attr| matches!(attr, Attribute::WithForeign))
376    }
377
378    pub fn object_impl(&self) -> Result<ObjectImpl> {
379        Ok(
380            match (self.contains_trait(), self.contains_with_foreign()) {
381                (true, true) => ObjectImpl::CallbackTrait,
382                (true, false) => ObjectImpl::Trait,
383                (false, false) => ObjectImpl::Struct,
384                (false, true) => bail!("WithForeign can't be specified without Trait"),
385            },
386        )
387    }
388    pub fn get_traits(&self) -> Vec<String> {
389        self.0
390            .iter()
391            .find_map(|attr| match attr {
392                Attribute::Traits(inner) => Some(inner.clone()),
393                _ => None,
394            })
395            .unwrap_or_default()
396    }
397}
398
399impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for InterfaceAttributes {
400    type Error = anyhow::Error;
401    fn try_from(
402        weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
403    ) -> Result<Self, Self::Error> {
404        let attrs = parse_attributes(weedle_attributes, |attr| match attr {
405            Attribute::Enum => Ok(()),
406            Attribute::Error => Ok(()),
407            Attribute::Trait => Ok(()),
408            Attribute::WithForeign => Ok(()),
409            Attribute::Traits(_) => Ok(()),
410            Attribute::Remote => Ok(()),
411            _ => bail!(format!("{attr:?} not supported for interface definition")),
412        })?;
413        // If `[Enum]` can only also have `[Traits]`
414        let ok_for_enum =
415            attrs.len() == 1 || attrs.iter().any(|a| matches!(a, Attribute::Traits(_)));
416        if attrs.iter().any(|a| matches!(a, Attribute::Enum)) && !ok_for_enum {
417            bail!("conflicting attributes on interface definition");
418        }
419        Ok(Self(attrs))
420    }
421}
422
423impl<T: TryInto<InterfaceAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
424    for InterfaceAttributes
425{
426    type Error = anyhow::Error;
427    fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
428        match value {
429            None => Ok(Default::default()),
430            Some(v) => v.try_into(),
431        }
432    }
433}
434
435/// Represents UDL attributes that might appear on a constructor.
436///
437/// This supports the `[Throws=ErrorName]` attribute for constructors that can produce
438/// an error, and the `[Name=MethodName]` for non-default constructors.
439#[derive(Debug, Clone, Checksum, Default)]
440pub(super) struct ConstructorAttributes(Vec<Attribute>);
441
442impl FromIterator<Attribute> for ConstructorAttributes {
443    fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
444        Self(Vec::from_iter(iter))
445    }
446}
447
448impl ConstructorAttributes {
449    pub(super) fn get_throws_err(&self) -> Option<&str> {
450        self.0.iter().find_map(|attr| match attr {
451            // This will hopefully return a helpful compilation error
452            // if the error is not defined.
453            Attribute::Throws(inner) => Some(inner.as_ref()),
454            _ => None,
455        })
456    }
457
458    pub(super) fn get_name(&self) -> Option<&str> {
459        self.0.iter().find_map(|attr| match attr {
460            Attribute::Name(inner) => Some(inner.as_ref()),
461            _ => None,
462        })
463    }
464
465    pub(super) fn is_async(&self) -> bool {
466        self.0.iter().any(|attr| matches!(attr, Attribute::Async))
467    }
468}
469
470impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ConstructorAttributes {
471    type Error = anyhow::Error;
472    fn try_from(
473        weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
474    ) -> Result<Self, Self::Error> {
475        let attrs = parse_attributes(weedle_attributes, |attr| match attr {
476            Attribute::Throws(_) => Ok(()),
477            Attribute::Name(_) => Ok(()),
478            Attribute::Async => Ok(()),
479            _ => bail!(format!("{attr:?} not supported for constructors")),
480        })?;
481        Ok(Self(attrs))
482    }
483}
484
485/// Represents UDL attributes that might appear on a method.
486///
487/// This supports the `[Throws=ErrorName]` attribute for methods that can produce
488/// an error, and the `[Self=ByArc]` attribute for methods that take `Arc<Self>` as receiver.
489#[derive(Debug, Clone, Checksum, Default)]
490pub(super) struct MethodAttributes(Vec<Attribute>);
491
492impl MethodAttributes {
493    pub(super) fn get_throws_err(&self) -> Option<&str> {
494        self.0.iter().find_map(|attr| match attr {
495            // This will hopefully return a helpful compilation error
496            // if the error is not defined.
497            Attribute::Throws(inner) => Some(inner.as_ref()),
498            _ => None,
499        })
500    }
501
502    pub(super) fn is_async(&self) -> bool {
503        self.0.iter().any(|attr| matches!(attr, Attribute::Async))
504    }
505
506    pub(super) fn get_self_by_arc(&self) -> bool {
507        self.0
508            .iter()
509            .any(|attr| matches!(attr, Attribute::SelfType(SelfType::ByArc)))
510    }
511}
512
513impl FromIterator<Attribute> for MethodAttributes {
514    fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
515        Self(Vec::from_iter(iter))
516    }
517}
518
519impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for MethodAttributes {
520    type Error = anyhow::Error;
521    fn try_from(
522        weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
523    ) -> Result<Self, Self::Error> {
524        let attrs = parse_attributes(weedle_attributes, |attr| match attr {
525            Attribute::SelfType(_) | Attribute::Throws(_) | Attribute::Async => Ok(()),
526            _ => bail!(format!("{attr:?} not supported for methods")),
527        })?;
528        Ok(Self(attrs))
529    }
530}
531
532impl<T: TryInto<MethodAttributes, Error = anyhow::Error>> TryFrom<Option<T>> for MethodAttributes {
533    type Error = anyhow::Error;
534    fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
535        match value {
536            None => Ok(Default::default()),
537            Some(v) => v.try_into(),
538        }
539    }
540}
541
542/// Represents the different possible types of method call receiver.
543///
544/// Actually we only support one of these right now, `[Self=ByArc]`.
545/// We might add more in future, e.g. a `[Self=ByRef]` if there are cases
546/// where we need to force the receiver to be taken by reference.
547#[derive(Debug, Clone, Checksum)]
548pub(super) enum SelfType {
549    ByArc, // Method receiver is `Arc<Self>`.
550}
551
552impl TryFrom<&weedle::attribute::IdentifierOrString<'_>> for SelfType {
553    type Error = anyhow::Error;
554    fn try_from(nm: &weedle::attribute::IdentifierOrString<'_>) -> Result<Self, Self::Error> {
555        Ok(match nm {
556            weedle::attribute::IdentifierOrString::Identifier(identifier) => match identifier.0 {
557                "ByArc" => SelfType::ByArc,
558                _ => bail!("Unsupported Self Type: {:?}", identifier.0),
559            },
560            weedle::attribute::IdentifierOrString::String(_) => {
561                bail!("Unsupported Self Type: {:?}", nm)
562            }
563        })
564    }
565}
566
567/// Represents UDL attributes that might appear on a typedef
568///
569/// This supports the `[External="crate_name"]` and `[Custom]` attributes for types.
570#[derive(Debug, Clone, Checksum, Default)]
571pub(super) struct TypedefAttributes(Vec<Attribute>);
572
573impl TypedefAttributes {
574    pub(super) fn get_crate_name(&self) -> Option<String> {
575        self.0.iter().find_map(|attr| match attr {
576            Attribute::External { crate_name, .. } => Some(crate_name.clone()),
577            Attribute::Custom {
578                crate_name: Some(crate_name),
579                ..
580            } => Some(crate_name.clone()),
581            _ => None,
582        })
583    }
584
585    pub(super) fn is_custom(&self) -> bool {
586        self.0
587            .iter()
588            .any(|attr| matches!(attr, Attribute::Custom { .. }))
589    }
590}
591
592impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for TypedefAttributes {
593    type Error = anyhow::Error;
594    fn try_from(
595        weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
596    ) -> Result<Self, Self::Error> {
597        let attrs = parse_attributes(weedle_attributes, |attr| match attr {
598            Attribute::External { .. } | Attribute::Custom { .. } => Ok(()),
599            _ => bail!(format!("{attr:?} not supported for typedefs")),
600        })?;
601        if attrs.len() > 1 {
602            bail!("Can't be [Custom] and [External]");
603        }
604        Ok(Self(attrs))
605    }
606}
607
608impl<T: TryInto<TypedefAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
609    for TypedefAttributes
610{
611    type Error = anyhow::Error;
612    fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
613        match value {
614            None => Ok(Default::default()),
615            Some(v) => v.try_into(),
616        }
617    }
618}
619
620#[cfg(test)]
621mod test {
622    use super::*;
623    use weedle::Parse;
624
625    #[test]
626    fn test_byref() -> Result<()> {
627        let (_, node) = weedle::attribute::ExtendedAttribute::parse("ByRef").unwrap();
628        let attr = Attribute::try_from(&node)?;
629        assert!(matches!(attr, Attribute::ByRef));
630        Ok(())
631    }
632
633    #[test]
634    fn test_enum() -> Result<()> {
635        let (_, node) = weedle::attribute::ExtendedAttribute::parse("Enum").unwrap();
636        let attr = Attribute::try_from(&node)?;
637        assert!(matches!(attr, Attribute::Enum));
638        assert!(attr.is_enum());
639        Ok(())
640    }
641
642    #[test]
643    fn test_error() -> Result<()> {
644        let (_, node) = weedle::attribute::ExtendedAttribute::parse("Error").unwrap();
645        let attr = Attribute::try_from(&node)?;
646        assert!(matches!(attr, Attribute::Error));
647        assert!(attr.is_error());
648        Ok(())
649    }
650
651    #[test]
652    fn test_name() -> Result<()> {
653        let (_, node) = weedle::attribute::ExtendedAttribute::parse("Name=Value").unwrap();
654        let attr = Attribute::try_from(&node)?;
655        assert!(matches!(attr, Attribute::Name(nm) if nm == "Value"));
656
657        let (_, node) = weedle::attribute::ExtendedAttribute::parse("Name").unwrap();
658        let err = Attribute::try_from(&node).unwrap_err();
659        assert_eq!(
660            err.to_string(),
661            "ExtendedAttributeNoArgs not supported: \"Name\""
662        );
663
664        Ok(())
665    }
666
667    #[test]
668    fn test_selftype() -> Result<()> {
669        let (_, node) = weedle::attribute::ExtendedAttribute::parse("Self=ByArc").unwrap();
670        let attr = Attribute::try_from(&node)?;
671        assert!(matches!(attr, Attribute::SelfType(SelfType::ByArc)));
672        let (_, node) = weedle::attribute::ExtendedAttribute::parse("Self=ByMistake").unwrap();
673        let err = Attribute::try_from(&node).unwrap_err();
674        assert_eq!(err.to_string(), "Unsupported Self Type: \"ByMistake\"");
675        Ok(())
676    }
677
678    #[test]
679    fn test_trait() -> Result<()> {
680        let (_, node) = weedle::attribute::ExtendedAttribute::parse("Trait").unwrap();
681        let attr = Attribute::try_from(&node)?;
682        assert!(matches!(attr, Attribute::Trait));
683        Ok(())
684    }
685
686    #[test]
687    fn test_throws() -> Result<()> {
688        let (_, node) = weedle::attribute::ExtendedAttribute::parse("Throws=Name").unwrap();
689        let attr = Attribute::try_from(&node)?;
690        assert!(matches!(attr, Attribute::Throws(nm) if nm == "Name"));
691
692        let (_, node) = weedle::attribute::ExtendedAttribute::parse("Throws").unwrap();
693        let err = Attribute::try_from(&node).unwrap_err();
694        assert_eq!(
695            err.to_string(),
696            "ExtendedAttributeNoArgs not supported: \"Throws\""
697        );
698
699        Ok(())
700    }
701
702    #[test]
703    fn test_unsupported() {
704        let (_, node) =
705            weedle::attribute::ExtendedAttribute::parse("UnsupportedAttribute").unwrap();
706        let err = Attribute::try_from(&node).unwrap_err();
707        assert_eq!(
708            err.to_string(),
709            "ExtendedAttributeNoArgs not supported: \"UnsupportedAttribute\""
710        );
711
712        let (_, node) =
713            weedle::attribute::ExtendedAttribute::parse("Unsupported=Attribute").unwrap();
714        let err = Attribute::try_from(&node).unwrap_err();
715        assert_eq!(
716            err.to_string(),
717            "Attribute identity Identifier not supported: \"Unsupported\""
718        );
719    }
720
721    #[test]
722    fn test_other_attributes_not_supported_for_enums() {
723        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Error, ByRef]").unwrap();
724        let err = EnumAttributes::try_from(&node).unwrap_err();
725        assert_eq!(err.to_string(), "ByRef not supported for enums");
726    }
727
728    #[test]
729    fn test_function_attributes() {
730        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap();
731        let attrs = FunctionAttributes::try_from(&node).unwrap();
732        assert!(matches!(attrs.get_throws_err(), Some("Error")));
733        assert!(!attrs.is_async());
734
735        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
736        let attrs = FunctionAttributes::try_from(&node).unwrap();
737        assert!(attrs.get_throws_err().is_none());
738        assert!(!attrs.is_async());
739
740        let (_, node) =
741            weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Async]").unwrap();
742        let attrs = FunctionAttributes::try_from(&node).unwrap();
743        assert!(matches!(attrs.get_throws_err(), Some("Error")));
744        assert!(attrs.is_async());
745    }
746
747    #[test]
748    fn test_other_attributes_not_supported_for_functions() {
749        let (_, node) =
750            weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, ByRef]").unwrap();
751        let err = FunctionAttributes::try_from(&node).unwrap_err();
752        assert_eq!(err.to_string(), "ByRef not supported for functions");
753
754        let (_, node) =
755            weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Self=ByArc]").unwrap();
756        let err = FunctionAttributes::try_from(&node).unwrap_err();
757        assert_eq!(
758            err.to_string(),
759            "SelfType(ByArc) not supported for functions"
760        );
761    }
762
763    #[test]
764    fn test_method_attributes() {
765        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap();
766        let attrs = MethodAttributes::try_from(&node).unwrap();
767        assert!(!attrs.get_self_by_arc());
768        assert!(matches!(attrs.get_throws_err(), Some("Error")));
769        assert!(!attrs.is_async());
770
771        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
772        let attrs = MethodAttributes::try_from(&node).unwrap();
773        assert!(!attrs.get_self_by_arc());
774        assert!(attrs.get_throws_err().is_none());
775        assert!(!attrs.is_async());
776
777        let (_, node) =
778            weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc, Throws=Error]").unwrap();
779        let attrs = MethodAttributes::try_from(&node).unwrap();
780        assert!(attrs.get_self_by_arc());
781        assert!(attrs.get_throws_err().is_some());
782        assert!(!attrs.is_async());
783
784        let (_, node) =
785            weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc, Throws=Error, Async]")
786                .unwrap();
787        let attrs = MethodAttributes::try_from(&node).unwrap();
788        assert!(attrs.get_self_by_arc());
789        assert!(attrs.get_throws_err().is_some());
790        assert!(attrs.is_async());
791
792        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc]").unwrap();
793        let attrs = MethodAttributes::try_from(&node).unwrap();
794        assert!(attrs.get_self_by_arc());
795        assert!(attrs.get_throws_err().is_none());
796        assert!(!attrs.is_async());
797    }
798
799    #[test]
800    fn test_constructor_attributes() {
801        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap();
802        let attrs = ConstructorAttributes::try_from(&node).unwrap();
803        assert!(matches!(attrs.get_throws_err(), Some("Error")));
804        assert!(attrs.get_name().is_none());
805
806        let (_, node) =
807            weedle::attribute::ExtendedAttributeList::parse("[Name=MyFactory]").unwrap();
808        let attrs = ConstructorAttributes::try_from(&node).unwrap();
809        assert!(attrs.get_throws_err().is_none());
810        assert!(matches!(attrs.get_name(), Some("MyFactory")));
811
812        let (_, node) =
813            weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Name=MyFactory]")
814                .unwrap();
815        let attrs = ConstructorAttributes::try_from(&node).unwrap();
816        assert!(matches!(attrs.get_throws_err(), Some("Error")));
817        assert!(matches!(attrs.get_name(), Some("MyFactory")));
818        assert!(!attrs.is_async());
819
820        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Async]").unwrap();
821        let attrs = ConstructorAttributes::try_from(&node).unwrap();
822        assert!(attrs.is_async());
823    }
824
825    #[test]
826    fn test_other_attributes_not_supported_for_constructors() {
827        let (_, node) =
828            weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, ByRef]").unwrap();
829        let err = ConstructorAttributes::try_from(&node).unwrap_err();
830        assert_eq!(err.to_string(), "ByRef not supported for constructors");
831
832        let (_, node) =
833            weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Self=ByArc]").unwrap();
834        let err = ConstructorAttributes::try_from(&node).unwrap_err();
835        assert_eq!(
836            err.to_string(),
837            "SelfType(ByArc) not supported for constructors"
838        );
839    }
840
841    #[test]
842    fn test_byref_attribute() {
843        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[ByRef]").unwrap();
844        let attrs = ArgumentAttributes::try_from(&node).unwrap();
845        assert!(attrs.by_ref());
846
847        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
848        let attrs = ArgumentAttributes::try_from(&node).unwrap();
849        assert!(!attrs.by_ref());
850    }
851
852    #[test]
853    fn test_other_attributes_not_supported_for_arguments() {
854        let (_, node) =
855            weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, ByRef]").unwrap();
856        let err = ArgumentAttributes::try_from(&node).unwrap_err();
857        assert_eq!(
858            err.to_string(),
859            "Throws(\"Error\") not supported for arguments"
860        );
861    }
862
863    #[test]
864    fn test_trait_attribute() {
865        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap();
866        let attrs = InterfaceAttributes::try_from(&node).unwrap();
867        assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::Trait);
868
869        let (_, node) =
870            weedle::attribute::ExtendedAttributeList::parse("[Trait, WithForeign]").unwrap();
871        let attrs = InterfaceAttributes::try_from(&node).unwrap();
872        assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::CallbackTrait);
873
874        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
875        let attrs = InterfaceAttributes::try_from(&node).unwrap();
876        assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::Struct);
877
878        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[WithForeign]").unwrap();
879        let attrs = InterfaceAttributes::try_from(&node).unwrap();
880        assert!(attrs.object_impl().is_err())
881    }
882
883    #[test]
884    fn test_dictionary_attributes() {
885        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Remote]").unwrap();
886        let attrs = DictionaryAttributes::try_from(&node).unwrap();
887        assert!(attrs.contains_remote());
888
889        let (_, node) =
890            weedle::attribute::ExtendedAttributeList::parse("[Traits=(Debug)]").unwrap();
891        DictionaryAttributes::try_from(&node).unwrap();
892
893        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap();
894        let err = DictionaryAttributes::try_from(&node).unwrap_err();
895        assert_eq!(err.to_string(), "Trait not supported for dictionaries");
896    }
897
898    #[test]
899    fn test_enum_attribute_on_interface() {
900        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum]").unwrap();
901        let attrs = InterfaceAttributes::try_from(&node).unwrap();
902        assert!(attrs.contains_enum_attr());
903
904        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
905        let attrs = InterfaceAttributes::try_from(&node).unwrap();
906        assert!(!attrs.contains_enum_attr());
907
908        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap();
909        let attrs = InterfaceAttributes::try_from(&node).unwrap();
910        assert!(!attrs.contains_enum_attr());
911
912        let (_, node) =
913            weedle::attribute::ExtendedAttributeList::parse("[Traits=(Debug), Enum]").unwrap();
914        InterfaceAttributes::try_from(&node).unwrap();
915
916        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait, Enum]").unwrap();
917        let err = InterfaceAttributes::try_from(&node).unwrap_err();
918        assert_eq!(
919            err.to_string(),
920            "conflicting attributes on interface definition"
921        );
922    }
923
924    // Test parsing attributes for enum definitions
925    #[test]
926    fn test_enum_attributes() {
927        let (_, node) =
928            weedle::attribute::ExtendedAttributeList::parse("[Error, NonExhaustive, Remote]")
929                .unwrap();
930        let attrs = EnumAttributes::try_from(&node).unwrap();
931        assert!(attrs.contains_error_attr());
932        assert!(attrs.contains_non_exhaustive_attr());
933        assert!(attrs.contains_remote());
934
935        let (_, node) =
936            weedle::attribute::ExtendedAttributeList::parse("[Traits=(Display)]").unwrap();
937        EnumAttributes::try_from(&node).unwrap();
938
939        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap();
940        let err = EnumAttributes::try_from(&node).unwrap_err();
941        assert_eq!(err.to_string(), "Trait not supported for enums");
942    }
943
944    // Test parsing attributes for interface definitions with the `[Enum]` attribute
945    #[test]
946    fn test_enum_attributes_from_interface() {
947        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum]").unwrap();
948        assert!(EnumAttributes::try_from(&node).is_ok());
949
950        let (_, node) =
951            weedle::attribute::ExtendedAttributeList::parse("[Enum, Error, NonExhaustive, Remote]")
952                .unwrap();
953        let attrs = EnumAttributes::try_from(&node).unwrap();
954        assert!(attrs.contains_error_attr());
955        assert!(attrs.contains_non_exhaustive_attr());
956        assert!(attrs.contains_remote());
957
958        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum, Trait]").unwrap();
959        let err = EnumAttributes::try_from(&node).unwrap_err();
960        assert_eq!(err.to_string(), "Trait not supported for enums");
961    }
962
963    #[test]
964    fn test_interface_attributes() {
965        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Remote]").unwrap();
966        let attrs = InterfaceAttributes::try_from(&node).unwrap();
967        assert!(attrs.contains_remote());
968
969        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait, ByRef]").unwrap();
970        let err = InterfaceAttributes::try_from(&node).unwrap_err();
971        assert_eq!(
972            err.to_string(),
973            "ByRef not supported for interface definition"
974        );
975    }
976
977    #[test]
978    fn test_typedef_attribute() {
979        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Custom]").unwrap();
980        let attrs = TypedefAttributes::try_from(&node).unwrap();
981        assert!(attrs.is_custom());
982        assert!(attrs.get_crate_name().is_none());
983
984        let (_, node) =
985            weedle::attribute::ExtendedAttributeList::parse("[Custom=\"crate_name\"]").unwrap();
986        let attrs = TypedefAttributes::try_from(&node).unwrap();
987        assert!(attrs.is_custom());
988        assert_eq!(attrs.get_crate_name().unwrap(), "crate_name");
989
990        let (_, node) =
991            weedle::attribute::ExtendedAttributeList::parse("[External=crate_name]").unwrap();
992        let attrs = TypedefAttributes::try_from(&node).unwrap();
993        assert!(!attrs.is_custom());
994        assert_eq!(attrs.get_crate_name().unwrap(), "crate_name");
995    }
996
997    #[test]
998    fn test_typedef_attributes_malformed() {
999        let (_, node) =
1000            weedle::attribute::ExtendedAttributeList::parse("[External=foo, Custom]").unwrap();
1001        let err = TypedefAttributes::try_from(&node).unwrap_err();
1002        assert_eq!(err.to_string(), "Can't be [Custom] and [External]");
1003
1004        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[External]").unwrap();
1005        let err = TypedefAttributes::try_from(&node).unwrap_err();
1006        assert_eq!(
1007            err.to_string(),
1008            "ExtendedAttributeNoArgs not supported: \"External\""
1009        );
1010    }
1011
1012    #[test]
1013    fn test_other_attributes_not_supported_for_typedef() {
1014        let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[ByRef]").unwrap();
1015        let err = TypedefAttributes::try_from(&node).unwrap_err();
1016        assert_eq!(err.to_string(), "ByRef not supported for typedefs");
1017    }
1018}