1pub mod app;
2pub mod styles;
3pub mod ui;
4pub mod data;
5
6use app::App;
7use crossterm::{
8 event::{self, DisableMouseCapture, Event, KeyCode, KeyModifiers, MouseEventKind},
9 execute,
10 terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
11};
12use log_analyzer::{
13 models::settings::Settings,
14 services::log_service::{LogAnalyzer, LogService},
15 stores::{
16 analysis_store::InMemmoryAnalysisStore, log_store::InMemmoryLogStore,
17 processing_store::InMemmoryProcessingStore,
18 },
19};
20
21use std::{
22 error::Error,
23 fs, io,
24 sync::Arc,
25 time::{Duration, Instant},
26};
27use tui::{
28 backend::{Backend, CrosstermBackend},
29 Frame, Terminal, style::Color,
30};
31use ui::{
32 ui_error_message::draw_error_popup, ui_filter_popup::draw_filter_popup,
33 ui_loading_popup::draw_loading_popup, ui_log_analyzer::draw_log_analyzer_view,
34 ui_navigation_popup::draw_navigation_popup, ui_source_popup::draw_source_popup,
35};
36
37
38pub async fn async_main(settings_path: Option<String>) -> Result<(), Box<dyn Error>> {
39 enable_raw_mode()?;
41 let mut stdout = io::stdout();
42 execute!(stdout, EnterAlternateScreen)?;
43 let backend = CrosstermBackend::new(stdout);
44 let mut terminal = Terminal::new(backend)?;
45
46 let log_store = Arc::new(InMemmoryLogStore::new());
48 let processing_store = Arc::new(InMemmoryProcessingStore::new());
49 let analysis_store = Arc::new(InMemmoryAnalysisStore::new());
50
51 let log_service = LogService::new(log_store, processing_store, analysis_store);
52 let mut color = Color::LightBlue;
53
54 if let Some(settings) = settings_path {
55 if let Ok(file) = fs::read_to_string(settings) {
56 if let Ok(settings) = Settings::from_json(&file) {
57 if let Some(formats) = settings.formats {
58 for format in formats {
59 log_service.add_format(&format.alias, &format.regex)?;
60 }
61 }
62 if let Some(filters) = settings.filters {
63 for filter in filters {
64 log_service.add_filter(filter);
65 }
66 }
67 if let Some((r, g, b)) = settings.primary_color {
68 color = Color::Rgb(r, g, b)
69 }
70 }
71 }
72 }
73
74 let tick_rate = Duration::from_millis(150);
76 let app = App::new(Box::new(log_service), color).await;
77 let res = run_app(&mut terminal, app, tick_rate).await;
78
79 disable_raw_mode()?;
81 execute!(
82 terminal.backend_mut(),
83 LeaveAlternateScreen,
84 DisableMouseCapture
85 )?;
86 terminal.show_cursor()?;
87
88 if let Err(err) = res {
89 println!("{:?}", err);
90 }
91 Ok(())
92}
93
94
95async fn run_app<B: Backend>(
96 terminal: &mut Terminal<B>,
97 mut app: App,
98 tick_rate: Duration,
99) -> io::Result<()> {
100 let mut last_tick = Instant::now();
101
102 loop {
103 terminal.draw(|f| ui(f, &mut app))?;
104
105 let timeout = tick_rate
106 .checked_sub(last_tick.elapsed())
107 .unwrap_or_else(|| Duration::from_secs(0));
108 if crossterm::event::poll(timeout)? {
109 let event = event::read()?;
110
111 match event {
112 Event::Key(key) => {
113 match key.modifiers {
114 KeyModifiers::CONTROL => match key.code {
116 KeyCode::Char('c') => return Ok(()),
117 _ => async_std::task::block_on(app.handle_input(key)),
118 },
119 KeyModifiers::SHIFT => match key.code {
121 KeyCode::Char(_) => async_std::task::block_on(app.handle_input(key)),
122 KeyCode::Up | KeyCode::BackTab => app.navigate(KeyCode::Up),
123 KeyCode::Down | KeyCode::Tab => app.navigate(KeyCode::Down),
124 KeyCode::Left => app.navigate(KeyCode::Left),
125 KeyCode::Right => app.navigate(KeyCode::Right),
126 _ => {}
127 },
128 _ => match key.code {
130 KeyCode::Tab => app.navigate(KeyCode::Down),
131 _ => app.handle_input(key).await,
132 },
133 }
134 }
135 Event::Mouse(mouse) => match mouse.kind {
136 MouseEventKind::ScrollUp => {}
137 MouseEventKind::ScrollDown => {}
138 MouseEventKind::Down(button) => match button {
139 crossterm::event::MouseButton::Left => {}
140 crossterm::event::MouseButton::Right => {}
141 _ => {}
142 },
143 _ => {}
144 },
145 _ => {}
146 }
147 }
148 if last_tick.elapsed() >= tick_rate {
149 app.on_tick().await;
150 last_tick = Instant::now();
151 }
152 }
153}
154
155fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
156 draw_log_analyzer_view(f, app);
157
158 if app.show_source_popup {
159 draw_source_popup(f, app)
160 } else if app.show_filter_popup {
161 draw_filter_popup(f, app)
162 } else if app.show_navigation_popup {
163 draw_navigation_popup(f, app)
164 }
165
166 if app.show_error_message {
167 draw_error_popup(f, app)
168 }
169
170 if app.processing.is_processing {
171 draw_loading_popup(f, app)
172 }
173}