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