codama_attributes/
attributes.rs

1use crate::{Attribute, AttributeContext, CodamaDirective};
2use codama_errors::IteratorCombineErrors;
3use codama_syn_helpers::extensions::*;
4use std::ops::{Deref, DerefMut, Index, IndexMut};
5
6#[derive(Debug, PartialEq)]
7pub struct Attributes<'a>(pub Vec<Attribute<'a>>);
8
9impl<'a> Attributes<'a> {
10    pub fn parse(attrs: &'a [syn::Attribute], ctx: AttributeContext<'a>) -> syn::Result<Self> {
11        let attributes = Self(
12            attrs
13                .iter()
14                .map(|attr| Attribute::parse(attr, &ctx))
15                .collect_and_combine_errors()?,
16        );
17        attributes.validate_codama_type_attributes()?;
18        Ok(attributes)
19    }
20
21    pub fn validate_codama_type_attributes(&self) -> syn::Result<()> {
22        let mut errors = Vec::<syn::Error>::new();
23        let mut has_seen_type = false;
24
25        for attribute in self.0.iter().rev() {
26            if let Attribute::Codama(attribute) = attribute {
27                match &attribute.directive {
28                    CodamaDirective::Type(_) if !has_seen_type => has_seen_type = true,
29                    CodamaDirective::Type(_)
30                    | CodamaDirective::Encoding(_)
31                    | CodamaDirective::FixedSize(_)
32                        if has_seen_type =>
33                    {
34                        errors.push(syn::Error::new_spanned(
35                            attribute.ast,
36                            "This attribute is overridden by a `#[codama(type = ...)]` attribute below",
37                        ));
38                    }
39                    _ => {}
40                }
41            }
42        }
43
44        if errors.is_empty() {
45            Ok(())
46        } else {
47            // Combine all errors into one
48            let mut combined_error = errors.remove(0);
49            for error in errors {
50                combined_error.combine(error);
51            }
52            Err(combined_error)
53        }
54    }
55
56    pub fn has_any_codama_derive(&self) -> bool {
57        self.has_codama_derive("CodamaAccount")
58            || self.has_codama_derive("CodamaAccounts")
59            || self.has_codama_derive("CodamaInstruction")
60            || self.has_codama_derive("CodamaInstructions")
61            || self.has_codama_derive("CodamaType")
62    }
63
64    pub fn has_codama_derive(&self, derive: &str) -> bool {
65        self.has_derive(&["", "codama", "codama_macros"], derive)
66    }
67
68    pub fn has_derive(&self, prefixes: &[&str], last: &str) -> bool {
69        self.iter().any(|attr| match attr {
70            Attribute::Derive(a) => a
71                .derives
72                .iter()
73                .any(|p| prefixes.contains(&p.prefix().as_str()) && p.last_str() == last),
74            _ => false,
75        })
76    }
77
78    pub fn has_codama_attribute(&self, name: &str) -> bool {
79        self.iter()
80            .filter_map(Attribute::codama)
81            .any(|a| a.directive.name() == name)
82    }
83}
84
85impl<'a> Deref for Attributes<'a> {
86    type Target = Vec<Attribute<'a>>;
87
88    fn deref(&self) -> &Self::Target {
89        &self.0
90    }
91}
92
93impl DerefMut for Attributes<'_> {
94    fn deref_mut(&mut self) -> &mut Self::Target {
95        &mut self.0
96    }
97}
98
99impl<'a> AsRef<[Attribute<'a>]> for Attributes<'a> {
100    fn as_ref(&self) -> &[Attribute<'a>] {
101        &self.0
102    }
103}
104
105impl<'a> AsMut<[Attribute<'a>]> for Attributes<'a> {
106    fn as_mut(&mut self) -> &mut [Attribute<'a>] {
107        &mut self.0
108    }
109}
110
111impl<'a> Index<usize> for Attributes<'a> {
112    type Output = Attribute<'a>;
113
114    fn index(&self, index: usize) -> &Self::Output {
115        &self.0[index]
116    }
117}
118
119impl IndexMut<usize> for Attributes<'_> {
120    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
121        &mut self.0[index]
122    }
123}