Compare commits
3 Commits
e42e362aea
...
1ac99e16f8
Author | SHA1 | Date |
---|---|---|
Ryan Heywood | 1ac99e16f8 | |
Ryan Heywood | 0ea49109d1 | |
Ryan Heywood | d8f9fc216f |
|
@ -441,6 +441,21 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"filedescriptor",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
|
@ -687,6 +702,17 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d"
|
||||
|
||||
[[package]]
|
||||
name = "filedescriptor"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"thiserror",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
|
@ -1068,6 +1094,14 @@ dependencies = [
|
|||
"smex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keyfork-prompt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crossterm",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keyfork-shard"
|
||||
version = "0.1.0"
|
||||
|
@ -1281,6 +1315,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
@ -2033,6 +2068,27 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
|
@ -2203,18 +2259,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.50"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
|
||||
checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.50"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||
checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -10,6 +10,7 @@ members = [
|
|||
"keyfork-frame",
|
||||
"keyfork-mnemonic-util",
|
||||
"keyfork-pinentry",
|
||||
"keyfork-prompt",
|
||||
"keyfork-plumbing",
|
||||
"keyfork-shard",
|
||||
"keyfork-slip10-test-data",
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "keyfork-prompt"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
crossterm = { version = "0.27.0", default-features = false, features = ["use-dev-tty", "events"] }
|
||||
thiserror = "1.0.51"
|
|
@ -0,0 +1,80 @@
|
|||
use std::{
|
||||
io::Write,
|
||||
os::fd::AsRawFd,
|
||||
};
|
||||
|
||||
use crossterm::{
|
||||
cursor::MoveTo,
|
||||
terminal::{EnterAlternateScreen, LeaveAlternateScreen},
|
||||
ExecutableCommand,
|
||||
};
|
||||
|
||||
use crate::Result;
|
||||
|
||||
pub(crate) struct AlternateScreen<'a, W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
write: &'a mut W,
|
||||
}
|
||||
|
||||
impl<'a, W> AlternateScreen<'a, W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
pub(crate) fn new(write_handle: &'a mut W) -> Result<Self> {
|
||||
write_handle.execute(EnterAlternateScreen)?.execute(MoveTo(0, 0))?;
|
||||
Ok(Self {
|
||||
write: write_handle,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> Write for AlternateScreen<'_, W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.write.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
self.write.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> AsRawFd for AlternateScreen<'_, W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
fn as_raw_fd(&self) -> std::os::fd::RawFd {
|
||||
self.write.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl<W> ExecutableCommand for AlternateScreen<W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
fn execute(&mut self, command: impl crossterm::Command) -> std::io::Result<&mut Self> {
|
||||
let mut write = self.write.lock().unwrap();
|
||||
match write.execute(command) {
|
||||
Ok(_) => {
|
||||
drop(write);
|
||||
Ok(self)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<W> Drop for AlternateScreen<'_, W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.write.execute(LeaveAlternateScreen).unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
use std::io::{stdin, stdout};
|
||||
|
||||
use keyfork_prompt::*;
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
let mut mgr = PromptManager::new(stdin(), stdout())?;
|
||||
mgr.prompt_input("Mnemonic: ")?;
|
||||
mgr.prompt_passphrase("Passphrase: ")?;
|
||||
mgr.prompt_message("Please press enter.")?;
|
||||
mgr.prompt_message("Please press space bar.")?;
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
use std::{
|
||||
io::{BufRead, BufReader, Read, Write},
|
||||
os::fd::AsRawFd,
|
||||
};
|
||||
|
||||
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: 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
|
||||
.execute(terminal::Clear(terminal::ClearType::All))?
|
||||
.execute(Print(prompt))?;
|
||||
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
|
||||
.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) => {
|
||||
terminal.execute(Print("*"))?;
|
||||
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
|
||||
.execute(terminal::Clear(terminal::ClearType::All))?
|
||||
.execute(Print(prompt))?;
|
||||
loop {
|
||||
match read()? {
|
||||
Event::Key(k) => match k.code {
|
||||
KeyCode::Enter | KeyCode::Char(' ') => break,
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
use std::{
|
||||
io::Write,
|
||||
os::fd::AsRawFd,
|
||||
};
|
||||
|
||||
use crossterm::terminal;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
pub(crate) struct RawMode<'a, W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
write: &'a mut W,
|
||||
}
|
||||
|
||||
// TODO: fork crossterm to allow using FD from as_raw_fd()
|
||||
impl<'a, W> RawMode<'a, W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
pub(crate) fn new(write_handle: &'a mut W) -> Result<Self> {
|
||||
terminal::enable_raw_mode()?;
|
||||
Ok(Self {
|
||||
write: write_handle,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> Write for RawMode<'_, W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.write.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
self.write.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> AsRawFd for RawMode<'_, W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
fn as_raw_fd(&self) -> std::os::fd::RawFd {
|
||||
self.write.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl<W> ExecutableCommand for RawMode<W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
fn execute(&mut self, command: impl crossterm::Command) -> std::io::Result<&mut Self> {
|
||||
let mut write = self.write.lock().unwrap();
|
||||
match write.execute(command) {
|
||||
Ok(_) => {
|
||||
drop(write);
|
||||
Ok(self)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<W> Drop for RawMode<'_, W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
terminal::disable_raw_mode().unwrap();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue