detritus/src/main.rs
2025-08-07 05:55:08 -04:00

71 lines
1.8 KiB
Rust

//! 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<dyn Error>> {
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<dyn Error>> {
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<dyn Error>> {
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(())
}