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("CodamaPda")
65 || self.has_codama_derive("CodamaType")
66 }
67
68 pub fn has_codama_derive(&self, derive: &str) -> bool {
69 self.has_derive(&["", "codama", "codama_macros"], derive)
70 }
71
72 pub fn has_derive(&self, prefixes: &[&str], last: &str) -> bool {
73 self.iter().filter_map(DeriveAttribute::filter).any(|attr| {
74 attr.derives
75 .iter()
76 .any(|p| p.last_str() == last && prefixes.contains(&p.prefix().as_str()))
77 })
78 }
79
80 pub fn has_codama_attribute(&self, name: &str) -> bool {
81 self.iter()
82 .filter_map(CodamaAttribute::filter)
83 .any(|a| a.directive.name() == name)
84 }
85
86 pub fn get_all<B: 'a, F>(&'a self, f: F) -> Vec<&'a B>
87 where
88 F: Fn(&'a Attribute<'a>) -> Option<&'a B>,
89 {
90 self.iter().filter_map(f).collect()
91 }
92
93 pub fn get_first<B: 'a, F>(&'a self, f: F) -> Option<&'a B>
94 where
95 F: Fn(&'a Attribute<'a>) -> Option<&'a B>,
96 {
97 self.iter().find_map(f)
98 }
99
100 pub fn get_last<B: 'a, F>(&'a self, f: F) -> Option<&'a B>
101 where
102 F: Fn(&'a Attribute<'a>) -> Option<&'a B>,
103 {
104 self.iter().rev().find_map(f)
105 }
106}
107
108impl<'a> Deref for Attributes<'a> {
109 type Target = Vec<Attribute<'a>>;
110
111 fn deref(&self) -> &Self::Target {
112 &self.0
113 }
114}
115
116impl DerefMut for Attributes<'_> {
117 fn deref_mut(&mut self) -> &mut Self::Target {
118 &mut self.0
119 }
120}
121
122impl<'a> AsRef<[Attribute<'a>]> for Attributes<'a> {
123 fn as_ref(&self) -> &[Attribute<'a>] {
124 &self.0
125 }
126}
127
128impl<'a> AsMut<[Attribute<'a>]> for Attributes<'a> {
129 fn as_mut(&mut self) -> &mut [Attribute<'a>] {
130 &mut self.0
131 }
132}
133
134impl<'a> Index<usize> for Attributes<'a> {
135 type Output = Attribute<'a>;
136
137 fn index(&self, index: usize) -> &Self::Output {
138 &self.0[index]
139 }
140}
141
142impl IndexMut<usize> for Attributes<'_> {
143 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
144 &mut self.0[index]
145 }
146}