console_listener/
lib.rs

1//! `console_listener` includes the `Listener` struct, used to listen for a specified string to be typed to the console.
2
3pub mod listener {
4    use std::{
5        any::Any,
6        io,
7        sync::{Arc, Mutex},
8        thread,
9    };
10
11    /// The Listener struct holds a thread that listens for a specified string written to the console.
12    /// 
13    /// The Listener struct implements functions to check whether the string was typed, `heard_key`, and to wait for the string to get typed, `wait_for_key`.
14    /// Create a new instance of Listener using `new` or `new_case_insensitive` and specify the string to listen for.
15    pub struct Listener {
16        heard_key: Arc<Mutex<bool>>,
17        thread: thread::JoinHandle<bool>,
18    }
19
20    impl Listener {
21        /// Creates a new Listener instance.
22        ///
23        /// The key is the string that the Listener listens for.
24        ///
25        /// # Panics
26        ///
27        /// The `new` function panics if the key is an empty string
28        /// 
29        /// # Examples
30        /// 
31        /// ```
32        /// let listener = Listener::new("q");
33        /// ```
34        pub fn new(key: &str) -> Listener {
35            assert!(!key.is_empty());
36
37            Self::new_inner(key, false)
38        }
39
40        /// Creates a new case insensitive Listener instance.
41        ///
42        /// The key is the string that the Listener listens for.
43        ///
44        /// # Panics
45        ///
46        /// The `new_case_insensitive` function panics if the key is an empty string
47        /// 
48        /// # Examples
49        /// 
50        /// ```
51        /// let listener = Listener::new_case_insensitive("q");
52        /// ```
53        pub fn new_case_insensitive(key: &str) -> Listener {
54            assert!(!key.is_empty());
55
56            Self::new_inner(key, true)
57        }
58
59        fn new_inner(key: &str, case_insensitive: bool) -> Listener {
60            let heard_key = Arc::new(Mutex::new(false));
61            let thread;
62            {
63                let key = String::from(key);
64                let heard_key = Arc::clone(&heard_key);
65                thread = thread::spawn(move || loop {
66                    let mut input = String::with_capacity(key.len());
67                    io::stdin()
68                        .read_line(&mut input)
69                        .expect("Error while reading input from console.");
70                    
71                    let input = input.trim_end();
72
73                    if case_insensitive && input.eq_ignore_ascii_case(&key) || input == key {
74                        let mut b = heard_key.lock().expect("Failed to lock heard_key.");
75                        *b = true;
76
77                        break true;
78                    }
79                });
80            }
81
82            Listener { heard_key, thread }
83        }
84
85        /// Returns true if the Listener has heard the key.
86        /// 
87        /// # Examples
88        /// 
89        /// ```
90        /// let listener = Listener::new("q");
91        /// 
92        /// while !listener.heard_key() {
93        ///     println!("Waiting for key.");
94        /// }
95        /// ```
96        pub fn heard_key(&self) -> bool {
97            match self.heard_key.lock() {
98                Ok(b) => *b,
99                Err(e) => *e.into_inner(),
100            }
101        }
102
103        /// Blocks the current thread until the listening thread ends. This consumes the Listener.
104        /// 
105        /// Returns `Ok` containing a boolean indicating whether the key was heard.
106        /// 
107        /// # Errors
108        /// 
109        /// Returns `Err` if the listening thread panicked.
110        /// 
111        /// # Examples
112        /// 
113        /// ```
114        /// let listener = Listener::new("q");
115        /// 
116        /// heard_key = listener.wait_for_key().unwrap();
117        /// 
118        /// assert!(heard_key);
119        /// ```
120        pub fn wait_for_key(self) -> Result<bool, Box<dyn Any + Send>> {
121            self.thread.join()
122        }
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::{listener::*};
129    use std::{thread, time::Duration};
130
131    #[test]
132    fn new_works() {
133        let _listener = Listener::new("test");
134    }
135
136    #[test]
137    #[should_panic]
138    fn new_panics_on_empty_key() {
139        let _listener = Listener::new("");
140    }
141
142    #[test]
143    fn new_case_insensitive_works() {
144        let _listener = Listener::new_case_insensitive("test");
145    }
146
147    #[test]
148    #[should_panic]
149    fn new_case_insensitive_panics_on_empty_key() {
150        let _listener = Listener::new_case_insensitive("");
151    }
152
153    #[test]
154    fn heard_key_returns_false() {
155        let listener = Listener::new("test");
156
157        thread::sleep(Duration::from_secs(5));
158
159        assert!(!listener.heard_key());
160    }
161}