typetui 0.1.0

A terminal-based typing test.
Documentation
use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::io::Write;
use std::path::{Path, PathBuf};

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest_path = PathBuf::from(&out_dir).join("content.rs");
    let mut file = File::create(&dest_path).unwrap();

    writeln!(file, "// Auto-generated by build.rs - DO NOT EDIT").unwrap();
    writeln!(file).unwrap();

    let text_dir = Path::new("text");
    let mut text_languages = Vec::new();
    let mut text_words: HashMap<String, Vec<String>> = HashMap::new();

    if text_dir.exists() {
        for entry in fs::read_dir(text_dir).unwrap() {
            let entry = entry.unwrap();
            let path = entry.path();
            if path.is_file() && path.extension().map(|e| e == "txt").unwrap_or(false) {
                let lang = path.file_stem().unwrap().to_str().unwrap().to_string();
                let content = fs::read_to_string(&path).unwrap();
                let words: Vec<String> = content
                    .lines()
                    .map(|s| s.trim().to_string())
                    .filter(|s| !s.is_empty())
                    .collect();
                text_languages.push(lang.clone());
                text_words.insert(lang, words);
            }
        }
    }

    let code_dir = Path::new("code");
    let mut code_languages = Vec::new();
    let mut code_snippets: HashMap<String, Vec<String>> = HashMap::new();

    if code_dir.exists() {
        for entry in fs::read_dir(code_dir).unwrap() {
            let entry = entry.unwrap();
            let path = entry.path();
            if path.is_dir() {
                let lang = path.file_name().unwrap().to_str().unwrap().to_string();
                let mut snippets = Vec::new();

                for file_entry in fs::read_dir(&path).unwrap() {
                    let file_entry = file_entry.unwrap();
                    let file_path = file_entry.path();
                    if file_path.is_file() {
                        let content = fs::read_to_string(&file_path).unwrap();
                        snippets.push(content);
                    }
                }

                if !snippets.is_empty() {
                    code_languages.push(lang.clone());
                    code_snippets.insert(lang, snippets);
                }
            }
        }
    }

    writeln!(file, "pub const TEXT_LANGUAGES: &[&str] = &[").unwrap();
    for lang in &text_languages {
        writeln!(file, "    \"{}\",", lang).unwrap();
    }
    writeln!(file, "];").unwrap();
    writeln!(file).unwrap();

    writeln!(file, "pub const CODE_LANGUAGES: &[&str] = &[").unwrap();
    for lang in &code_languages {
        writeln!(file, "    \"{}\",", lang).unwrap();
    }
    writeln!(file, "];").unwrap();
    writeln!(file).unwrap();

    writeln!(file, "pub fn get_word_list(language: &str) -> Vec<&'static str> {{").unwrap();
    writeln!(file, "    match language {{").unwrap();
    for (lang, words) in &text_words {
        let words_str = words
            .iter()
            .map(|w| format!("\"{}\"", w.replace('"', "\\\"")))
            .collect::<Vec<_>>()
            .join(", ");
        writeln!(file, "        \"{}\" => vec![{}],", lang, words_str).unwrap();
    }
    writeln!(file, "        _ => vec![]").unwrap();
    writeln!(file, "    }}").unwrap();
    writeln!(file, "}}").unwrap();
    writeln!(file).unwrap();

    writeln!(file, "pub fn get_snippets(language: &str) -> Vec<&'static str> {{").unwrap();
    writeln!(file, "    match language {{").unwrap();
    for (lang, snippets) in &code_snippets {
        let snippets_str = snippets
            .iter()
            .map(|s| format!("r#####\"{}\"#####", s.replace("#####", "")))
            .collect::<Vec<_>>()
            .join(", ");
        writeln!(file, "        \"{}\" => vec![{}],", lang, snippets_str).unwrap();
    }
    writeln!(file, "        _ => vec![]").unwrap();
    writeln!(file, "    }}").unwrap();
    writeln!(file, "}}").unwrap();

    println!("cargo:rerun-if-changed=text/");
    println!("cargo:rerun-if-changed=code/");
}