codama_attributes/codama_directives/
program_directive.rs1use crate::{utils::SetOnce, Attribute, CodamaAttribute, CodamaDirective, TryFromFilter};
2use codama_errors::CodamaError;
3use codama_nodes::{CamelCaseString, Node, ProgramNode};
4use codama_syn_helpers::{extensions::*, Meta};
5
6#[derive(Debug, PartialEq)]
7pub struct ProgramDirective {
8 pub name: CamelCaseString,
9 pub address: String,
10}
11
12impl ProgramDirective {
13 pub fn parse(meta: &Meta) -> syn::Result<Self> {
14 let pl = meta.assert_directive("program")?.as_path_list()?;
15
16 let mut name = SetOnce::<CamelCaseString>::new("name");
17 let mut address = SetOnce::<String>::new("address");
18
19 pl.each(|ref meta| match meta.path_str().as_str() {
20 "name" => name.set(meta.as_value()?.as_expr()?.as_string()?.into(), meta),
21 "address" => address.set(meta.as_value()?.as_expr()?.as_string()?, meta),
22 _ => Err(meta.error("unrecognized attribute")),
23 })?;
24
25 Ok(Self {
26 name: name.take(meta)?,
27 address: address.take(meta)?,
28 })
29 }
30
31 pub fn apply(attributes: &crate::Attributes, node: Node) -> Node {
32 match attributes.get_last(Self::filter) {
33 Some(pd) => pd.update_or_wrap_program_node(node),
34 None => node,
35 }
36 }
37
38 pub fn update_or_wrap_program_node(&self, node: Node) -> Node {
39 match node {
40 Node::Root(mut root) => {
41 root.program.name = self.name.clone();
42 root.program.public_key = self.address.clone();
43 root.into()
44 }
45 Node::Program(mut program) => {
46 program.name = self.name.clone();
47 program.public_key = self.address.clone();
48 program.into()
49 }
50 Node::Account(account) => ProgramNode {
51 name: self.name.clone(),
52 public_key: self.address.clone(),
53 accounts: vec![account],
54 ..ProgramNode::default()
55 }
56 .into(),
57 Node::Constant(constant) => ProgramNode {
58 name: self.name.clone(),
59 public_key: self.address.clone(),
60 constants: vec![constant],
61 ..ProgramNode::default()
62 }
63 .into(),
64 Node::Instruction(instruction) => ProgramNode {
65 name: self.name.clone(),
66 public_key: self.address.clone(),
67 instructions: vec![instruction],
68 ..ProgramNode::default()
69 }
70 .into(),
71 Node::Error(error) => ProgramNode {
72 name: self.name.clone(),
73 public_key: self.address.clone(),
74 errors: vec![error],
75 ..ProgramNode::default()
76 }
77 .into(),
78 Node::Pda(pda) => ProgramNode {
79 name: self.name.clone(),
80 public_key: self.address.clone(),
81 pdas: vec![pda],
82 ..ProgramNode::default()
83 }
84 .into(),
85 Node::Event(event) => ProgramNode {
86 name: self.name.clone(),
87 public_key: self.address.clone(),
88 events: vec![event],
89 ..ProgramNode::default()
90 }
91 .into(),
92 other => other,
93 }
94 }
95}
96
97impl<'a> TryFrom<&'a CodamaAttribute<'a>> for &'a ProgramDirective {
98 type Error = CodamaError;
99
100 fn try_from(attribute: &'a CodamaAttribute) -> Result<Self, Self::Error> {
101 match attribute.directive.as_ref() {
102 CodamaDirective::Program(ref a) => Ok(a),
103 _ => Err(CodamaError::InvalidCodamaDirective {
104 expected: "program".to_string(),
105 actual: attribute.directive.name().to_string(),
106 }),
107 }
108 }
109}
110
111impl<'a> TryFrom<&'a Attribute<'a>> for &'a ProgramDirective {
112 type Error = CodamaError;
113
114 fn try_from(attribute: &'a Attribute) -> Result<Self, Self::Error> {
115 <&CodamaAttribute>::try_from(attribute)?.try_into()
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn ok() {
125 let meta: Meta = syn::parse_quote! { program(name = "associatedToken", address = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL") };
126 let directive = ProgramDirective::parse(&meta).unwrap();
127 assert_eq!(
128 directive,
129 ProgramDirective {
130 name: CamelCaseString::from("associatedToken"),
131 address: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL".to_string(),
132 }
133 );
134 }
135
136 #[test]
137 fn name_missing() {
138 let meta: Meta =
139 syn::parse_quote! { program(address = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL") };
140 let error = ProgramDirective::parse(&meta).unwrap_err();
141 assert_eq!(error.to_string(), "name is missing");
142 }
143
144 #[test]
145 fn address_missing() {
146 let meta: Meta = syn::parse_quote! { program(name = "associatedToken") };
147 let error = ProgramDirective::parse(&meta).unwrap_err();
148 assert_eq!(error.to_string(), "address is missing");
149 }
150}