ferristype 1.0.2

FerrisType is a minimalist typing game where your sole task is to type whatever appears on your terminal screen. Test and improve your typing speed and accuracy as random words and phrases appear, challenging you to type them quickly and correctly. Embrace the simplicity, hone your typing skills, and reach new levels of typing mastery in this immersive terminal-based game.
Documentation
use crate::stats::Timer;
use crate::terminalui::Input;
use std::{fmt, time::Duration};
use termion::{clear, cursor, event::Key};

pub struct Statistics {
    wpm: f64,
    raw_wpm: f64,
    accuracy: f64,
    time: Duration,
    correct_lines: usize,
    incorrect_lines: usize,
}

impl Statistics {
    pub fn calculate(timer: Timer, lines: &Vec<String>) -> Option<Statistics> {
        let mut correct_lines = 0;
        let mut incorrect_lines = 0;
        let mut correct_chars = 0;
        let mut incorrect_chars = 0;
        let mut inputs = lines.into_iter().map(|x| Input::new(x)).peekable();
        let mut current_input = inputs.next().unwrap();
        let mut final_time = Duration::ZERO;
        for (t, k) in timer.key_timer {
            final_time = t;
            match k {
                Key::Char('\n') => {
                    if current_input.is_complete() && current_input.correct {
                        correct_lines += 1;
                    } else {
                        incorrect_lines += 1;
                    }
                    match inputs.next() {
                        Some(n) => current_input = n,
                        None => break,
                    }
                }
                Key::Char(c) => {
                    let correct = current_input.update(c);
                    if correct {
                        correct_chars += 1;
                    } else {
                        incorrect_chars += 1;
                    }
                    if current_input.is_complete()
                        && current_input.correct
                        && inputs.peek().is_none()
                    {
                        correct_lines += 1
                    }
                }
                Key::Backspace => {
                    current_input.delete_one();
                }
                Key::Ctrl('c') => break,
                _ => unreachable!("Unexpected key in history"),
            }
        }
        let wpm = correct_chars as f64 / final_time.as_millis() as f64 * 12000.0;
        let raw_wpm =
            (correct_chars + incorrect_chars) as f64 / final_time.as_millis() as f64 * 12000.0;
        let accuracy = correct_chars as f64 / (correct_chars + incorrect_chars) as f64;
        Some(Statistics {
            wpm,
            raw_wpm,
            accuracy,
            time: final_time,
            correct_lines,
            incorrect_lines,
        })
    }
}

impl fmt::Display for Statistics {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", cursor::Goto(1, 1), clear::CurrentLine)?;
        write!(
            f,
            "T: {}s, lines without failure: {}, lines without correcting: {}",
            self.time.as_millis() as f64 / 1000.0,
            self.correct_lines,
            self.incorrect_lines,
        )?;
        write!(f, "{}{}", cursor::Goto(1, 2), clear::CurrentLine)?;
        write!(
            f,
            "WPM: {:.2}, Raw: {:.2}, Accuracy {:.3}",
            self.wpm,
            self.raw_wpm,
            self.accuracy * 100.0
        )
    }
}