1#![warn(missing_docs)]
26#![warn(clippy::all)]
27#![warn(clippy::pedantic)]
28#![allow(clippy::cast_possible_truncation)]
30#![allow(clippy::cast_sign_loss)]
31#![allow(clippy::cast_precision_loss)]
32#![allow(clippy::missing_errors_doc)]
33#![allow(clippy::missing_panics_doc)]
34#![allow(clippy::must_use_candidate)]
35#![allow(clippy::module_name_repetitions)]
36#![allow(clippy::similar_names)]
37#![allow(clippy::too_many_lines)]
38#![allow(clippy::doc_markdown)]
39#![allow(clippy::uninlined_format_args)]
40#![allow(clippy::needless_pass_by_value)]
41#![allow(clippy::unused_self)]
42#![allow(clippy::match_same_arms)]
43#![allow(clippy::single_match_else)]
44#![allow(clippy::items_after_statements)]
45#![allow(clippy::manual_div_ceil)]
46#![allow(clippy::struct_field_names)]
47#![allow(clippy::option_map_or_none)]
48#![allow(clippy::map_unwrap_or)]
49#![allow(clippy::unnecessary_wraps)]
50#![allow(clippy::trivially_copy_pass_by_ref)]
51#![allow(clippy::redundant_else)]
52#![allow(clippy::needless_raw_string_hashes)]
53#![allow(clippy::needless_bool)]
54#![allow(clippy::bool_comparison)]
55#![allow(clippy::should_implement_trait)]
56#![allow(clippy::nonminimal_bool)]
57#![allow(clippy::return_self_not_must_use)]
58
59pub mod app;
60pub mod event;
61pub mod theme;
62pub mod ui;
63pub mod views;
64
65pub use app::{App, Tab};
66pub use theme::AxonmlTheme;
67pub use views::{DataView, FilesView, GraphsView, HelpView, ModelView, TrainingView};
68
69use std::io::{self, stdout};
70use std::path::PathBuf;
71use std::time::Duration;
72
73use crossterm::{
74 event::Event,
75 execute,
76 terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
77};
78use ratatui::prelude::*;
79
80pub fn run(model_path: Option<PathBuf>, data_path: Option<PathBuf>) -> io::Result<()> {
93 enable_raw_mode()?;
95 let mut stdout = stdout();
96 execute!(stdout, EnterAlternateScreen)?;
97 let backend = CrosstermBackend::new(stdout);
98 let mut terminal = Terminal::new(backend)?;
99
100 let mut app = App::new();
102
103 if let Some(path) = model_path {
105 app.load_model(path);
106 }
107
108 if let Some(path) = data_path {
110 app.load_dataset(path);
111 }
112
113 let result = run_app(&mut terminal, &mut app);
115
116 disable_raw_mode()?;
118 execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
119 terminal.show_cursor()?;
120
121 result
122}
123
124fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<()> {
126 loop {
127 terminal.draw(|frame| {
129 ui::render(app, frame);
130 })?;
131
132 if event::poll_event(Duration::from_millis(100))? {
134 if let Event::Key(key) = event::read_event()? {
135 event::handle_key_event(app, key);
136 }
137 }
138
139 if app.should_quit {
141 return Ok(());
142 }
143
144 if app.active_tab == Tab::Training {
146 app.training_view.tick();
147 }
148 }
149}
150
151#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn test_app_creation() {
161 let app = App::new();
162 assert_eq!(app.active_tab, Tab::Model);
163 assert!(!app.should_quit);
164 }
165}