#![forbid(unsafe_code)]
mod resources;
const CATLOCK_OPTSTRING : &str = "hvp?";
fn print_help() {
println!("catlock [ -v | -h | -? | -p ]");
println!("Protects keyboard against side effects caused by your cat interactions.");
println!("Password for unlocking screen is \"{}\". Keep it secret from your cat.",
resources::unlock_pw.iter().map(|&c| c as char).collect::<String>()
);
println!("You do not need to type spaces or specifically lowercase letters in the password.");
println!(" -h|? print this help message");
println!(" -v print version number and exit");
println!(" -p print a cat");
println!("\nCuddle and play with your cat daily! Cats need your attention.");
println!("Make sure your cat always has access to fresh water.");
}
fn main() {
use getopt3::hideBin;
let g = getopt3::new(hideBin(std::env::args()), CATLOCK_OPTSTRING).unwrap();
if g.has('v') {
const VERSION: &str = env!("CARGO_PKG_VERSION");
println!("{}", VERSION);
return
} else if g.has('h') || g.has('?') {
print_help();
return
} else if g.has('p') {
let (cat, desc) = random_cat();
println!("{}\n{}", cat, desc);
return
}
use crossterm::tty::IsTty;
if std::io::stdin().is_tty() {
println!("Your keyboard is now CAT PROOF.");
println!("{}", lockscreen_cat());
println!("To unlock type: NOT A CAT\n");
let rc = do_unlock();
match rc {
Ok(_) => println!("You are certainly not a cat!"),
Err(e) => eprintln!("Error reading password: {}", e)
}
} else {
eprintln!("This program requires a terminal.");
}
}
fn do_unlock() -> std::io::Result<()> {
crossterm::terminal::enable_raw_mode()?;
let mut checkpos : usize = 0;
loop {
let maybekey = get_next_key();
match maybekey {
Err(r) => {
crossterm::terminal::disable_raw_mode()?;
return Err(r)
}
_ => { }
}
let key = maybekey.unwrap();
if ! is_ignored(key) {
if resources::unlock_pw[checkpos] == key.to_ascii_lowercase() {
#[cfg(debug_assertions)]
println!("tick");
checkpos += 1;
if checkpos >= resources::unlock_pw.len() {
break;
}
} else {
#[cfg(debug_assertions)]
println!("snap");
checkpos = 0;
}
} else {
#[cfg(debug_assertions)]
println!("dup");
}
}
crossterm::terminal::disable_raw_mode()?;
Ok(())
}
fn get_next_key() -> std::io::Result<u8> {
use std::io::Read;
use std::io::ErrorKind;
let mut buf: [u8;1] = [0];
let mut handle = std::io::stdin().lock();
let keys = handle.read(&mut buf);
match keys {
Ok(1) => {
#[cfg(debug_assertions)]
println!("You pressed: {:?}\n", buf[0]);
Ok(buf[0])
}
Ok(0) => {
Err(std::io::Error::new(ErrorKind::UnexpectedEof, "EOF reached"))
}
Err(e) => Err(e),
_ => unreachable!()
}
}
fn is_ignored(keystroke: u8) -> bool {
resources::ignored_keys.contains(&keystroke)
}
fn lockscreen_cat() -> &'static str {
let random_cat : usize = stdrandom::gen_range(0..resources::cats::lockscreen_cats.len() as u64, stdrandom::fast_u64);
resources::cats::lockscreen_cats[random_cat]
}
fn random_cat() -> (&'static str, &'static str) {
let random_cat : usize = stdrandom::gen_range(0..resources::cats::all_cats.len() as u64, stdrandom::fast_u64);
(
resources::cats::all_cats[random_cat],
resources::cats::all_cats_desc[random_cat]
)
}