partial_idl_parser/
parser.rs

1use serde::Deserialize;
2
3/// The discriminant length of anchor instruction
4pub type AnchorInstructionDiscriminatLen = [u8; 8];
5
6/// Holds the address and instructions parsed from JSON IDL.
7///
8/// 1. It contains the address of the program (Public Key) defined as a String
9///
10/// 2. The instructions of the program
11#[derive(Debug, Deserialize, PartialEq, PartialOrd)]
12pub struct AnchorIdlPartialData {
13    address: String,
14    instructions: Vec<AnchorInstruction>,
15}
16
17impl AnchorIdlPartialData {
18    /// Parse JSON IDL
19    pub fn parse(idl_json_data: &str) -> Result<Self, serde_json::Error> {
20        serde_json::from_str::<Self>(idl_json_data)
21    }
22
23    /// Get the program ID
24    pub fn program_id(&self) -> &str {
25        self.address.as_str()
26    }
27
28    /// Get an instruction by the name defined in the JSON IDL
29    pub fn get_instruction(&self, name: &str) -> Option<&AnchorInstruction> {
30        self.instructions
31            .iter()
32            .find(|instruction| instruction.name.as_bytes() == name.as_bytes())
33    }
34
35    /// Get the discriminant of an instruction given the instruction name
36    pub fn get_discriminant(&self, name: &str) -> Option<AnchorInstructionDiscriminatLen> {
37        self.get_instruction(name).map(|ix| ix.discriminator)
38    }
39
40    /// Get all Instructions
41    pub fn get_instructions(&self) -> &[AnchorInstruction] {
42        self.instructions.as_slice()
43    }
44
45    /// Get all the names for all instructions
46    pub fn get_instruction_names(&self) -> Vec<&str> {
47        self.instructions
48            .iter()
49            .map(|instruction| instruction.name.as_str())
50            .collect::<Vec<&str>>()
51    }
52}
53
54/// An IDL defined instruction
55#[derive(Debug, Deserialize, PartialEq, PartialOrd)]
56pub struct AnchorInstruction {
57    /// Name of the Instruction
58    pub name: String,
59    /// The discriminant of the instruction
60    pub discriminator: [u8; 8],
61}
62
63#[cfg(test)]
64mod sanity_checks {
65    use super::AnchorIdlPartialData;
66
67    #[test]
68    fn correctness() {
69        let idl = "
70            {
71                \"address\": \"3bF44ZTKPSc4qZV97mpRA85NkQaM9D9Z6i3uYjKbs8E6\",
72                \"metadata\": {
73                    \"name\": \"temp\",
74                    \"version\": \"0.1.0\",
75                    \"spec\": \"0.1.0\",
76                    \"description\": \"Created with Anchor\"
77                },
78                \"instructions\": [
79                    {
80                    \"name\": \"initialize\",
81                    \"discriminator\": [
82                        175,
83                        175,
84                        109,
85                        31,
86                        13,
87                        152,
88                        155,
89                        237
90                    ],
91                    \"accounts\": [],
92                    \"args\": []
93                    }
94                ]
95            }
96        ";
97
98        let parse = AnchorIdlPartialData::parse(idl);
99        parse.as_ref().unwrap();
100
101        assert!(parse.is_ok());
102        assert_eq!(
103            parse.as_ref().unwrap().address,
104            "3bF44ZTKPSc4qZV97mpRA85NkQaM9D9Z6i3uYjKbs8E6"
105        );
106
107        assert_eq!(
108            parse
109                .as_ref()
110                .unwrap()
111                .get_instruction("initialize")
112                .map(|found| found.name.as_str()),
113            Some("initialize")
114        );
115        assert_eq!(
116            parse.as_ref().unwrap().get_discriminant("initialize"),
117            Some([175, 175, 109, 31, 13, 152, 155, 237])
118        );
119    }
120}