//! Solves a fun little puzzle invented by a coworker use detritus::{T9, Trie9}; use repline::prebaked::*; use std::{ error::Error, fs::File, io::{BufRead, BufReader, IsTerminal, Write}, }; fn main() -> Result<(), Box> { let mut trie = Trie9::new(); // "Parse" CLI arg let args: Vec<_> = std::env::args().skip(1).collect(); let path = match args.as_slice() { [] => "/usr/share/dict/words", [path] => path, _ => Err("Usage: detritus [dict]")?, }; // Read in the database and construct the Trie9 for line in BufReader::new(File::open(path)?).lines() { trie.insert(&line?); } if std::io::stdin().is_terminal() { yestty(trie) } else { notty(trie) } } fn yestty(trie: Trie9) -> Result<(), Box> { repline::read_and("\x1b[36m", " t>", " ?>", |line| { let key: Vec<_> = match line.trim().chars().map(T9::try_from).collect() { Ok(t9) => t9, Err(e) => { println!("Unexpected character: {e}"); return Ok(Response::Deny); } }; key.iter().for_each(|&c| print!("{}", char::from(c))); println!(); let Some(trie) = trie.at(key.iter().copied()) else { return Ok(Response::Accept); }; for item in trie { println!("{item}") } Ok(Response::Accept) })?; Ok(()) } fn notty(trie: Trie9) -> Result<(), Box> { let stdin = std::io::stdin().lock(); let key = std::io::read_to_string(stdin)?; if let Some(trie) = trie.at(key.trim().chars()) { for word in trie.iter() { if writeln!(std::io::stdout(), "{word}").is_err() { break; } } } Ok(()) }