Skip to main content

psibase/
trace.rs

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}