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}