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}