1#![allow(missing_docs)]
2use super::error::{PromptError, Result};
5
6pub fn is_interactive() -> bool {
7 use std::io::IsTerminal;
8 std::io::stderr().is_terminal() && std::io::stdin().is_terminal()
9}
10
11#[derive(Debug, Clone, PartialEq)]
14pub enum Key {
15 Up,
16 Down,
17 Left,
18 Right,
19 Home,
20 End,
21 PageUp,
22 PageDown,
23 Enter,
24 Tab,
25 Char(char),
27 Backspace,
28 Delete,
29 Escape,
30 Interrupt,
32 DeleteWordBack,
34}
35
36pub mod interactive {
39 use super::{Key, PromptError, Result};
40 use crossterm::event::{self, Event, KeyCode, KeyModifiers};
41
42 pub fn read_key() -> Result<Key> {
43 loop {
44 match event::read().map_err(PromptError::Io)? {
45 Event::Key(ke) => {
46 if ke.modifiers.contains(KeyModifiers::CONTROL) {
47 match ke.code {
48 KeyCode::Char('c') | KeyCode::Char('d') => {
49 return Ok(Key::Interrupt);
50 }
51 KeyCode::Char('w') => return Ok(Key::DeleteWordBack),
52 KeyCode::Char('a') => return Ok(Key::Home),
53 KeyCode::Char('e') => return Ok(Key::End),
54 _ => continue,
55 }
56 }
57 return Ok(match ke.code {
58 KeyCode::Up => Key::Up,
59 KeyCode::Down => Key::Down,
60 KeyCode::Left => Key::Left,
61 KeyCode::Right => Key::Right,
62 KeyCode::Home => Key::Home,
63 KeyCode::End => Key::End,
64 KeyCode::PageUp => Key::PageUp,
65 KeyCode::PageDown => Key::PageDown,
66 KeyCode::Enter => Key::Enter,
67 KeyCode::Tab => Key::Tab,
68 KeyCode::Char(c) => Key::Char(c),
69 KeyCode::Backspace => Key::Backspace,
70 KeyCode::Delete => Key::Delete,
71 KeyCode::Esc => Key::Escape,
72 _ => continue,
73 });
74 }
75 _ => continue,
76 }
77 }
78 }
79
80 pub fn enter_raw() -> Result<()> {
81 crossterm::terminal::enable_raw_mode().map_err(PromptError::Io)
82 }
83
84 pub fn leave_raw() -> Result<()> {
85 crossterm::terminal::disable_raw_mode().map_err(PromptError::Io)
86 }
87}
88
89pub mod fallback {
92 use super::{PromptError, Result};
93 use std::io::BufRead;
94
95 pub fn read_line_raw() -> Result<String> {
96 let mut line = String::new();
97 std::io::stdin()
98 .lock()
99 .read_line(&mut line)
100 .map_err(PromptError::Io)?;
101 if line.is_empty() {
102 return Err(PromptError::Interrupted);
103 }
104 Ok(line.trim().to_string())
105 }
106}