codama_nodes/
instruction_node.rs

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