inquire/terminal/
console.rs

1use std::io::{Result, Write};
2
3use console::{Attribute, Color, Key, Style, Term};
4
5use crate::{
6    error::InquireResult,
7    ui::{Attributes, InputReader, StyleSheet, Styled},
8};
9
10use super::Terminal;
11
12#[derive(Clone)]
13pub struct ConsoleTerminal {
14    term: Term,
15}
16
17impl ConsoleTerminal {
18    #[allow(unused)]
19    pub fn new() -> Self {
20        Self {
21            term: Term::stderr(),
22        }
23    }
24}
25
26impl InputReader for ConsoleTerminal {
27    fn read_key(&mut self) -> InquireResult<crate::ui::Key> {
28        let key = self.term.read_key()?;
29        Ok(key.into())
30    }
31}
32
33impl Terminal for ConsoleTerminal {
34    fn cursor_up(&mut self, cnt: u16) -> Result<()> {
35        match cnt {
36            0 => Ok(()),
37            cnt => self.term.move_cursor_up(cnt as usize),
38        }
39    }
40
41    fn cursor_down(&mut self, cnt: u16) -> Result<()> {
42        match cnt {
43            0 => Ok(()),
44            cnt => self.term.move_cursor_down(cnt as usize),
45        }
46    }
47
48    fn cursor_left(&mut self, cnt: u16) -> Result<()> {
49        match cnt {
50            0 => Ok(()),
51            cnt => self.term.move_cursor_left(cnt as usize),
52        }
53    }
54
55    fn cursor_right(&mut self, cnt: u16) -> Result<()> {
56        match cnt {
57            0 => Ok(()),
58            cnt => self.term.move_cursor_right(cnt as usize),
59        }
60    }
61
62    fn cursor_move_to_column(&mut self, idx: u16) -> Result<()> {
63        // console has no built-in method to set cursor column ¯\_(ツ)_/¯
64        self.term.move_cursor_left(1000)?;
65        self.term.move_cursor_right(idx as usize)?;
66
67        Ok(())
68    }
69
70    fn flush(&mut self) -> Result<()> {
71        self.term.flush()
72    }
73
74    fn get_size(&self) -> Result<Option<super::TerminalSize>> {
75        let (height, width) = self.term.size();
76
77        Ok(super::TerminalSize::new(width, height))
78    }
79
80    fn write<T: std::fmt::Display>(&mut self, val: T) -> Result<()> {
81        write!(self.term, "{}", val)
82    }
83
84    fn write_styled<T: std::fmt::Display>(&mut self, val: &Styled<T>) -> Result<()> {
85        let styled_object = Style::from(val.style).apply_to(&val.content);
86        write!(self.term, "{}", styled_object)
87    }
88
89    fn clear_line(&mut self) -> Result<()> {
90        self.term.clear_line()
91    }
92
93    fn clear_until_new_line(&mut self) -> Result<()> {
94        write!(self.term, "\x1b[K")
95    }
96
97    fn cursor_hide(&mut self) -> Result<()> {
98        self.term.hide_cursor()
99    }
100
101    fn cursor_show(&mut self) -> Result<()> {
102        self.term.show_cursor()
103    }
104}
105
106impl Drop for ConsoleTerminal {
107    fn drop(&mut self) {
108        let _unused = self.flush();
109    }
110}
111
112impl From<StyleSheet> for Style {
113    fn from(from: StyleSheet) -> Self {
114        let mut style = Style::new();
115
116        let bg = from.bg.and_then(crate::ui::Color::into_console_color);
117        if let Some(bg) = bg {
118            style = style.bg(bg);
119        }
120
121        let fg = from.fg.and_then(crate::ui::Color::into_console_color);
122        if let Some(fg) = fg {
123            style = style.fg(fg);
124        }
125
126        if from.att.contains(Attributes::BOLD) {
127            style = style.attr(Attribute::Bold);
128        }
129
130        if from.att.contains(Attributes::ITALIC) {
131            style = style.attr(Attribute::Italic);
132        }
133
134        style
135    }
136}
137
138impl crate::ui::Color {
139    fn into_console_color(self) -> Option<Color> {
140        use crate::ui::Color as C;
141        match self {
142            C::Black | C::DarkGrey => Some(Color::Black),
143            C::LightRed | C::DarkRed => Some(Color::Red),
144            C::LightGreen | C::DarkGreen => Some(Color::Green),
145            C::LightYellow | C::DarkYellow => Some(Color::Yellow),
146            C::LightBlue | C::DarkBlue => Some(Color::Blue),
147            C::LightMagenta | C::DarkMagenta => Some(Color::Magenta),
148            C::LightCyan | C::DarkCyan => Some(Color::Cyan),
149            C::White | C::Grey => Some(Color::White),
150            C::Rgb { r: _, g: _, b: _ } => None,
151            C::AnsiValue(v) => Some(Color::Color256(v)),
152        }
153    }
154}
155
156impl From<Key> for crate::ui::Key {
157    fn from(key: Key) -> Self {
158        use crate::ui::KeyModifiers;
159
160        match key {
161            Key::Escape => Self::Escape,
162            Key::Char('\n' | '\r') | Key::Enter => Self::Enter,
163            Key::Char('\t') | Key::Tab => Self::Tab,
164            Key::Backspace => Self::Backspace,
165            Key::Del => Self::Delete(KeyModifiers::empty()),
166            Key::Home => Self::Home,
167            Key::End => Self::End,
168            Key::PageUp => Self::PageUp(KeyModifiers::empty()),
169            Key::PageDown => Self::PageDown(KeyModifiers::empty()),
170            Key::ArrowUp => Self::Up(KeyModifiers::empty()),
171            Key::ArrowDown => Self::Down(KeyModifiers::empty()),
172            Key::ArrowLeft => Self::Left(KeyModifiers::empty()),
173            Key::ArrowRight => Self::Right(KeyModifiers::empty()),
174            Key::Char(c) => Self::Char(c, KeyModifiers::empty()),
175            #[allow(deprecated)]
176            _ => Self::Any,
177        }
178    }
179}