mod commands;
mod generator;
mod input;
mod metrics;
mod model;
mod msg;
mod persistence;
mod stats;
mod update;
mod view;
#[cfg(test)]
mod integration_tests;
use std::time::{Duration, Instant};
use anyhow::Result;
use commands::{Command, execute_command};
use model::{Model, TestStatus};
use msg::Msg;
use rand::rngs::SmallRng;
use update::update;
use view::view;
fn main() -> Result<()> {
let mut terminal = ratatui::init();
let result = run(&mut terminal);
ratatui::restore();
result
}
fn run(terminal: &mut ratatui::DefaultTerminal) -> Result<()> {
let mut rng: SmallRng = rand::make_rng();
let mut model = Model::default();
match persistence::load() {
Ok(history) => model.history = history,
Err(e) => eprintln!("kern: failed to load stats: {e}"),
}
let mut timer_start: Option<Instant> = None;
let word_count = model.config.word_count;
execute_command(
&mut model,
Command::GenerateWords { count: word_count },
&mut rng,
);
loop {
terminal.draw(|frame| view(&model, frame))?;
if crossterm::event::poll(Duration::from_millis(16))?
&& let Some(msg) = input::event_to_msg(crossterm::event::read()?)
{
let cmd = update(&mut model, msg);
execute_command(&mut model, cmd, &mut rng);
}
if timer_start.is_none() && model.session.status == TestStatus::Running {
timer_start = Some(Instant::now());
}
if timer_start.is_some() && model.session.status == TestStatus::Waiting {
timer_start = None;
}
if timer_start.is_some() && model.session.status == TestStatus::Done {
timer_start = None;
}
let elapsed = timer_start.map(|t| t.elapsed()).unwrap_or(Duration::ZERO);
let cmd = update(&mut model, Msg::Tick(elapsed));
execute_command(&mut model, cmd, &mut rng);
if model.screen == model::Screen::Quitting {
break;
}
}
Ok(())
}