atlas_arch/
schema.rs

1use arch_program::account::AccountMeta;
2use arch_program::pubkey::Pubkey;
3use {
4    crate::{collection::InstructionDecoderCollection, instruction::DecodedInstruction},
5    serde::de::DeserializeOwned,
6    std::collections::HashMap,
7};
8
9#[derive(Debug, Clone)]
10pub enum SchemaNode<T: InstructionDecoderCollection> {
11    Instruction(InstructionSchemaNode<T>),
12    Any,
13}
14
15#[derive(Debug, Clone)]
16pub struct InstructionSchemaNode<T: InstructionDecoderCollection> {
17    pub ix_type: T::InstructionType,
18    pub name: String,
19}
20
21#[derive(Debug, Clone)]
22pub struct ParsedInstruction<T: InstructionDecoderCollection> {
23    pub program_id: Pubkey,
24    pub instruction: DecodedInstruction<T>,
25    pub inner_instructions: Vec<ParsedInstruction<T>>,
26}
27
28#[derive(Debug, Clone)]
29pub struct TransactionSchema<T: InstructionDecoderCollection> {
30    pub root: Vec<SchemaNode<T>>,
31}
32
33impl<T: InstructionDecoderCollection> TransactionSchema<T> {
34    pub fn match_schema<U>(&self, instructions: &[ParsedInstruction<T>]) -> Option<U>
35    where
36        U: DeserializeOwned,
37    {
38        log::trace!(
39            "Schema::match_schema(self: {:?}, instructions: {:?})",
40            self,
41            instructions
42        );
43        let value = serde_json::to_value(self.match_nodes(instructions)).ok()?;
44
45        log::trace!("Schema::match_schema: deserializing value: {:?}", value);
46        serde_json::from_value::<U>(value).ok()
47    }
48
49    pub fn match_nodes(
50        &self,
51        instructions: &[ParsedInstruction<T>],
52    ) -> Option<HashMap<String, (T, Vec<AccountMeta>)>> {
53        log::trace!(
54            "Schema::match_nodes(self: {:?}, instructions: {:?})",
55            self,
56            instructions
57        );
58        let mut output = HashMap::<String, (T, Vec<AccountMeta>)>::new();
59
60        let mut node_index = 0;
61        let mut instruction_index = 0;
62
63        let mut any = false;
64
65        while let Some(node) = self.root.get(node_index) {
66            log::trace!(
67                "Schema::match_nodes: current node ({}): {:?}",
68                node_index,
69                node
70            );
71
72            if let SchemaNode::Any = node {
73                log::trace!("Schema::match_nodes: Any node detected, skipping");
74                any = true;
75                node_index += 1;
76                continue;
77            }
78
79            let mut matched = false;
80
81            while let Some(current_instruction) = instructions.get(instruction_index) {
82                log::trace!(
83                    "Schema::match_nodes: current instruction ({}): {:?}",
84                    instruction_index,
85                    current_instruction
86                );
87
88                let SchemaNode::Instruction(instruction_node) = node else {
89                    return None;
90                };
91
92                if current_instruction.instruction.data.get_type() != instruction_node.ix_type
93                    && !any
94                {
95                    log::trace!(
96                        "Schema::match_nodes: instruction type mismatch, returning (any = false)"
97                    );
98                    return None;
99                }
100
101                if current_instruction.instruction.data.get_type() != instruction_node.ix_type
102                    && any
103                {
104                    log::trace!(
105                        "Schema::match_nodes: instruction type mismatch, skipping (any = true)"
106                    );
107                    instruction_index += 1;
108                    continue;
109                }
110
111                output.insert(
112                    instruction_node.name.clone(),
113                    (
114                        current_instruction.instruction.data.clone(),
115                        current_instruction.instruction.accounts.clone(),
116                    ),
117                );
118
119                log::trace!(
120                    "Schema::match_nodes: instruction matched, output: {:?}",
121                    output
122                );
123
124                instruction_index += 1;
125                node_index += 1;
126                any = false;
127                matched = true;
128                break;
129            }
130
131            if !matched {
132                log::trace!("Schema::match_nodes: node not matched, returning");
133                return None;
134            }
135        }
136
137        log::trace!("Schema::match_nodes: final output: {:?}", output);
138
139        Some(output)
140    }
141}
142
143pub fn merge_hashmaps<K, V>(
144    a: HashMap<K, (V, Vec<AccountMeta>)>,
145    b: HashMap<K, (V, Vec<AccountMeta>)>,
146) -> HashMap<K, (V, Vec<AccountMeta>)>
147where
148    K: std::cmp::Eq + std::hash::Hash,
149{
150    log::trace!("merge_hashmaps(a, b)");
151    let mut output = a;
152    for (key, value) in b {
153        output.insert(key, value);
154    }
155    output
156}