codama_nodes/
instruction_node.rs1use crate::{
2 CamelCaseString, DiscriminatorNode, Docs, InstructionAccountNode, InstructionArgumentNode,
3 InstructionByteDeltaNode, InstructionRemainingAccountsNode,
4};
5use codama_nodes_derive::node;
6use serde::{Deserialize, Serialize};
7
8#[node]
9#[derive(Default)]
10pub struct InstructionNode {
11 pub name: CamelCaseString,
13 #[serde(default)]
14 #[serde(skip_serializing_if = "Docs::is_empty")]
15 pub docs: Docs,
16 #[serde(default)]
17 #[serde(skip_serializing_if = "InstructionOptionalAccountStrategy::is_default")]
18 pub optional_account_strategy: InstructionOptionalAccountStrategy,
19
20 pub accounts: Vec<InstructionAccountNode>,
22 pub arguments: Vec<InstructionArgumentNode>,
23 #[serde(default)]
24 #[serde(skip_serializing_if = "Vec::is_empty")]
25 pub extra_arguments: Vec<InstructionArgumentNode>,
26 #[serde(default)]
27 #[serde(skip_serializing_if = "Vec::is_empty")]
28 pub remaining_accounts: Vec<InstructionRemainingAccountsNode>,
29 #[serde(default)]
30 #[serde(skip_serializing_if = "Vec::is_empty")]
31 pub byte_deltas: Vec<InstructionByteDeltaNode>,
32 #[serde(default)]
33 #[serde(skip_serializing_if = "Vec::is_empty")]
34 pub discriminators: Vec<DiscriminatorNode>,
35 #[serde(default)]
36 #[serde(skip_serializing_if = "Vec::is_empty")]
37 pub sub_instructions: Vec<InstructionNode>,
38}
39
40#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize)]
41#[serde(rename_all = "camelCase")]
42pub enum InstructionOptionalAccountStrategy {
43 Omitted,
44 #[default]
45 ProgramId,
46}
47
48impl InstructionOptionalAccountStrategy {
49 pub fn is_default(&self) -> bool {
50 matches!(self, Self::ProgramId)
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn empty_instruction() {
60 let node = InstructionNode {
61 name: "myInstruction".into(),
62 ..InstructionNode::default()
63 };
64 assert_eq!(node.name, CamelCaseString::new("myInstruction"));
65 assert_eq!(node.docs, Docs::default());
66 assert_eq!(
67 node.optional_account_strategy,
68 InstructionOptionalAccountStrategy::ProgramId
69 );
70 assert_eq!(node.accounts, vec![]);
71 assert_eq!(node.arguments, vec![]);
72 assert_eq!(node.extra_arguments, vec![]);
73 assert_eq!(node.remaining_accounts, vec![]);
74 assert_eq!(node.byte_deltas, vec![]);
75 assert_eq!(node.discriminators, vec![]);
76 assert_eq!(node.sub_instructions, vec![]);
77 }
78
79 #[test]
80 fn instruction_with_sub_instructions() {
81 let node = InstructionNode {
82 name: "myInstruction".into(),
83 sub_instructions: vec![
84 InstructionNode {
85 name: "mySubInstructionA".into(),
86 ..InstructionNode::default()
87 },
88 InstructionNode {
89 name: "mySubInstructionB".into(),
90 ..InstructionNode::default()
91 },
92 ],
93 ..InstructionNode::default()
94 };
95 assert_eq!(
96 node.sub_instructions,
97 vec![
98 InstructionNode {
99 name: "mySubInstructionA".into(),
100 ..InstructionNode::default()
101 },
102 InstructionNode {
103 name: "mySubInstructionB".into(),
104 ..InstructionNode::default()
105 },
106 ]
107 );
108 }
109
110 #[test]
111 fn to_json() {
112 let node = InstructionNode {
113 name: "myInstruction".into(),
114 ..InstructionNode::default()
115 };
116 let json = serde_json::to_string(&node).unwrap();
117 assert_eq!(
118 json,
119 r#"{"kind":"instructionNode","name":"myInstruction","accounts":[],"arguments":[]}"#
120 );
121 }
122
123 #[test]
124 fn from_json() {
125 let json =
126 r#"{"kind":"instructionNode","name":"myInstruction","accounts":[],"arguments":[]}"#;
127 let node: InstructionNode = serde_json::from_str(json).unwrap();
128 assert_eq!(
129 node,
130 InstructionNode {
131 name: "myInstruction".into(),
132 ..InstructionNode::default()
133 }
134 );
135 }
136}