1use std::fmt;
2
3use crate::{Action, Hex, Pack};
4use fracpack::Unpack;
5use serde::{Deserialize, Serialize};
6use serde_aux::prelude::deserialize_number_from_string;
7
8#[derive(Debug, Clone, Pack, Unpack, Serialize, Deserialize)]
9#[fracpack(fracpack_mod = "fracpack")]
10#[serde(rename_all = "camelCase")]
11pub struct ActionTrace {
12 pub action: Action,
13 pub raw_retval: Hex<Vec<u8>>,
14 pub inner_traces: Vec<InnerTrace>,
15 #[serde(deserialize_with = "deserialize_number_from_string")]
16 pub total_time: i64,
17 pub error: Option<String>,
18}
19
20impl fmt::Display for ActionTrace {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 format_action_trace(self, 0, f)
23 }
24}
25
26#[derive(Debug, Clone, Pack, Unpack, Serialize, Deserialize)]
27#[fracpack(fracpack_mod = "fracpack")]
28#[serde(rename_all = "camelCase")]
29pub struct EventTrace {
30 pub name: String,
31 pub data: Hex<Vec<u8>>,
32}
33
34#[derive(Debug, Clone, Pack, Unpack, Serialize, Deserialize)]
35#[fracpack(fracpack_mod = "fracpack")]
36#[serde(rename_all = "camelCase")]
37pub struct ConsoleTrace {
38 pub console: String,
39}
40
41#[derive(Debug, Clone, Pack, Unpack, Serialize, Deserialize)]
42#[fracpack(fracpack_mod = "fracpack")]
43pub enum InnerTraceEnum {
44 ConsoleTrace(ConsoleTrace),
45 EventTrace(EventTrace),
46 ActionTrace(ActionTrace),
47}
48
49#[derive(Debug, Clone, Pack, Unpack, Serialize, Deserialize)]
50#[fracpack(fracpack_mod = "fracpack")]
51#[serde(rename_all = "camelCase")]
52pub struct InnerTrace {
53 pub inner: InnerTraceEnum,
54}
55
56#[derive(Debug, Clone, Pack, Unpack, Serialize, Deserialize)]
57#[fracpack(fracpack_mod = "fracpack")]
58#[serde(rename_all = "camelCase")]
59pub struct TransactionTrace {
60 pub action_traces: Vec<ActionTrace>,
61 pub error: Option<String>,
62}
63
64impl TransactionTrace {
65 pub fn ok(self) -> Result<TransactionTrace, anyhow::Error> {
66 if let Some(e) = self.error {
67 Err(anyhow::anyhow!("{}", e))
68 } else {
69 Ok(self)
70 }
71 }
72
73 pub fn match_error(self, msg: &str) -> Result<TransactionTrace, anyhow::Error> {
74 if let Some(e) = &self.error {
75 if e.contains(msg) {
76 Ok(self)
77 } else {
78 Err(anyhow::anyhow!(
79 "Transaction was expected to fail with \"{}\", but failed with \"{}\"",
80 msg,
81 e
82 ))
83 }
84 } else {
85 Err(anyhow::anyhow!(
86 "Transaction was expected to fail with \"{}\", but succeeded",
87 msg,
88 ))
89 }
90 }
91 pub fn console(&self) -> TraceConsole<'_> {
92 return TraceConsole { trace: self };
93 }
94}
95
96impl fmt::Display for TransactionTrace {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 format_transaction_trace(self, 0, f)
99 }
100}
101
102pub struct TraceConsole<'a> {
103 trace: &'a TransactionTrace,
104}
105
106impl fmt::Display for TraceConsole<'_> {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 for atrace in &self.trace.action_traces {
109 write_action_console(atrace, f)?;
110 }
111 Ok(())
112 }
113}
114
115fn write_action_console(atrace: &ActionTrace, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 for inner in &atrace.inner_traces {
117 match &inner.inner {
118 InnerTraceEnum::ConsoleTrace(c) => write!(f, "{}", &c.console)?,
119 InnerTraceEnum::EventTrace(_) => {}
120 InnerTraceEnum::ActionTrace(a) => write_action_console(a, f)?,
121 }
122 }
123 Ok(())
124}
125
126fn format_string(mut s: &str, indent: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 while let Some(nl) = s.find('\n') {
128 write!(f, "{}", &s[..nl])?;
129 s = &s[nl + 1..];
130 if !s.is_empty() {
131 write!(f, "\n{:indent$}", "")?;
132 }
133 }
134 write!(f, "{}", s)
135}
136
137fn format_console(c: &ConsoleTrace, indent: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 write!(f, "{:indent$}console: ", "")?;
139 format_string(&c.console, indent + 12, f)
140}
141
142fn format_event(_: &EventTrace, indent: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 writeln!(f, "{:indent$}event", "")
144}
145
146fn format_action_trace(
147 atrace: &ActionTrace,
148 indent: usize,
149 f: &mut fmt::Formatter<'_>,
150) -> fmt::Result {
151 writeln!(f, "{:indent$}action:", "")?;
152 writeln!(
153 f,
154 "{:indent$} {} => {}::{}",
155 "", atrace.action.sender, atrace.action.service, atrace.action.method
156 )?;
157 writeln!(
158 f,
159 "{:indent$} raw_retval: {} bytes",
160 "",
161 atrace.raw_retval.len()
162 )?;
163 for inner in &atrace.inner_traces {
164 match &inner.inner {
165 InnerTraceEnum::ConsoleTrace(c) => format_console(c, indent + 4, f)?,
166 InnerTraceEnum::EventTrace(e) => format_event(e, indent + 4, f)?,
167 InnerTraceEnum::ActionTrace(a) => format_action_trace(a, indent + 4, f)?,
168 }
169 }
170 Ok(())
171}
172
173fn format_transaction_trace(
174 ttrace: &TransactionTrace,
175 indent: usize,
176 f: &mut fmt::Formatter<'_>,
177) -> fmt::Result {
178 for a in &ttrace.action_traces {
179 format_action_trace(a, indent, f)?;
180 }
181 if let Some(e) = &ttrace.error {
182 write!(f, "{:indent$}error: ", "")?;
183 format_string(e, indent + 11, f)?;
184 }
185 Ok(())
186}
187
188fn format_error_stack<T: std::fmt::Write>(atrace: &ActionTrace, f: &mut T) -> fmt::Result {
189 if atrace.error.is_some() {
190 writeln!(
191 f,
192 "{} => {}::{}",
193 atrace.action.sender, atrace.action.service, atrace.action.method
194 )?;
195 for inner in &atrace.inner_traces {
196 match &inner.inner {
197 InnerTraceEnum::ConsoleTrace(_) => (),
198 InnerTraceEnum::EventTrace(_) => (),
199 InnerTraceEnum::ActionTrace(a) => format_error_stack(a, f)?,
200 }
201 }
202 }
203 Ok(())
204}
205
206fn format_transaction_error_stack<T: std::fmt::Write>(
207 ttrace: &TransactionTrace,
208 f: &mut T,
209) -> fmt::Result {
210 for a in &ttrace.action_traces {
211 format_error_stack(a, f)?;
212 break;
213 }
214 if let Some(message) = &ttrace.error {
215 writeln!(f, "{}", message)?;
216 }
217 Ok(())
218}
219
220impl TransactionTrace {
221 pub fn fmt_stack(&self) -> String {
222 let mut result = String::new();
223 format_transaction_error_stack(self, &mut result).unwrap();
224 result
225 }
226}