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}