stdin_nonblocking/
lib.rs

1use std::io::{self, BufRead};
2use std::sync::mpsc::{self, Receiver, Sender};
3use std::thread;
4use std::time::Duration;
5
6/// Spawns a background thread that continuously reads from stdin as a stream.
7///
8/// This function returns an `mpsc Receiver`, allowing non-blocking polling
9/// of stdin input just like `spawn_stdin_channel`.
10///
11/// # Returns
12/// A `Receiver<String>` that emits lines from stdin.
13///
14/// # Example
15/// ```
16/// use stdin_nonblocking::spawn_stdin_stream;
17/// use std::sync::mpsc::TryRecvError;
18/// use std::time::Duration;
19///
20/// fn main() {
21///     let stdin_stream = spawn_stdin_stream();
22///
23///     loop {
24///         match stdin_stream.try_recv() {
25///             Ok(line) => println!("Received: {}", line),
26///             Err(TryRecvError::Empty) => {
27///                 // No input yet; continue execution
28///             }
29///             Err(TryRecvError::Disconnected) => {
30///                 println!("Input stream closed. Exiting...");
31///                 break;
32///             }
33///         }
34///         std::thread::sleep(Duration::from_millis(500));
35///     }
36/// }
37/// ```
38pub fn spawn_stdin_stream() -> Receiver<String> {
39    let (tx, rx): (Sender<String>, Receiver<String>) = mpsc::channel();
40
41    thread::spawn(move || {
42        let stdin = io::stdin();
43        let mut stdin_lock = stdin.lock();
44
45        loop {
46            let mut buffer = String::new();
47            match stdin_lock.read_line(&mut buffer) {
48                Ok(0) => break, // EOF detected, exit thread
49                Ok(_) => {
50                    if tx.send(buffer.trim().to_string()).is_err() {
51                        break; // Exit if receiver is dropped
52                    }
53                }
54                Err(_) => break, // Read failure
55            }
56        }
57    });
58
59    rx
60}
61
62/// Reads from stdin if available, otherwise returns a default value.
63///
64/// **Non-blocking:** This function polls `stdin` once and immediately returns.
65///
66/// # Arguments
67/// * `default` - A fallback value returned if no input is available.
68///
69/// # Returns
70/// * `String` - Trimmed stdin input or `default` if no input is received.
71///
72/// # Example
73/// ```
74/// use stdin_nonblocking::get_stdin_or_default;
75///
76/// fn main() {
77///     let input = get_stdin_or_default("fallback_value");
78///     println!("Final input: {}", input);
79/// }
80/// ```
81pub fn get_stdin_or_default(default: &str) -> String {
82    let stdin_channel = spawn_stdin_stream();
83
84    // Give the reader thread a short time to capture any available input
85    thread::sleep(Duration::from_millis(50));
86
87    match stdin_channel.try_recv() {
88        Ok(input) if !input.trim().is_empty() => input,
89        _ => default.to_string(),
90    }
91}