93 lines
2.4 KiB
Rust
93 lines
2.4 KiB
Rust
|
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)
|
||
|
}
|
||
|
}
|