pub mod listener {
use std::{
any::Any,
io,
sync::{Arc, Mutex},
thread,
};
pub struct Listener {
heard_key: Arc<Mutex<bool>>,
thread: thread::JoinHandle<bool>,
}
impl Listener {
pub fn new(key: &str) -> Listener {
assert!(!key.is_empty());
Self::new_inner(key, false)
}
pub fn new_case_insensitive(key: &str) -> Listener {
assert!(!key.is_empty());
Self::new_inner(key, true)
}
fn new_inner(key: &str, case_insensitive: bool) -> Listener {
let heard_key = Arc::new(Mutex::new(false));
let thread;
{
let key = String::from(key);
let heard_key = Arc::clone(&heard_key);
thread = thread::spawn(move || loop {
let mut input = String::with_capacity(key.len());
io::stdin()
.read_line(&mut input)
.expect("Error while reading input from console.");
let input = input.trim_end();
if case_insensitive && input.eq_ignore_ascii_case(&key) || input == key {
let mut b = heard_key.lock().expect("Failed to lock heard_key.");
*b = true;
break true;
}
});
}
Listener { heard_key, thread }
}
pub fn heard_key(&self) -> bool {
match self.heard_key.lock() {
Ok(b) => *b,
Err(e) => *e.into_inner(),
}
}
pub fn wait_for_key(self) -> Result<bool, Box<dyn Any + Send>> {
self.thread.join()
}
}
}
#[cfg(test)]
mod tests {
use super::{listener::*};
use std::{thread, time::Duration};
#[test]
fn new_works() {
let _listener = Listener::new("test");
}
#[test]
#[should_panic]
fn new_panics_on_empty_key() {
let _listener = Listener::new("");
}
#[test]
fn new_case_insensitive_works() {
let _listener = Listener::new_case_insensitive("test");
}
#[test]
#[should_panic]
fn new_case_insensitive_panics_on_empty_key() {
let _listener = Listener::new_case_insensitive("");
}
#[test]
fn heard_key_returns_false() {
let listener = Listener::new("test");
thread::sleep(Duration::from_secs(5));
assert!(!listener.heard_key());
}
}