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    Calldata {
66        bytes: usize,
67        data: String,
68    },
69    /// The variables in scope, and the state variables.
70    Variables {
71        #[serde(skip_serializing_if = "Option::is_none")]
72        warning: Option<String>,
73        pc: u64,
74        locals: Vec<VariableInfo>,
75        /// Why no locals could be read, when none could.
76        #[serde(skip_serializing_if = "Option::is_none")]
77        unavailable: Option<String>,
78        state: StateInfo,
79    },
80    /// One variable, from `print`.
81    Variable {
82        #[serde(skip_serializing_if = "Option::is_none")]
83        warning: Option<String>,
84        variable: VariableInfo,
85    },
86    /// The contracts whose debug resources are loaded.
87    Resources {
88        contracts: Vec<ResourceInfo>,
89        /// Whether the user asked for JSON, so a text renderer prints it as such.
90        json: bool,
91    },
92    Help {
93        lines: Vec<String>,
94    },
95    /// The user asked for the full-screen view.
96    Tui,
97    /// The user asked to leave.
98    Quit,
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
102#[serde(rename_all = "snake_case")]
103pub enum Level {
104    Info,
105    Warning,
106    /// Something worth knowing about what a command did, said under it.
107    Note,
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
111pub struct LoadedContract {
112    pub name: String,
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub address: Option<String>,
115}
116
117/// Why the debugger stopped where it did.
118#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
119#[serde(tag = "reason", rename_all = "snake_case")]
120pub enum StopReason {
121    /// The session started here.
122    Initial,
123    /// A stepping command ended here.
124    Moved,
125    Breakpoint {
126        id: u32,
127        label: String,
128    },
129    /// The end of the recording.
130    End,
131    /// The start of the recording, reached backward.
132    Start,
133}
134
135#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
136pub struct Stop {
137    #[serde(flatten)]
138    pub reason: StopReason,
139    pub step: usize,
140    /// The last step of the recording.
141    pub last_step: usize,
142    pub pc: u64,
143    pub op: String,
144    pub gas: u64,
145    /// Where the step is in the source, when the loaded sources say.
146    #[serde(skip_serializing_if = "Option::is_none")]
147    pub location: Option<StopLocation>,
148    /// The address executing, said when there is no source for the step.
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub address: Option<String>,
151    /// Whether sources are loaded at all, so a step without one can be told from a
152    /// session without any.
153    pub has_source: bool,
154    /// The stack, top first, when the display mode is assembly.
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub stack: Option<Vec<String>>,
157    /// Something the movement turned up: a condition that could not be evaluated, a
158    /// breakpoint that can never be hit.
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub note: Option<String>,
161}
162
163#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
164pub struct StopLocation {
165    pub path: String,
166    pub line: u64,
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub function: Option<String>,
169    /// Compiler-generated code attributed to this line.
170    pub generated: bool,
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub text: Option<String>,
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
176#[serde(rename_all = "snake_case")]
177pub enum BreakpointEvent {
178    Set,
179    Cleared,
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
183pub struct BreakpointInfo {
184    pub id: u32,
185    pub label: String,
186}
187
188#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
189pub struct FrameInfo {
190    pub index: usize,
191    /// The function, or the contract or address executing.
192    pub name: String,
193    pub arguments: Vec<ArgumentInfo>,
194    #[serde(skip_serializing_if = "Option::is_none")]
195    pub location: Option<String>,
196    /// The address, for an external frame whose function is named.
197    #[serde(skip_serializing_if = "Option::is_none")]
198    pub address: Option<String>,
199    pub step: usize,
200    pub pc: u64,
201}
202
203#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
204pub struct ArgumentInfo {
205    pub name: String,
206    pub value: String,
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
210pub struct ListedLine {
211    pub line: u64,
212    pub text: String,
213    pub current: bool,
214}
215
216#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
217pub struct MemoryInfo {
218    /// The size of memory at the step.
219    pub total: usize,
220    pub start: usize,
221    pub end: usize,
222    /// Each word as its offset and its hex.
223    pub words: Vec<MemoryWord>,
224}
225
226#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
227pub struct MemoryWord {
228    pub offset: usize,
229    pub hex: String,
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
233pub struct SlotInfo {
234    pub slot: String,
235    pub value: String,
236    /// The value before this step changed it, when it did.
237    #[serde(skip_serializing_if = "Option::is_none")]
238    pub was: Option<String>,
239}
240
241#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
242pub struct VariableInfo {
243    pub name: String,
244    pub ty: String,
245    pub value: String,
246    #[serde(skip_serializing_if = "Option::is_none")]
247    pub raw: Option<String>,
248    pub status: ValueStatus,
249    /// Where the value was read from: `stack+2`, `slot 0x0`, `slot 0x1 + 16, from the
250    /// chain at block 42`. Absent when the value could not be read.
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub place: Option<String>,
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
256#[serde(rename_all = "snake_case")]
257pub enum ValueStatus {
258    Decoded,
259    Raw,
260    Unavailable,
261}
262
263/// The state variables at a step, or why there are none to show.
264#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
265#[serde(tag = "status", rename_all = "snake_case")]
266pub enum StateInfo {
267    Variables {
268        variables: Vec<VariableInfo>,
269    },
270    /// The contract has a layout that declares no state variables.
271    None,
272    /// No storage layout is loaded for the contract.
273    NoLayout,
274    /// The backend recorded no storage to read them from.
275    NoStorage,
276}
277
278#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
279pub struct ResourceInfo {
280    pub name: String,
281    #[serde(skip_serializing_if = "Option::is_none")]
282    pub address: Option<String>,
283    pub environment: String,
284    pub sources: Vec<String>,
285}