input_py/lib.rs
1use std::io::{self, Write};
2
3/// Errors that can occur during input operations
4#[derive(Debug)]
5pub enum InputError {
6 /// Failed to flush stdout
7 FlushError(io::Error),
8 /// Failed to read from stdin
9 ReadError(io::Error),
10}
11
12impl std::fmt::Display for InputError {
13 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14 match self {
15 InputError::FlushError(e) => write!(f, "Failed to flush stdout: {e}"),
16 InputError::ReadError(e) => write!(f, "Failed to read from stdin: {e}"),
17 }
18 }
19}
20
21impl std::error::Error for InputError {}
22
23/// Reads a line of input from stdin with a prompt, similar to Python's input() function.
24///
25/// # Arguments
26/// * `comment` - The prompt text to display before the colon. If empty, no prompt is shown.
27///
28/// # Returns
29/// * `Ok(String)` - The input string with leading/trailing whitespace removed
30/// * `Err(InputError)` - An error if stdout flush or stdin read fails
31///
32/// # Examples
33/// ```
34/// use input_py::input;
35///
36/// // Basic usage with prompt
37/// match input("Enter your name") {
38/// Ok(name) => println!("Hello, {}!", name),
39/// Err(e) => eprintln!("Error: {}", e),
40/// }
41///
42/// // Empty prompt
43/// match input("") {
44/// Ok(data) => println!("You entered: {}", data),
45/// Err(e) => eprintln!("Error: {}", e),
46/// }
47/// ```
48pub fn input(comment: &str) -> Result<String, InputError> {
49 let mut buf = String::new();
50
51 if !comment.is_empty() {
52 print!("{comment}:");
53 io::stdout().flush().map_err(InputError::FlushError)?;
54 }
55
56 io::stdin().read_line(&mut buf).map_err(InputError::ReadError)?;
57 Ok(buf.trim().to_string())
58}
59
60/// Reads a line of input with a default value if nothing is entered.
61///
62/// # Arguments
63/// * `comment` - The prompt text to display
64/// * `default` - The default value to return if the user enters nothing
65///
66/// # Returns
67/// * `Ok(String)` - The input string or default value
68/// * `Err(InputError)` - An error if stdout flush or stdin read fails
69///
70/// # Examples
71/// ```
72/// use input_py::input_with_default;
73///
74/// match input_with_default("Enter port", "8080") {
75/// Ok(port) => println!("Using port: {}", port),
76/// Err(e) => eprintln!("Error: {}", e),
77/// }
78/// ```
79pub fn input_with_default(comment: &str, default: &str) -> Result<String, InputError> {
80 let mut buf = String::new();
81
82 if default.is_empty() {
83 print!("{comment}:");
84 } else {
85 print!("{comment} [{default}]:");
86 }
87 io::stdout().flush().map_err(InputError::FlushError)?;
88
89 io::stdin().read_line(&mut buf).map_err(InputError::ReadError)?;
90 let trimmed = buf.trim();
91
92 if trimmed.is_empty() {
93 Ok(default.to_string())
94 } else {
95 Ok(trimmed.to_string())
96 }
97}
98
99/// Reads a line of input with configurable trimming behavior.
100///
101/// # Arguments
102/// * `comment` - The prompt text to display
103/// * `trim_whitespace` - Whether to trim leading/trailing whitespace
104///
105/// # Returns
106/// * `Ok(String)` - The input string (trimmed or not based on setting)
107/// * `Err(InputError)` - An error if stdout flush or stdin read fails
108///
109/// # Examples
110/// ```
111/// use input_py::input_trim;
112///
113/// // Preserve whitespace
114/// match input_trim("Enter text", false) {
115/// Ok(text) => println!("Raw input: '{}'", text),
116/// Err(e) => eprintln!("Error: {}", e),
117/// }
118///
119/// // Trim whitespace (default behavior)
120/// match input_trim("Enter text", true) {
121/// Ok(text) => println!("Trimmed input: '{}'", text),
122/// Err(e) => eprintln!("Error: {}", e),
123/// }
124/// ```
125pub fn input_trim(comment: &str, trim_whitespace: bool) -> Result<String, InputError> {
126 let mut buf = String::new();
127
128 if !comment.is_empty() {
129 print!("{comment}:");
130 io::stdout().flush().map_err(InputError::FlushError)?;
131 }
132
133 io::stdin().read_line(&mut buf).map_err(InputError::ReadError)?;
134
135 if trim_whitespace {
136 Ok(buf.trim().to_string())
137 } else {
138 // Remove only the trailing newline
139 if buf.ends_with('\n') {
140 buf.pop();
141 if buf.ends_with('\r') {
142 buf.pop();
143 }
144 }
145 Ok(buf)
146 }
147}