keyfork/keyfork-prompt/src/lib.rs

93 lines
2.4 KiB
Rust
Raw Normal View History

use std::{
io::{BufRead, BufReader, Read, Write},
os::fd::AsRawFd,
sync::{Arc, Mutex},
};
use crossterm::{
event::{read, Event, KeyCode},
style::Print,
terminal,
tty::IsTty,
ExecutableCommand,
};
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: Arc<Mutex<BufReader<R>>>,
write: Arc<Mutex<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: Arc::new(Mutex::new(BufReader::new(read_handle))),
write: Arc::new(Mutex::new(write_handle)),
})
}
fn alt_screen(&self) -> Result<AlternateScreen<W>> {
AlternateScreen::new(self.write.clone())
}
pub fn prompt_input(&mut self, prompt: &str) -> Result<String> {
let mut alt_screen = self.alt_screen()?;
alt_screen
.execute(terminal::Clear(terminal::ClearType::All))?
.execute(Print(prompt))?;
let mut line = String::new();
self.read.lock().unwrap().read_line(&mut line)?;
Ok(line)
}
// TODO: return secrecy::Secret<String>
// TODO: write a guard drop system for raw mode
pub fn prompt_passphrase(&mut self, prompt: &str) -> Result<String> {
let write = AlternateScreen::new(self.write.clone())?;
let mut write = RawMode::new(write.arc_mutex())?;
write
.execute(terminal::Clear(terminal::ClearType::All))?
.execute(Print(prompt))?;
let mut passphrase = String::new();
loop {
match read()? {
Event::Key(k) => match k.code {
KeyCode::Enter => {
passphrase.push('\n');
break;
}
KeyCode::Char(c) => {
write.execute(Print("*"))?;
passphrase.push(c);
}
_ => (),
},
_ => (),
}
}
Ok(passphrase)
}
}