Skip to main content

soldb_repl/
response.rs

1//! What a command answers with, as data.
2//!
3//! A session never prints. It answers each command with [`Output`] values, which a
4//! frontend renders as text ([`crate::Renderer`]), as JSON (every `Output` serializes,
5//! one object per item, tagged by `kind`), or into the panes of a full-screen view.
6//! The same data reaches the terminal, an editor, and a script, so they cannot
7//! disagree about what happened.
8
9use serde::Serialize;
10
11/// One item of a command's answer.
12#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
13#[serde(tag = "kind", rename_all = "snake_case")]
14pub enum Output {
15    /// What was loaded, said once when a session starts.
16    Loaded {
17        steps: usize,
18        /// The contracts whose sources are loaded, name and address.
19        contracts: Vec<LoadedContract>,
20    },
21    /// Where the debugger stopped, and why.
22    Stop(Stop),
23    /// A one-line message, with an optional note under it.
24    Message {
25        level: Level,
26        text: String,
27        #[serde(skip_serializing_if = "Option::is_none")]
28        note: Option<String>,
29    },
30    /// A breakpoint was set or cleared.
31    Breakpoint {
32        event: BreakpointEvent,
33        id: u32,
34        label: String,
35    },
36    Breakpoints {
37        breakpoints: Vec<BreakpointInfo>,
38    },
39    Backtrace {
40        /// Said once a session, when frames carry arguments read off the stack: a
41        /// warning line and the note under it.
42        #[serde(skip_serializing_if = "Option::is_none")]
43        warning: Option<String>,
44        #[serde(skip_serializing_if = "Option::is_none")]
45        note: Option<String>,
46        frames: Vec<FrameInfo>,
47    },
48    Listing {
49        path: String,
50        current_line: u64,
51        lines: Vec<ListedLine>,
52    },
53    Stack {
54        /// The whole stack, bottom first: index `i` is what `vars` calls `stack+i`.
55        words: Vec<String>,
56        /// How many words, from the top, the user asked to see.
57        shown: usize,
58    },
59    Memory(MemoryInfo),
60    Storage {
61        #[serde(skip_serializing_if = "Option::is_none")]
62        address: Option<String>,
63        slots: Vec<SlotInfo>,
64    },
65    /// The transient storage slots known at a step; every other slot is zero.
66    TransientStorage {
67        #[serde(skip_serializing_if = "Option::is_none")]
68        address: Option<String>,
69        slots: Vec<SlotInfo>,
70    },
71    Calldata {
72        bytes: usize,
73        data: String,
74    },
75    /// The return data buffer at a step, `0x`-prefixed.
76    ReturnData {
77        bytes: usize,
78        data: String,
79    },
80    /// The variables in scope, and the state variables.
81    Variables {
82        #[serde(skip_serializing_if = "Option::is_none")]
83        warning: Option<String>,
84        pc: u64,
85        locals: Vec<VariableInfo>,
86        /// Why no locals could be read, when none could.
87        #[serde(skip_serializing_if = "Option::is_none")]
88        unavailable: Option<String>,
89        state: StateInfo,
90        /// The `transient` state variables, when the contract has a transient layout.
91        #[serde(skip_serializing_if = "Vec::is_empty")]
92        transient: Vec<VariableInfo>,
93    },
94    /// One variable, from `print`.
95    Variable {
96        #[serde(skip_serializing_if = "Option::is_none")]
97        warning: Option<String>,
98        variable: VariableInfo,
99    },
100    /// The contracts whose debug resources are loaded.
101    Resources {
102        contracts: Vec<ResourceInfo>,
103        /// Whether the user asked for JSON, so a text renderer prints it as such.
104        json: bool,
105    },
106    Help {
107        lines: Vec<String>,
108    },
109    /// The user asked for the full-screen view.
110    Tui,
111    /// The user asked to leave.
112    Quit,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
116#[serde(rename_all = "snake_case")]
117pub enum Level {
118    Info,
119    Warning,
120    /// Something worth knowing about what a command did, said under it.
121    Note,
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
125pub struct LoadedContract {
126    pub name: String,
127    #[serde(skip_serializing_if = "Option::is_none")]
128    pub address: Option<String>,
129}
130
131/// Why the debugger stopped where it did.
132#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
133#[serde(tag = "reason", rename_all = "snake_case")]
134pub enum StopReason {
135    /// The session started here.
136    Initial,
137    /// A stepping command ended here.
138    Moved,
139    Breakpoint {
140        id: u32,
141        label: String,
142    },
143    /// The end of the recording.
144    End,
145    /// The start of the recording, reached backward.
146    Start,
147}
148
149#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
150pub struct Stop {
151    #[serde(flatten)]
152    pub reason: StopReason,
153    pub step: usize,
154    /// The last step of the recording.
155    pub last_step: usize,
156    pub pc: u64,
157    pub op: String,
158    pub gas: u64,
159    /// Where the step is in the source, when the loaded sources say.
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub location: Option<StopLocation>,
162    /// The address executing, said when there is no source for the step.
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub address: Option<String>,
165    /// Whether sources are loaded at all, so a step without one can be told from a
166    /// session without any.
167    pub has_source: bool,
168    /// The stack, top first, when the display mode is assembly.
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub stack: Option<Vec<String>>,
171    /// Something the movement turned up: a condition that could not be evaluated, a
172    /// breakpoint that can never be hit.
173    #[serde(skip_serializing_if = "Option::is_none")]
174    pub note: Option<String>,
175}
176
177#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
178pub struct StopLocation {
179    pub path: String,
180    pub line: u64,
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub function: Option<String>,
183    /// Compiler-generated code attributed to this line.
184    pub generated: bool,
185    #[serde(skip_serializing_if = "Option::is_none")]
186    pub text: Option<String>,
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
190#[serde(rename_all = "snake_case")]
191pub enum BreakpointEvent {
192    Set,
193    Cleared,
194}
195
196#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
197pub struct BreakpointInfo {
198    pub id: u32,
199    pub label: String,
200}
201
202#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
203pub struct FrameInfo {
204    pub index: usize,
205    /// The function, or the contract or address executing.
206    pub name: String,
207    pub arguments: Vec<ArgumentInfo>,
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub location: Option<String>,
210    /// The address, for an external frame whose function is named.
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub address: Option<String>,
213    pub step: usize,
214    pub pc: u64,
215    /// For a root frame, what its top-level call is, when the trace says.
216    #[serde(skip_serializing_if = "Option::is_none")]
217    pub kind: Option<String>,
218}
219
220#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
221pub struct ArgumentInfo {
222    pub name: String,
223    pub value: String,
224}
225
226#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
227pub struct ListedLine {
228    pub line: u64,
229    pub text: String,
230    pub current: bool,
231}
232
233#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
234pub struct MemoryInfo {
235    /// The size of memory at the step.
236    pub total: usize,
237    pub start: usize,
238    pub end: usize,
239    /// Each word as its offset and its hex.
240    pub words: Vec<MemoryWord>,
241}
242
243#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
244pub struct MemoryWord {
245    pub offset: usize,
246    pub hex: String,
247}
248
249#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
250pub struct SlotInfo {
251    pub slot: String,
252    pub value: String,
253    /// The value before this step changed it, when it did.
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub was: Option<String>,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
259pub struct VariableInfo {
260    pub name: String,
261    pub ty: String,
262    pub value: String,
263    #[serde(skip_serializing_if = "Option::is_none")]
264    pub raw: Option<String>,
265    pub status: ValueStatus,
266    /// Where the value was read from: `stack+2`, `slot 0x0`, `slot 0x1 + 16, from the
267    /// chain at block 42`. Absent when the value could not be read.
268    #[serde(skip_serializing_if = "Option::is_none")]
269    pub place: Option<String>,
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
273#[serde(rename_all = "snake_case")]
274pub enum ValueStatus {
275    Decoded,
276    Raw,
277    Unavailable,
278}
279
280/// The state variables at a step, or why there are none to show.
281#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
282#[serde(tag = "status", rename_all = "snake_case")]
283pub enum StateInfo {
284    Variables {
285        variables: Vec<VariableInfo>,
286    },
287    /// The contract has a layout that declares no state variables.
288    None,
289    /// No storage layout is loaded for the contract.
290    NoLayout,
291    /// The backend recorded no storage to read them from.
292    NoStorage,
293}
294
295#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
296pub struct ResourceInfo {
297    pub name: String,
298    #[serde(skip_serializing_if = "Option::is_none")]
299    pub address: Option<String>,
300    pub environment: String,
301    pub sources: Vec<String>,
302}