keyfork/keyfork-prompt/src/lib.rs

114 lines
3.1 KiB
Rust
Raw Normal View History

use std::{
io::{BufRead, BufReader, Read, Write},
os::fd::AsRawFd,
};
use crossterm::{
event::{read, Event, KeyCode},
style::{Print, PrintStyledContent, Stylize},
terminal,
cursor,
tty::IsTty,
QueueableCommand,
};
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> {
read: BufReader<R>,
write: W,
}
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 {
read: BufReader::new(read_handle),
write: write_handle,
})
}
pub fn prompt_input(&mut self, prompt: &str) -> Result<String> {
let mut terminal = AlternateScreen::new(&mut self.write)?;
terminal
.queue(terminal::Clear(terminal::ClearType::All))?
.queue(Print(prompt))?
.flush()?;
let mut line = String::new();
self.read.read_line(&mut line)?;
Ok(line)
}
// TODO: return secrecy::Secret<String>
pub fn prompt_passphrase(&mut self, prompt: &str) -> Result<String> {
let mut terminal = AlternateScreen::new(&mut self.write)?;
let mut terminal = RawMode::new(&mut terminal)?;
terminal
.queue(terminal::Clear(terminal::ClearType::All))?
.queue(Print(prompt))?
.flush()?;
let mut passphrase = String::new();
loop {
match read()? {
Event::Key(k) => match k.code {
KeyCode::Enter => {
passphrase.push('\n');
break;
}
KeyCode::Char(c) => {
terminal.queue(Print("*"))?.flush()?;
passphrase.push(c);
}
_ => (),
},
_ => (),
}
}
Ok(passphrase)
}
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
.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()?;
loop {
match read()? {
Event::Key(k) => match k.code {
KeyCode::Enter | KeyCode::Char(' ') => break,
_ => (),
},
_ => (),
}
}
terminal.queue(cursor::EnableBlinking)?.flush()?;
Ok(())
}
}