Skip to main content

codama_nodes/
instruction_argument_node.rs

1use crate::{
2    CamelCaseString, Docs, InstructionArgumentNode, InstructionInputValueNode, StructFieldTypeNode,
3    StructTypeNode, TypeNode,
4};
5
6impl InstructionArgumentNode {
7    pub fn new<T, U>(name: T, r#type: U) -> Self
8    where
9        T: Into<CamelCaseString>,
10        U: Into<TypeNode>,
11    {
12        Self {
13            name: name.into(),
14            default_value_strategy: None,
15            docs: Docs::default(),
16            r#type: Box::new(r#type.into()),
17            default_value: Box::new(None),
18            display: None,
19        }
20    }
21}
22
23impl From<StructFieldTypeNode> for InstructionArgumentNode {
24    fn from(value: StructFieldTypeNode) -> Self {
25        Self {
26            name: value.name,
27            default_value_strategy: value.default_value_strategy,
28            docs: value.docs,
29            // `StructFieldTypeNode.r#type` is `Box<TypeNode>`;
30            // `InstructionArgumentNode.r#type` is also `Box<TypeNode>`,
31            // so forward the existing box.
32            r#type: value.r#type,
33            default_value: Box::new((*value.default_value).map(InstructionInputValueNode::from)),
34            // Both nodes carry display metadata as `structFieldDisplayNode`,
35            // so the member's presentation survives the conversion.
36            display: value.display,
37        }
38    }
39}
40
41impl From<StructTypeNode> for Vec<InstructionArgumentNode> {
42    fn from(val: StructTypeNode) -> Self {
43        val.fields
44            .into_iter()
45            .map(InstructionArgumentNode::from)
46            .collect()
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use crate::{ArgumentValueNode, DefaultValueStrategy, NumberTypeNode, U32};
54
55    #[test]
56    fn new() {
57        let node = InstructionArgumentNode::new("my_argument", NumberTypeNode::le(U32));
58        assert_eq!(node.name, CamelCaseString::new("myArgument"));
59        assert_eq!(*node.r#type, TypeNode::Number(NumberTypeNode::le(U32)));
60    }
61
62    #[test]
63    fn direct_instantiation() {
64        let node = InstructionArgumentNode {
65            name: "myArgument".into(),
66            default_value_strategy: Some(DefaultValueStrategy::Optional),
67            docs: vec!["Hello".to_string()].into(),
68            r#type: Box::new(NumberTypeNode::le(U32).into()),
69            default_value: Box::new(Some(ArgumentValueNode::new("myOtherArgument").into())),
70            display: None,
71        };
72
73        assert_eq!(node.name, CamelCaseString::new("myArgument"));
74        assert_eq!(
75            node.default_value_strategy,
76            Some(DefaultValueStrategy::Optional)
77        );
78        assert_eq!(*node.docs, vec!["Hello".to_string()]);
79        assert_eq!(*node.r#type, TypeNode::Number(NumberTypeNode::le(U32)));
80        assert_eq!(
81            *node.default_value,
82            Some(InstructionInputValueNode::ArgumentValue(
83                ArgumentValueNode::new("myOtherArgument")
84            ))
85        );
86    }
87
88    #[test]
89    fn to_json() {
90        let node = InstructionArgumentNode::new("myArgument", NumberTypeNode::le(U32));
91        let json = serde_json::to_string(&node).unwrap();
92        assert_eq!(
93            json,
94            r#"{"kind":"instructionArgumentNode","name":"myArgument","type":{"kind":"numberTypeNode","format":"u32","endian":"le"}}"#
95        );
96    }
97
98    #[test]
99    fn from_json() {
100        let json = r#"{"kind":"instructionArgumentNode","name":"myArgument","type":{"kind":"numberTypeNode","format":"u32","endian":"le"}}"#;
101        let node: InstructionArgumentNode = serde_json::from_str(json).unwrap();
102        assert_eq!(
103            node,
104            InstructionArgumentNode::new("myArgument", NumberTypeNode::le(U32))
105        );
106    }
107}