2023-12-21 17:04:35 +00:00
|
|
|
use std::{
|
|
|
|
io::{BufRead, BufReader, Read, Write},
|
|
|
|
os::fd::AsRawFd,
|
|
|
|
};
|
|
|
|
|
|
|
|
use crossterm::{
|
|
|
|
event::{read, Event, KeyCode},
|
2023-12-21 19:02:42 +00:00
|
|
|
style::{Print, PrintStyledContent, Stylize},
|
2023-12-21 17:04:35 +00:00
|
|
|
terminal,
|
2023-12-21 19:02:42 +00:00
|
|
|
cursor,
|
2023-12-21 17:04:35 +00:00
|
|
|
tty::IsTty,
|
2023-12-21 19:02:42 +00:00
|
|
|
QueueableCommand,
|
2023-12-21 17:04:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
mod alternate_screen;
|
|
|
|
mod raw_mode;
|
|
|
|
use alternate_screen::*;
|
|
|
|
use raw_mode::*;
|
|
|
|
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
#[error("The given handler is not a TTY")]
|
|
|
|
NotATTY,
|
|
|
|
|
|
|
|
#[error("IO Error: {0}")]
|
|
|
|
IO(#[from] std::io::Error),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
|
|
|
|
|
|
|
pub struct PromptManager<R, W> {
|
2023-12-21 17:12:52 +00:00
|
|
|
read: BufReader<R>,
|
|
|
|
write: W,
|
2023-12-21 17:04:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<R, W> PromptManager<R, W>
|
|
|
|
where
|
|
|
|
R: Read + Sized,
|
|
|
|
W: Write + AsRawFd + Sized,
|
|
|
|
{
|
|
|
|
pub fn new(read_handle: R, write_handle: W) -> Result<Self> {
|
|
|
|
if !write_handle.is_tty() {
|
|
|
|
return Err(Error::NotATTY);
|
|
|
|
}
|
|
|
|
Ok(Self {
|
2023-12-21 17:12:52 +00:00
|
|
|
read: BufReader::new(read_handle),
|
|
|
|
write: write_handle,
|
2023-12-21 17:04:35 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn prompt_input(&mut self, prompt: &str) -> Result<String> {
|
2023-12-21 17:12:52 +00:00
|
|
|
let mut terminal = AlternateScreen::new(&mut self.write)?;
|
|
|
|
terminal
|
2023-12-21 19:02:42 +00:00
|
|
|
.queue(terminal::Clear(terminal::ClearType::All))?
|
|
|
|
.queue(Print(prompt))?
|
|
|
|
.flush()?;
|
2023-12-21 17:04:35 +00:00
|
|
|
let mut line = String::new();
|
2023-12-21 17:12:52 +00:00
|
|
|
self.read.read_line(&mut line)?;
|
2023-12-21 17:04:35 +00:00
|
|
|
Ok(line)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: return secrecy::Secret<String>
|
|
|
|
pub fn prompt_passphrase(&mut self, prompt: &str) -> Result<String> {
|
2023-12-21 17:12:52 +00:00
|
|
|
let mut terminal = AlternateScreen::new(&mut self.write)?;
|
|
|
|
let mut terminal = RawMode::new(&mut terminal)?;
|
|
|
|
terminal
|
2023-12-21 19:02:42 +00:00
|
|
|
.queue(terminal::Clear(terminal::ClearType::All))?
|
|
|
|
.queue(Print(prompt))?
|
|
|
|
.flush()?;
|
2023-12-21 17:04:35 +00:00
|
|
|
let mut passphrase = String::new();
|
|
|
|
loop {
|
|
|
|
match read()? {
|
|
|
|
Event::Key(k) => match k.code {
|
|
|
|
KeyCode::Enter => {
|
|
|
|
passphrase.push('\n');
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
KeyCode::Char(c) => {
|
2023-12-21 19:02:42 +00:00
|
|
|
terminal.queue(Print("*"))?.flush()?;
|
2023-12-21 17:04:35 +00:00
|
|
|
passphrase.push(c);
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(passphrase)
|
|
|
|
}
|
2023-12-21 17:18:16 +00:00
|
|
|
|
|
|
|
pub fn prompt_message(&mut self, prompt: &str) -> Result<()> {
|
|
|
|
let mut terminal = AlternateScreen::new(&mut self.write)?;
|
|
|
|
let mut terminal = RawMode::new(&mut terminal)?;
|
|
|
|
terminal
|
2023-12-21 19:02:42 +00:00
|
|
|
.queue(terminal::Clear(terminal::ClearType::All))?
|
|
|
|
.queue(Print(prompt))?
|
|
|
|
.queue(cursor::DisableBlinking)?
|
|
|
|
.queue(cursor::MoveDown(1))?
|
|
|
|
.queue(cursor::MoveToColumn(0))?
|
|
|
|
.queue(PrintStyledContent(" OK ".negative()))?
|
|
|
|
.flush()?;
|
2023-12-21 17:18:16 +00:00
|
|
|
loop {
|
|
|
|
match read()? {
|
|
|
|
Event::Key(k) => match k.code {
|
|
|
|
KeyCode::Enter | KeyCode::Char(' ') => break,
|
|
|
|
_ => (),
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
2023-12-21 19:02:42 +00:00
|
|
|
terminal.queue(cursor::EnableBlinking)?.flush()?;
|
2023-12-21 17:18:16 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2023-12-21 17:04:35 +00:00
|
|
|
}
|