read_stdin/
lib.rs

1mod tests;
2
3use std::any::TypeId;
4use std::fmt::Display;
5use std::io::stdin;
6use std::io::stdout;
7use std::io::BufRead;
8use std::io::Write;
9use std::str::FromStr;
10
11/// Read user input from `stdin` and try to parse it into a generic type.
12///
13/// # Example
14/// ```rust
15/// use read_stdin;
16///
17/// let Ok(n) = read_stdin::once::<i32>() else {
18///     println!("You entered an incorrect data type!");
19///     return;
20/// };
21///
22/// println!("You entered: {}", n);
23/// ```
24pub fn once<T>() -> Result<T, <T as FromStr>::Err>
25where
26    T: FromStr + 'static,
27{
28    let mut stdin = stdin().lock();
29    let mut buf: String = String::new();
30
31    while stdin.read_line(&mut buf).is_err() {
32        println!("Failed to read line. Please try again.");
33    }
34
35    if TypeId::of::<T>() != TypeId::of::<String>() {
36        buf = buf.trim().to_string();
37    }
38
39    buf.parse::<T>()
40}
41
42/// Read user input from `stdin` and try to parse it into a generic type. I will keep prompting
43/// the user for input until they enter data that properly parses.
44///
45/// # Example
46/// ```rust
47/// use read_stdin;
48///
49/// let n: i32 = read_stdin::until_ok();
50///
51/// println!("You entered: {}", n);
52/// ```
53pub fn until_ok<T>() -> T
54where
55    T: FromStr + 'static,
56{
57    loop {
58        match once::<T>() {
59            Ok(value) => return value,
60            Err(_) => {
61                println!("Failed to parse. Please input a correct data type.");
62                continue;
63            }
64        }
65    }
66}
67
68/// Creates a CLI prompt and asks the user for input.
69///
70/// # Example
71/// ```rust
72/// use read_stdin;
73///
74/// let Ok(n) = read_stdin::prompt::<i32>("Enter a number: ") else {
75///     println!("Failed to parse");
76///     return;
77/// };
78///
79/// println!("You entered: {}", n);
80/// ```
81pub fn prompt<T>(prompt: &(impl ToString + Display + ?Sized)) -> Result<T, <T as FromStr>::Err>
82where
83    T: FromStr + 'static,
84{
85    let mut stdout = stdout().lock();
86
87    print!("{}", prompt);
88    stdout.flush().expect("Failed to flush stdout");
89    once()
90}
91
92/// Creates a CLI prompt and repeatetly asks the user for input until their input is of valid type.
93///
94/// # Example
95/// ```rust
96/// use read_stdin;
97///
98/// let n: i32 = read_stdin::prompt_until_ok("Enter a number: ");
99///
100/// println!("You entered: {}", n);
101/// ```
102pub fn prompt_until_ok<T>(prompt: &(impl ToString + Display + ?Sized)) -> T
103where
104    T: FromStr + 'static,
105{
106    let mut stdout = stdout().lock();
107
108    loop {
109        print!("{}", prompt);
110        stdout.flush().expect("Failed to flush stdout");
111        match once::<T>() {
112            Ok(value) => return value,
113            Err(_) => {
114                println!("Failed to parse. Please input a correct data type.");
115                continue;
116            }
117        }
118    };
119}
120
121// Compatibility
122#[deprecated(since = "1.1.0", note = "Use `until_ok` instead.")]
123pub fn read_stdin_until_ok<T>() -> T
124where
125    T: FromStr + 'static,
126{
127    until_ok()
128}
129
130#[deprecated(since = "1.1.0", note = "Use `once` instead.")]
131pub fn read_stdin<T>() -> Result<T, <T as FromStr>::Err>
132where
133    T: FromStr + 'static,
134{
135    once()
136}