Skip to main content

feather_tui/
input.rs

1use crate::{error::FtuiResult, renderer as ren};
2use std::io::{self, Write};
3use crossterm as ct;
4
5/// Reads a line of input from the user after displaying a prompt.
6///
7/// # Parameters
8/// - `prompt`: A `&str` containing the message to display before user input.
9///
10/// # Returns
11/// - `Ok(String)`: The user’s input as a `String`, including the newline character.
12/// - `Err(FtuiError)`: Returns an error.
13///
14/// # Notes
15/// - The returned `String` includes the newline (`\n`). Use `.trim()` if necessary.
16///
17/// # Example
18/// ```rust
19/// // Get the user input and print it out if error occure print the error
20/// match line("Input Something") {
21///     Ok(e) => println!("User Input {}", e),
22///     Err(e) => eprintln!("Error: {}", e),
23/// };
24/// ```
25pub fn line(promt: &str) -> FtuiResult<String> {
26    ren::unready()?;
27
28    print!("{} -> ", promt);
29    
30    io::stdout().flush()?;
31
32    let mut line = String::new();
33    io::stdin().read_line(&mut line)?;
34
35    ren::ready()?;
36    Ok(line)
37}
38
39/// Reads a key press event as `KeyCode` from the terminal without blocking.
40///
41/// # Returns
42/// - `Ok(Some(KeyCode))`: If a key event is detected.
43/// - `Ok(None)`: If no key event is detected.
44/// - `Err(FtuiError)`: Returns an error. 
45///
46/// # Notes
47/// - This function does not block waiting for input.
48///
49/// # Example
50/// ```rust
51/// fn main() -> FtuiResult<()> {
52///     // Get the user key input as `KeyCode` and print it out
53///     match key()? {
54///         Some(key) => println!("Key pressed: {:?}", key),
55///         None => println!("No key press detected"),
56///     };
57///
58///     Ok(())
59/// }
60/// ```
61pub fn key() -> FtuiResult<Option<ct::event::KeyCode>> {
62    let mut key_code: Option<ct::event::KeyCode> = None;
63    
64    ct::terminal::enable_raw_mode()?;
65
66    if ct::event::poll(std::time::Duration::from_millis(0))? {
67        match ct::event::read()? {
68            ct::event::Event::Key(event) => {
69                key_code = Some(event.code);
70            }
71            _ => {}
72        }
73    }
74
75    ct::terminal::disable_raw_mode()?;
76    Ok(key_code)
77} 
78
79/// Converts a `KeyCode` into its corresponding character, if applicable.
80///
81/// # Parameters
82/// - `code`: The `KeyCode` to convert.
83///
84/// # Returns
85/// - `Some(char)`: If the `KeyCode` represents a printable character.
86/// - `None`: If the `KeyCode` is not a character (e.g., arrow keys, function keys).
87///
88/// # Example
89/// ```rust
90/// fn main() -> FtuiResult<()> {
91///     // Capture user keyboard input as a KeyCode.
92///     // If reading fails, terminate with an error.
93///     let key_code = key()?;
94///
95///     // If a key was pressed, attempt to convert it to a character.
96///     match key_code {
97///         Some(code) => match keycode_to_char(code) {
98///             // Print the character if it's a printable key.
99///             Some(c) => println!("Key pressed: {}", c), 
100///             None => println!("Unprintable KeyCode"), 
101///         },
102///         // No key was pressed, exit the function.
103///         None => return, 
104///     }
105///
106///     Ok(())
107/// }
108/// ```
109pub fn keycode_to_char(code: ct::event::KeyCode) -> Option<char> {
110    match code {
111        ct::event::KeyCode::Char(c) => Some(c),
112        _ => None,
113    }
114}
115
116/// Reads a key press event as a `char` from the terminal without blocking.
117///
118/// # Returns
119/// - `Ok(Some(char))`: if a printable key was pressed.
120/// - `Ok(None)`: if a non-printable key was pressed or no input was detected.
121/// - `Err(FtuiError)`: Returns an error.
122///
123/// # Example
124/// ```rust
125/// fn main() -> FtuiResult<()> {
126///     // Capture user keyboard input as a character and print it out if
127///     // possible. 
128///     match key_char()? {
129///         Some(c) => println!("Key pressed: {}", c),
130///         None => println!("No key pressed or no printable key pressed"),
131///     }
132/// }
133/// ```
134pub fn key_char() -> FtuiResult<Option<char>> {
135    match key()? {
136        Some(code) => Ok(keycode_to_char(code)),
137        None => Ok(None),
138    }
139}