1use {
2 crate::error::{DebuggerError, DebuggerResult},
3 serde::Deserialize,
4 solana_account::Account,
5 solana_address::Address,
6 solana_instruction::{AccountMeta, Instruction},
7 std::{fs, path::Path, str::FromStr},
8};
9
10#[derive(Deserialize)]
11struct DebuggerInput {
12 instruction: InstructionJson,
13 accounts: Vec<AccountJson>,
14 #[serde(default)]
15 programs: Vec<ProgramJson>,
16}
17
18#[derive(Deserialize)]
19struct InstructionJson {
20 program_id: String,
21 accounts: Vec<AccountMetaJson>,
22 #[serde(default)]
23 data: String,
24}
25
26#[derive(Deserialize)]
27struct AccountMetaJson {
28 pubkey: String,
29 is_signer: bool,
30 is_writable: bool,
31}
32
33#[derive(Deserialize)]
34struct AccountJson {
35 pubkey: String,
36 owner: String,
37 lamports: u64,
38 #[serde(default)]
39 data: String,
40 #[serde(default)]
41 executable: bool,
42}
43
44#[derive(Deserialize)]
45struct ProgramJson {
46 program_id: String,
47 elf: String,
48}
49
50pub struct ParsedInput {
51 pub instruction: Instruction,
52 pub accounts: Vec<(Address, Account)>,
53 pub programs: Vec<(Address, Vec<u8>)>,
54}
55
56pub fn parse_input(input: &str) -> DebuggerResult<ParsedInput> {
57 let input = input.trim();
58 if input.is_empty() {
59 let program_id = Address::new_unique();
60 return Ok(ParsedInput {
61 instruction: Instruction::new_with_bytes(program_id, &[], vec![]),
62 accounts: Vec::new(),
63 programs: Vec::new(),
64 });
65 }
66
67 let input_path = Path::new(input);
69 let (json_str, base_dir) = if input_path.exists() {
70 let base = input_path.parent().unwrap_or(Path::new(".")).to_path_buf();
71 (fs::read_to_string(input)?, base)
72 } else {
73 (input.to_string(), Path::new(".").to_path_buf())
74 };
75
76 let debugger_input: DebuggerInput =
77 serde_json::from_str(&json_str).map_err(|e| DebuggerError::InvalidInput(e.to_string()))?;
78
79 let program_id = Address::from_str(&debugger_input.instruction.program_id)
80 .map_err(|e| DebuggerError::InvalidInput(format!("Invalid program_id: {}", e)))?;
81
82 let account_metas: Vec<AccountMeta> = debugger_input
83 .instruction
84 .accounts
85 .iter()
86 .map(|a| {
87 let pubkey = Address::from_str(&a.pubkey)
88 .map_err(|e| DebuggerError::InvalidInput(format!("Invalid pubkey: {}", e)))?;
89 Ok(AccountMeta {
90 pubkey,
91 is_signer: a.is_signer,
92 is_writable: a.is_writable,
93 })
94 })
95 .collect::<DebuggerResult<Vec<_>>>()?;
96
97 let instruction_data = if debugger_input.instruction.data.is_empty() {
98 Vec::new()
99 } else {
100 bs58::decode(&debugger_input.instruction.data)
101 .into_vec()
102 .map_err(|e| {
103 DebuggerError::InvalidInput(format!("Invalid base58 instruction data: {}", e))
104 })?
105 };
106
107 let instruction = Instruction::new_with_bytes(program_id, &instruction_data, account_metas);
108
109 let accounts: Vec<(Address, Account)> = debugger_input
110 .accounts
111 .iter()
112 .map(|a| {
113 let pubkey = Address::from_str(&a.pubkey)
114 .map_err(|e| DebuggerError::InvalidInput(format!("Invalid pubkey: {}", e)))?;
115 let owner = Address::from_str(&a.owner)
116 .map_err(|e| DebuggerError::InvalidInput(format!("Invalid owner: {}", e)))?;
117 let data = if a.data.is_empty() {
118 Vec::new()
119 } else {
120 bs58::decode(&a.data).into_vec().map_err(|e| {
121 DebuggerError::InvalidInput(format!("Invalid base58 account data: {}", e))
122 })?
123 };
124 Ok((
125 pubkey,
126 Account {
127 lamports: a.lamports,
128 data,
129 owner,
130 executable: a.executable,
131 rent_epoch: 0,
132 },
133 ))
134 })
135 .collect::<DebuggerResult<Vec<_>>>()?;
136
137 let programs: Vec<(Address, Vec<u8>)> = debugger_input
138 .programs
139 .iter()
140 .map(|p| {
141 let program_id = Address::from_str(&p.program_id)
142 .map_err(|e| DebuggerError::InvalidInput(format!("Invalid program_id: {}", e)))?;
143 let elf_path = base_dir.join(&p.elf);
144 let elf_bytes = fs::read(&elf_path).map_err(|e| {
145 DebuggerError::InvalidInput(format!(
146 "Failed to read ELF at {}: {}",
147 elf_path.display(),
148 e
149 ))
150 })?;
151 Ok((program_id, elf_bytes))
152 })
153 .collect::<DebuggerResult<Vec<_>>>()?;
154
155 Ok(ParsedInput {
156 instruction,
157 accounts,
158 programs,
159 })
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_parse_empty_input() {
168 let parsed = parse_input("").unwrap();
169 assert!(parsed.accounts.is_empty());
170 }
171
172 #[test]
173 fn test_parse_json_string() {
174 let program_id = Address::new_unique();
175 let account_pubkey = Address::new_unique();
176 let owner = Address::new_unique();
177
178 let json = format!(
179 r#"{{
180 "instruction": {{
181 "program_id": "{}",
182 "accounts": [
183 {{ "pubkey": "{}", "is_signer": true, "is_writable": true }}
184 ],
185 "data": "q"
186 }},
187 "accounts": [
188 {{
189 "pubkey": "{}",
190 "owner": "{}",
191 "lamports": 1000000,
192 "data": "",
193 "executable": false
194 }}
195 ]
196 }}"#,
197 program_id, account_pubkey, account_pubkey, owner
198 );
199
200 let parsed = parse_input(&json).unwrap();
201 assert_eq!(parsed.instruction.program_id, program_id);
202 assert_eq!(parsed.accounts.len(), 1);
203 assert_eq!(parsed.accounts[0].0, account_pubkey);
204 }
205}