1use crate::components::command_panel::{OptimizedInputHandler, OptimizedRenderer};
2use crate::components::ebpf_panel::{EbpfPanelHandler, EbpfPanelRenderer};
3use crate::components::loading::{LoadingState, LoadingUI};
4use crate::events::EventRegistry;
5use crate::model::ui_state::LayoutMode;
6use crate::model::{CommandPanelState, EbpfPanelState, SourcePanelState, UIState};
7use crate::ui::EmojiConfig;
8use std::fs::File;
9use std::io::{BufWriter, Write};
10use std::path::PathBuf;
11
12#[derive(Debug)]
14pub struct RealtimeSessionLogger {
15 pub enabled: bool,
16 pub file_path: Option<PathBuf>,
17 pub writer: Option<BufWriter<File>>,
18}
19
20impl Default for RealtimeSessionLogger {
21 fn default() -> Self {
22 Self::new()
23 }
24}
25
26impl RealtimeSessionLogger {
27 pub fn new() -> Self {
28 Self {
29 enabled: false,
30 file_path: None,
31 writer: None,
32 }
33 }
34
35 pub fn start(&mut self, file_path: PathBuf) -> anyhow::Result<()> {
37 let file = std::fs::OpenOptions::new()
38 .create(true)
39 .append(true)
40 .open(&file_path)?;
41
42 self.file_path = Some(file_path);
43 self.writer = Some(BufWriter::new(file));
44 self.enabled = true;
45 Ok(())
46 }
47
48 pub fn stop(&mut self) -> anyhow::Result<()> {
50 if let Some(mut writer) = self.writer.take() {
51 writer.flush()?;
52 }
53 self.enabled = false;
54 self.file_path = None;
55 Ok(())
56 }
57
58 pub fn write_line(&mut self, line: &str) -> anyhow::Result<()> {
60 if let Some(writer) = &mut self.writer {
61 writeln!(writer, "{line}")?;
62 writer.flush()?; }
64 Ok(())
65 }
66}
67
68#[derive(Debug)]
70pub struct RealtimeOutputLogger {
71 pub enabled: bool,
72 pub file_path: Option<PathBuf>,
73 pub writer: Option<BufWriter<File>>,
74}
75
76impl Default for RealtimeOutputLogger {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82impl RealtimeOutputLogger {
83 pub fn new() -> Self {
84 Self {
85 enabled: false,
86 file_path: None,
87 writer: None,
88 }
89 }
90
91 pub fn start(&mut self, file_path: PathBuf) -> anyhow::Result<()> {
93 let file = std::fs::OpenOptions::new()
94 .create(true)
95 .append(true)
96 .open(&file_path)?;
97
98 self.file_path = Some(file_path);
99 self.writer = Some(BufWriter::new(file));
100 self.enabled = true;
101 Ok(())
102 }
103
104 pub fn stop(&mut self) -> anyhow::Result<()> {
106 if let Some(mut writer) = self.writer.take() {
107 writer.flush()?;
108 }
109 self.enabled = false;
110 self.file_path = None;
111 Ok(())
112 }
113
114 pub fn write_line(&mut self, line: &str) -> anyhow::Result<()> {
116 if let Some(writer) = &mut self.writer {
117 writeln!(writer, "{line}")?;
118 writer.flush()?; }
120 Ok(())
121 }
122}
123
124#[derive(Debug)]
126pub struct AppState {
127 pub should_quit: bool,
128
129 pub loading_state: LoadingState,
131 pub loading_ui: LoadingUI,
132
133 pub source_panel: SourcePanelState,
135 pub ebpf_panel: EbpfPanelState,
136 pub command_panel: CommandPanelState,
137
138 pub command_renderer: OptimizedRenderer,
140 pub command_input_handler: OptimizedInputHandler,
141
142 pub ebpf_panel_handler: EbpfPanelHandler,
144 pub ebpf_panel_renderer: EbpfPanelRenderer,
145
146 pub ui: UIState,
148
149 pub command_panel_width: u16,
151
152 pub event_registry: EventRegistry,
154
155 pub route_file_info_to_file_search: bool,
157
158 pub target_pid: Option<u32>,
160
161 pub emoji_config: EmojiConfig,
163
164 pub expecting_second_ctrl_c: bool,
166
167 pub realtime_session_logger: RealtimeSessionLogger,
169 pub realtime_output_logger: RealtimeOutputLogger,
170}
171
172impl AppState {
173 pub fn new(event_registry: EventRegistry, layout_mode: LayoutMode) -> Self {
174 Self {
175 should_quit: false,
176 loading_state: LoadingState::Initializing, loading_ui: LoadingUI::new(),
178 source_panel: SourcePanelState::new(),
179 ebpf_panel: EbpfPanelState::new(),
180 command_panel: CommandPanelState::new(),
181 command_renderer: OptimizedRenderer::new(),
182 command_input_handler: OptimizedInputHandler::new(),
183 ebpf_panel_handler: EbpfPanelHandler::new(),
184 ebpf_panel_renderer: EbpfPanelRenderer::new(),
185 ui: UIState::new_with_layout_mode(layout_mode),
186 command_panel_width: 80, event_registry,
188 route_file_info_to_file_search: false,
189 target_pid: None,
190 emoji_config: EmojiConfig::default(),
191 expecting_second_ctrl_c: false,
192 realtime_session_logger: RealtimeSessionLogger::new(),
193 realtime_output_logger: RealtimeOutputLogger::new(),
194 }
195 }
196
197 pub fn new_with_config(
198 event_registry: EventRegistry,
199 ui_config: crate::model::ui_state::UiConfig,
200 ) -> Self {
201 Self {
202 should_quit: false,
203 loading_state: LoadingState::Initializing, loading_ui: LoadingUI::new(),
205 source_panel: SourcePanelState::new(),
206 ebpf_panel: EbpfPanelState::new_with_max_messages(ui_config.ebpf_max_messages),
207 command_panel: CommandPanelState::new_with_config(&ui_config.history),
208 command_renderer: OptimizedRenderer::new(),
209 command_input_handler: OptimizedInputHandler::new(),
210 ebpf_panel_handler: EbpfPanelHandler::new(),
211 ebpf_panel_renderer: EbpfPanelRenderer::new(),
212 ui: UIState::new(ui_config),
213 command_panel_width: 80, event_registry,
215 route_file_info_to_file_search: false,
216 target_pid: None,
217 emoji_config: EmojiConfig::default(),
218 expecting_second_ctrl_c: false,
219 realtime_session_logger: RealtimeSessionLogger::new(),
220 realtime_output_logger: RealtimeOutputLogger::new(),
221 }
222 }
223
224 pub fn is_loading(&self) -> bool {
226 !self.loading_state.is_ready() && !self.loading_state.is_failed()
227 }
228
229 pub fn set_loading_state(&mut self, state: LoadingState) {
231 tracing::debug!(
232 "Loading state change: {:?} -> {:?}",
233 self.loading_state,
234 state
235 );
236 self.loading_state = state;
237 }
238}