use crossterm::{
event::{KeyCode, KeyEvent, KeyModifiers},
style::{style, Color, PrintStyledContent},
};
use std::cmp;
pub enum Figures {
ArrowUp,
ArrowDown,
ArrowLeft,
ArrowRight,
RadioOn,
RadioOff,
Tick,
Cross,
Ellipsis,
PointerSmall,
Line,
Pointer,
}
impl Figures {
#[cfg(windows)]
pub fn as_str(&self) -> &'static str {
match self {
Figures::ArrowUp => "↑",
Figures::ArrowDown => "↓",
Figures::ArrowLeft => "←",
Figures::ArrowRight => "→",
Figures::RadioOn => "(*)",
Figures::RadioOff => "( )",
Figures::Tick => "√",
Figures::Cross => "×",
Figures::Ellipsis => "...",
Figures::PointerSmall => "»",
Figures::Line => "─",
Figures::Pointer => ">",
}
}
#[cfg(not(windows))]
pub fn as_str(&self) -> &'static str {
match self {
Figures::ArrowUp => "↑",
Figures::ArrowDown => "↓",
Figures::ArrowLeft => "←",
Figures::ArrowRight => "→",
Figures::RadioOn => "◉",
Figures::RadioOff => "◯",
Figures::Tick => "✔",
Figures::Cross => "✖",
Figures::Ellipsis => "…",
Figures::PointerSmall => "›",
Figures::Line => "─",
Figures::Pointer => "❯",
}
}
}
#[derive(Eq, PartialEq, Debug)]
pub enum PromptState {
Created,
Running,
Validate,
Aborted,
Success,
}
impl Default for PromptState {
fn default() -> PromptState {
PromptState::Created
}
}
impl PromptState {
pub fn is_done(&self) -> bool {
*self == PromptState::Aborted || *self == PromptState::Success
}
}
pub fn is_abort_event(event: KeyEvent) -> bool {
match event {
KeyEvent {
modifiers: KeyModifiers::CONTROL,
code: KeyCode::Char('c'),
} => true,
KeyEvent {
modifiers: KeyModifiers::CONTROL,
code: KeyCode::Char('d'),
} => true,
KeyEvent {
modifiers,
code: KeyCode::Esc,
} if modifiers == KeyModifiers::empty() => true,
_ => false,
}
}
pub fn print_state_icon(state: &PromptState) -> PrintStyledContent<&'static str> {
PrintStyledContent(match state {
PromptState::Aborted => style(Figures::Cross.as_str()).with(Color::Red),
PromptState::Success => style(Figures::Tick.as_str()).with(Color::Green),
_ => style("?").with(Color::Magenta),
})
}
pub fn print_input_icon(state: &PromptState) -> PrintStyledContent<String> {
PrintStyledContent(
style(match state {
PromptState::Aborted => "".to_string(),
PromptState::Success => format!("{} ", Figures::Ellipsis.as_str()),
_ => format!("{} ", Figures::PointerSmall.as_str()),
})
.with(Color::Grey),
)
}
pub fn calc_entries(current: usize, total: usize, limit: usize) -> (usize, usize) {
let start_index = cmp::min(
total.checked_sub(limit).unwrap_or(0),
current.checked_sub(limit / 2).unwrap_or(0),
);
let end_index = cmp::min(start_index + limit, total);
(start_index, end_index)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(windows)]
fn write_all() {
let figures = [
Figures::ArrowUp,
Figures::ArrowDown,
Figures::ArrowLeft,
Figures::ArrowRight,
Figures::RadioOn,
Figures::RadioOff,
Figures::Tick,
Figures::Cross,
Figures::Ellipsis,
Figures::PointerSmall,
Figures::Line,
Figures::Pointer,
];
let mut s = String::from("");
for figure in figures.iter() {
s.push_str(figure.as_str());
}
assert_eq!(s, "↑↓←→(*)( )√×...»─>");
}
#[test]
#[cfg(not(windows))]
fn write_all() {
let figures = [
Figures::ArrowUp,
Figures::ArrowDown,
Figures::ArrowLeft,
Figures::ArrowRight,
Figures::RadioOn,
Figures::RadioOff,
Figures::Tick,
Figures::Cross,
Figures::Ellipsis,
Figures::PointerSmall,
Figures::Line,
Figures::Pointer,
];
let mut s = String::from("");
for figure in figures.iter() {
s.push_str(figure.as_str());
}
assert_eq!(s, "↑↓←→◉◯✔✖…›─❯");
}
}