chainerrors_solana/
log_parser.rs1#[derive(Debug, Clone, PartialEq)]
11pub enum ParsedError {
12 Code(u32),
14 Message(String),
16}
17
18pub fn parse_program_error(log_line: &str) -> Option<ParsedError> {
22 if let Some(rest) = log_line
24 .find("custom program error: 0x")
25 .map(|pos| &log_line[pos + 24..])
26 {
27 let hex_str: String = rest.chars().take_while(|c| c.is_ascii_hexdigit()).collect();
28 if !hex_str.is_empty() {
29 if let Ok(code) = u32::from_str_radix(&hex_str, 16) {
30 return Some(ParsedError::Code(code));
31 }
32 }
33 }
34
35 if let Some(rest) = log_line
37 .find("custom program error: ")
38 .map(|pos| &log_line[pos + 22..])
39 {
40 let num_str: String = rest.chars().take_while(|c| c.is_ascii_digit()).collect();
41 if !num_str.is_empty() {
42 if let Ok(code) = num_str.parse::<u32>() {
43 return Some(ParsedError::Code(code));
44 }
45 }
46 }
47
48 if let Some(pos) = log_line.find("Program log: Error: ") {
50 let message = log_line[pos + 20..].trim().to_string();
51 if !message.is_empty() {
52 return Some(ParsedError::Message(message));
53 }
54 }
55
56 if let Some(pos) = log_line.find("Error Number: ") {
60 let rest = &log_line[pos + 14..];
61 let num_str: String = rest.chars().take_while(|c| c.is_ascii_digit()).collect();
62 if !num_str.is_empty() {
63 if let Ok(code) = num_str.parse::<u32>() {
64 return Some(ParsedError::Code(code));
65 }
66 }
67 }
68
69 if let Some(pos) = log_line.find("Error Message: ") {
71 let message = log_line[pos + 15..].trim().to_string();
72 if !message.is_empty() {
73 return Some(ParsedError::Message(message));
74 }
75 }
76
77 None
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn parse_hex_error_code() {
86 let line = "Program ABC123 failed: custom program error: 0x1";
87 assert_eq!(parse_program_error(line), Some(ParsedError::Code(1)));
88 }
89
90 #[test]
91 fn parse_hex_error_code_large() {
92 let line = "Program ABC123 failed: custom program error: 0xbc4";
93 assert_eq!(parse_program_error(line), Some(ParsedError::Code(0xbc4)));
94 }
95
96 #[test]
97 fn parse_decimal_error_code() {
98 let line = "Program failed: custom program error: 3012";
99 assert_eq!(parse_program_error(line), Some(ParsedError::Code(3012)));
100 }
101
102 #[test]
103 fn parse_error_message() {
104 let line = "Program log: Error: insufficient funds";
105 assert_eq!(
106 parse_program_error(line),
107 Some(ParsedError::Message("insufficient funds".to_string()))
108 );
109 }
110
111 #[test]
112 fn parse_anchor_error_message() {
113 let line = "Error Message: A seeds constraint was violated.";
114 assert_eq!(
115 parse_program_error(line),
116 Some(ParsedError::Message(
117 "A seeds constraint was violated.".to_string()
118 ))
119 );
120 }
121
122 #[test]
123 fn parse_anchor_error_number() {
124 let line = "Error Code: AccountNotInitialized. Error Number: 3012. Error Message: Account is not initialized.";
125 assert_eq!(parse_program_error(line), Some(ParsedError::Code(3012)));
126 }
127
128 #[test]
129 fn parse_unrecognized_returns_none() {
130 assert!(parse_program_error("Program log: some info").is_none());
131 assert!(parse_program_error("random text").is_none());
132 assert!(parse_program_error("").is_none());
133 }
134
135 #[test]
136 fn parse_zero_error_code() {
137 let line = "Program failed: custom program error: 0x0";
138 assert_eq!(parse_program_error(line), Some(ParsedError::Code(0)));
139 }
140}