Skip to main content

edb_tui/ui/
status.rs

1// EDB - Ethereum Debugger
2// Copyright (C) 2024 Zhuo Zhang and Wuqi Zhang
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Affero General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17//! Enhanced status icon system for the TUI
18//!
19//! Provides comprehensive status indicators with contextual icons and animations
20
21use crate::ui::icons::Icons;
22use ratatui::style::{Color, Style};
23
24/// Connection status with appropriate icons and colors
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum ConnectionStatus {
27    /// Connected to RPC server
28    Connected,
29    /// Connecting to RPC server
30    Connecting,
31    /// Disconnected from RPC server
32    Disconnected,
33    /// Connection error occurred
34    Error,
35}
36
37impl ConnectionStatus {
38    /// Get the appropriate icon for this connection status
39    pub fn icon(&self) -> &'static str {
40        match self {
41            Self::Connected => "🟒",
42            Self::Connecting => "🟑",
43            Self::Disconnected => "πŸ”΄",
44            Self::Error => "❌",
45        }
46    }
47
48    /// Get the appropriate color for this connection status
49    pub fn color(&self) -> Color {
50        match self {
51            Self::Connected => Color::Green,
52            Self::Connecting => Color::Yellow,
53            Self::Disconnected => Color::Red,
54            Self::Error => Color::Red,
55        }
56    }
57
58    /// Get a descriptive text for this connection status
59    pub fn text(&self) -> &'static str {
60        match self {
61            Self::Connected => "Connected",
62            Self::Connecting => "Connecting",
63            Self::Disconnected => "Disconnected",
64            Self::Error => "Connection Error",
65        }
66    }
67
68    /// Get formatted status display with icon and text
69    pub fn display(&self) -> String {
70        format!("{} {}", self.icon(), self.text())
71    }
72}
73
74/// RPC operation status with contextual feedback
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum RpcStatus {
77    /// Operation completed successfully
78    Success,
79    /// Operation failed with error
80    Error,
81    /// Operation in progress
82    Loading,
83    /// Operation timed out
84    Timeout,
85    /// No operation (idle state)
86    Idle,
87}
88
89impl RpcStatus {
90    /// Get the appropriate icon for this RPC status
91    pub fn icon(&self) -> &'static str {
92        match self {
93            Self::Success => Icons::SUCCESS,
94            Self::Error => Icons::ERROR,
95            Self::Loading => Icons::PROCESSING,
96            Self::Timeout => Icons::WARNING,
97            Self::Idle => "⏸️",
98        }
99    }
100
101    /// Get the appropriate color for this RPC status
102    pub fn color(&self) -> Color {
103        match self {
104            Self::Success => Color::Green,
105            Self::Error => Color::Red,
106            Self::Loading => Color::Blue,
107            Self::Timeout => Color::Yellow,
108            Self::Idle => Color::Gray,
109        }
110    }
111
112    /// Get formatted display with icon
113    pub fn display(&self, operation: &str) -> String {
114        match self {
115            Self::Success => format!("{} {}", self.icon(), operation),
116            Self::Error => format!("{} {} Failed", self.icon(), operation),
117            Self::Loading => format!("{} {}...", self.icon(), operation),
118            Self::Timeout => format!("{} {} Timeout", self.icon(), operation),
119            Self::Idle => "Ready".to_string(),
120        }
121    }
122}
123
124/// Debug execution status with appropriate visual feedback
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126pub enum ExecutionStatus {
127    /// Currently executing/stepping through code
128    Running,
129    /// Paused at breakpoint
130    Paused,
131    /// Execution completed successfully
132    Finished,
133    /// Execution failed with error
134    Failed,
135    /// At start of execution
136    Start,
137    /// At end of execution
138    End,
139}
140
141impl ExecutionStatus {
142    /// Get the appropriate icon for this execution status
143    pub fn icon(&self) -> &'static str {
144        match self {
145            Self::Running => "▢️",
146            Self::Paused => "⏸️",
147            Self::Finished => Icons::SUCCESS,
148            Self::Failed => Icons::ERROR,
149            Self::Start => "🏁",
150            Self::End => "🏁",
151        }
152    }
153
154    /// Get the appropriate color for this execution status
155    pub fn color(&self) -> Color {
156        match self {
157            Self::Running => Color::Green,
158            Self::Paused => Color::Yellow,
159            Self::Finished => Color::Green,
160            Self::Failed => Color::Red,
161            Self::Start => Color::Blue,
162            Self::End => Color::Blue,
163        }
164    }
165
166    /// Get formatted display text
167    pub fn display(&self) -> String {
168        match self {
169            Self::Running => format!("{} Running", self.icon()),
170            Self::Paused => format!("{} Paused", self.icon()),
171            Self::Finished => format!("{} Finished", self.icon()),
172            Self::Failed => format!("{} Failed", self.icon()),
173            Self::Start => format!("{} At Start", self.icon()),
174            Self::End => format!("{} At End", self.icon()),
175        }
176    }
177}
178
179/// Panel focus status with visual indicators
180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
181pub enum PanelStatus {
182    /// Panel is currently focused
183    Focused,
184    /// Panel is not focused
185    Unfocused,
186    /// Panel has pending updates
187    HasUpdates,
188    /// Panel is in error state
189    Error,
190}
191
192impl PanelStatus {
193    /// Get the appropriate indicator for this panel status
194    pub fn indicator(&self) -> &'static str {
195        match self {
196            Self::Focused => "●",
197            Self::Unfocused => "β—‹",
198            Self::HasUpdates => "β—‰",
199            Self::Error => "⚠",
200        }
201    }
202
203    /// Get the appropriate color for this panel status
204    pub fn color(&self) -> Color {
205        match self {
206            Self::Focused => Color::Cyan,
207            Self::Unfocused => Color::Gray,
208            Self::HasUpdates => Color::Yellow,
209            Self::Error => Color::Red,
210        }
211    }
212}
213
214/// File status with contextual icons
215#[derive(Debug, Clone, Copy, PartialEq, Eq)]
216pub enum FileStatus {
217    /// Source code is available
218    SourceAvailable,
219    /// Only opcodes available
220    OpcodesOnly,
221    /// File contains current execution
222    HasExecution,
223    /// File has been modified
224    Modified,
225    /// File is read-only
226    ReadOnly,
227    /// File not found
228    NotFound,
229}
230
231impl FileStatus {
232    /// Get the appropriate icon for this file status
233    pub fn icon(&self) -> &'static str {
234        match self {
235            Self::SourceAvailable => Icons::FILE,
236            Self::OpcodesOnly => "πŸ”§",
237            Self::HasExecution => "β–Ί",
238            Self::Modified => "πŸ“",
239            Self::ReadOnly => "πŸ”’",
240            Self::NotFound => "❓",
241        }
242    }
243
244    /// Get the appropriate color for this file status
245    pub fn color(&self) -> Color {
246        match self {
247            Self::SourceAvailable => Color::Green,
248            Self::OpcodesOnly => Color::Yellow,
249            Self::HasExecution => Color::Cyan,
250            Self::Modified => Color::Blue,
251            Self::ReadOnly => Color::Gray,
252            Self::NotFound => Color::Red,
253        }
254    }
255
256    /// Get formatted display with icon
257    pub fn display(&self, filename: &str) -> String {
258        format!("{} {}", self.icon(), filename)
259    }
260}
261
262/// Breakpoint status with visual feedback
263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
264pub enum BreakpointStatus {
265    /// Breakpoint is active
266    Active,
267    /// Breakpoint is disabled
268    Disabled,
269    /// Breakpoint hit during execution
270    Hit,
271    /// Invalid breakpoint location
272    Invalid,
273}
274
275impl BreakpointStatus {
276    /// Get the appropriate icon for this breakpoint status
277    pub fn icon(&self) -> &'static str {
278        match self {
279            Self::Active => "●",
280            Self::Disabled => "β—‹",
281            Self::Hit => "β—‰",
282            Self::Invalid => "⚠",
283        }
284    }
285
286    /// Get the appropriate color for this breakpoint status
287    pub fn color(&self) -> Color {
288        match self {
289            Self::Active => Color::Red,
290            Self::Disabled => Color::Gray,
291            Self::Hit => Color::Yellow,
292            Self::Invalid => Color::Red,
293        }
294    }
295
296    /// Get styled span for rendering
297    pub fn styled_span(&self) -> ratatui::text::Span<'static> {
298        ratatui::text::Span::styled(self.icon(), Style::default().fg(self.color()))
299    }
300}
301
302/// Comprehensive status bar builder
303pub struct StatusBar {
304    /// Connection status
305    connection: Option<ConnectionStatus>,
306    /// RPC operation status
307    rpc: Option<(RpcStatus, String)>,
308    /// Execution status
309    execution: Option<ExecutionStatus>,
310    /// Current panel
311    current_panel: Option<String>,
312    /// Additional status messages
313    messages: Vec<String>,
314}
315
316impl StatusBar {
317    /// Create a new status bar builder
318    pub fn new() -> Self {
319        Self {
320            connection: None,
321            rpc: None,
322            execution: None,
323            current_panel: None,
324            messages: Vec::new(),
325        }
326    }
327
328    /// Set connection status
329    pub fn connection(mut self, status: ConnectionStatus) -> Self {
330        self.connection = Some(status);
331        self
332    }
333
334    /// Set RPC operation status
335    pub fn rpc(mut self, status: RpcStatus, operation: String) -> Self {
336        self.rpc = Some((status, operation));
337        self
338    }
339
340    /// Set execution status
341    pub fn execution(mut self, status: ExecutionStatus) -> Self {
342        self.execution = Some(status);
343        self
344    }
345
346    /// Set current panel
347    pub fn current_panel(mut self, panel: String) -> Self {
348        self.current_panel = Some(panel);
349        self
350    }
351
352    /// Add a status message
353    pub fn message<S: Into<String>>(mut self, msg: S) -> Self {
354        self.messages.push(msg.into());
355        self
356    }
357
358    /// Build the complete status line
359    pub fn build(&self) -> String {
360        let mut parts = Vec::new();
361
362        // Connection status (always first if present)
363        if let Some(conn) = self.connection {
364            parts.push(conn.display());
365        }
366
367        // RPC operation status
368        if let Some((status, op)) = &self.rpc {
369            parts.push(status.display(op));
370        }
371
372        // Execution status
373        if let Some(exec) = self.execution {
374            parts.push(exec.display());
375        }
376
377        // Current panel
378        if let Some(panel) = &self.current_panel {
379            parts.push(format!("Panel: {panel}"));
380        }
381
382        // Additional messages
383        parts.extend(self.messages.clone());
384
385        parts.join(" | ")
386    }
387}
388
389impl Default for StatusBar {
390    fn default() -> Self {
391        Self::new()
392    }
393}