Compare commits
No commits in common. "3fd992d5826a93e76dd230d73d4343d52f6c466a" and "c868afedbf6ced096b36f026310566d1602a4cb8" have entirely different histories.
3fd992d582
...
c868afedbf
|
@ -1674,7 +1674,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyfork"
|
name = "keyfork"
|
||||||
version = "0.2.2"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"card-backend-pcsc",
|
"card-backend-pcsc",
|
||||||
"clap",
|
"clap",
|
||||||
|
@ -1744,7 +1744,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyfork-derive-openpgp"
|
name = "keyfork-derive-openpgp"
|
||||||
version = "0.1.2"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "keyfork-derive-openpgp"
|
name = "keyfork-derive-openpgp"
|
||||||
version = "0.1.2"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-only"
|
license = "AGPL-3.0-only"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "keyfork"
|
name = "keyfork"
|
||||||
version = "0.2.2"
|
version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-only"
|
license = "AGPL-3.0-only"
|
||||||
|
|
||||||
|
|
|
@ -3,34 +3,36 @@
|
||||||
use std::io::{stdin, stdout};
|
use std::io::{stdin, stdout};
|
||||||
|
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
MaybeIdentifier, PromptHandler, Terminal,
|
validators::{mnemonic, Validator},
|
||||||
|
Terminal, PromptHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
use keyfork_mnemonic_util::English;
|
||||||
pub enum Example {
|
|
||||||
RetryQR,
|
|
||||||
UseMnemonic,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MaybeIdentifier for Example {}
|
|
||||||
|
|
||||||
impl std::fmt::Display for Example {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Example::RetryQR => f.write_str("Retry QR Code"),
|
|
||||||
Example::UseMnemonic => f.write_str("Use Mnemonic"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut mgr = Terminal::new(stdin(), stdout())?;
|
let mut mgr = Terminal::new(stdin(), stdout())?;
|
||||||
|
let transport_validator = mnemonic::MnemonicSetValidator {
|
||||||
|
word_lengths: [9, 24],
|
||||||
|
};
|
||||||
|
let combine_validator = mnemonic::MnemonicSetValidator {
|
||||||
|
word_lengths: [24, 48],
|
||||||
|
};
|
||||||
|
|
||||||
let choice = mgr.prompt_choice(
|
let mnemonics = mgr.prompt_validated_wordlist::<English, _>(
|
||||||
"Unable to detect QR code.",
|
"Enter a 9-word and 24-word mnemonic: ",
|
||||||
&[Example::RetryQR, Example::UseMnemonic],
|
3,
|
||||||
|
transport_validator.to_fn(),
|
||||||
)?;
|
)?;
|
||||||
dbg!(choice);
|
assert_eq!(mnemonics[0].as_bytes().len(), 12);
|
||||||
|
assert_eq!(mnemonics[1].as_bytes().len(), 32);
|
||||||
|
|
||||||
|
let mnemonics = mgr.prompt_validated_wordlist::<English, _>(
|
||||||
|
"Enter a 24 and 48-word mnemonic: ",
|
||||||
|
3,
|
||||||
|
combine_validator.to_fn(),
|
||||||
|
)?;
|
||||||
|
assert_eq!(mnemonics[0].as_bytes().len(), 32);
|
||||||
|
assert_eq!(mnemonics[1].as_bytes().len(), 64);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use keyfork_mnemonic_util::Wordlist;
|
||||||
///
|
///
|
||||||
pub mod terminal;
|
pub mod terminal;
|
||||||
pub mod validators;
|
pub mod validators;
|
||||||
pub use terminal::{default_terminal, DefaultTerminal, Terminal};
|
pub use terminal::{Terminal, DefaultTerminal, default_terminal};
|
||||||
|
|
||||||
/// An error occurred while displaying a prompt.
|
/// An error occurred while displaying a prompt.
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
@ -42,12 +42,6 @@ pub enum Message {
|
||||||
Data(String),
|
Data(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MaybeIdentifier {
|
|
||||||
fn identifier(&self) -> Option<char> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait to allow displaying prompts and accepting input.
|
/// A trait to allow displaying prompts and accepting input.
|
||||||
pub trait PromptHandler {
|
pub trait PromptHandler {
|
||||||
/// Prompt the user for input.
|
/// Prompt the user for input.
|
||||||
|
@ -64,9 +58,7 @@ pub trait PromptHandler {
|
||||||
/// The method may return an error if the message was not able to be displayed or if the input
|
/// The method may return an error if the message was not able to be displayed or if the input
|
||||||
/// could not be read.
|
/// could not be read.
|
||||||
#[cfg(feature = "mnemonic")]
|
#[cfg(feature = "mnemonic")]
|
||||||
fn prompt_wordlist<X>(&mut self, prompt: &str) -> Result<String>
|
fn prompt_wordlist<X>(&mut self, prompt: &str) -> Result<String> where X: Wordlist;
|
||||||
where
|
|
||||||
X: Wordlist;
|
|
||||||
|
|
||||||
/// Prompt the user for input based on a wordlist, while validating the wordlist using a
|
/// Prompt the user for input based on a wordlist, while validating the wordlist using a
|
||||||
/// provided parser function, returning the type from the parser. A language must be specified
|
/// provided parser function, returning the type from the parser. A language must be specified
|
||||||
|
@ -105,19 +97,6 @@ pub trait PromptHandler {
|
||||||
validator_fn: impl Fn(String) -> Result<V, Box<dyn std::error::Error>>,
|
validator_fn: impl Fn(String) -> Result<V, Box<dyn std::error::Error>>,
|
||||||
) -> Result<V, Error>;
|
) -> Result<V, Error>;
|
||||||
|
|
||||||
/// Prompt the user to select a choice between multiple options.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// The metho may return an error if the message was not able to be displayed or if a choice
|
|
||||||
/// could not be received.
|
|
||||||
fn prompt_choice<'a, T>(
|
|
||||||
&mut self,
|
|
||||||
prompt: &str,
|
|
||||||
choices: &'a [T],
|
|
||||||
) -> Result<&'a T, Error>
|
|
||||||
where
|
|
||||||
T: std::fmt::Display + PartialEq + MaybeIdentifier;
|
|
||||||
|
|
||||||
/// Prompt the user with a [`Message`].
|
/// Prompt the user with a [`Message`].
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
@ -15,7 +15,7 @@ use keyfork_crossterm::{
|
||||||
|
|
||||||
use keyfork_bug::bug;
|
use keyfork_bug::bug;
|
||||||
|
|
||||||
use crate::{Error, MaybeIdentifier, Message, PromptHandler, Wordlist};
|
use crate::{Error, Message, PromptHandler, Wordlist};
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
@ -122,9 +122,6 @@ where
|
||||||
W: Write + AsRawFd,
|
W: Write + AsRawFd,
|
||||||
{
|
{
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.write
|
|
||||||
.execute(cursor::Show)
|
|
||||||
.expect(bug!("can't enable cursor blinking"));
|
|
||||||
self.write
|
self.write
|
||||||
.execute(DisableBracketedPaste)
|
.execute(DisableBracketedPaste)
|
||||||
.expect(bug!("can't restore bracketed paste"));
|
.expect(bug!("can't restore bracketed paste"));
|
||||||
|
@ -458,79 +455,6 @@ where
|
||||||
Ok(passphrase)
|
Ok(passphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prompt_choice<'a, T>(&mut self, prompt: &str, choices: &'a [T]) -> Result<&'a T, Error>
|
|
||||||
where
|
|
||||||
T: std::fmt::Display + PartialEq + MaybeIdentifier,
|
|
||||||
{
|
|
||||||
let mut terminal = self.lock().alternate_screen()?.raw_mode()?;
|
|
||||||
|
|
||||||
terminal
|
|
||||||
.queue(terminal::Clear(terminal::ClearType::All))?
|
|
||||||
.queue(cursor::MoveTo(0, 0))?
|
|
||||||
.queue(cursor::Hide)?;
|
|
||||||
|
|
||||||
for line in prompt.lines() {
|
|
||||||
terminal.queue(Print(line))?;
|
|
||||||
terminal
|
|
||||||
.queue(cursor::MoveDown(1))?
|
|
||||||
.queue(cursor::MoveToColumn(0))?;
|
|
||||||
|
|
||||||
}
|
|
||||||
terminal.flush()?;
|
|
||||||
|
|
||||||
let mut active_choice = 0;
|
|
||||||
|
|
||||||
let mut redraw = |active_choice| {
|
|
||||||
terminal.queue(cursor::MoveToColumn(0))?;
|
|
||||||
|
|
||||||
let mut iter = choices.iter().enumerate().peekable();
|
|
||||||
while let Some((i, choice)) = iter.next() {
|
|
||||||
// if active choice, flip foreground and background
|
|
||||||
// if active choice, wrap in []
|
|
||||||
// if not, wrap in spaces, to preserve spacing
|
|
||||||
if i == active_choice {
|
|
||||||
terminal.queue(PrintStyledContent(format!("[{choice}]").to_string().reverse()))?;
|
|
||||||
} else {
|
|
||||||
terminal.queue(Print(format!(" {choice} ").to_string()))?;
|
|
||||||
}
|
|
||||||
if iter.peek().is_some() {
|
|
||||||
terminal.queue(Print(" "))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
terminal.flush()?;
|
|
||||||
Ok::<_, Error>(())
|
|
||||||
};
|
|
||||||
|
|
||||||
redraw(active_choice)?;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if let Event::Key(k) = read()? {
|
|
||||||
match k.code {
|
|
||||||
KeyCode::Char('c') if k.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
return Err(Error::CtrlC);
|
|
||||||
}
|
|
||||||
KeyCode::Left => {
|
|
||||||
eprintln!("{active_choice}");
|
|
||||||
// prevent underflow
|
|
||||||
// if 0, max is 1, -1 is 0, no underflow
|
|
||||||
// if 1, max is 1, -1 is 0
|
|
||||||
// if 2 or higher, max is 2 or higher, -1 is fine
|
|
||||||
active_choice = std::cmp::max(1, active_choice) - 1;
|
|
||||||
}
|
|
||||||
KeyCode::Right => {
|
|
||||||
eprintln!("{active_choice}");
|
|
||||||
active_choice = std::cmp::min(choices.len() - 1, active_choice + 1);
|
|
||||||
}
|
|
||||||
KeyCode::Enter => {
|
|
||||||
return Ok(&choices[active_choice]);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
redraw(active_choice)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prompt_message(&mut self, prompt: impl Borrow<Message>) -> Result<()> {
|
fn prompt_message(&mut self, prompt: impl Borrow<Message>) -> Result<()> {
|
||||||
let mut terminal = self.lock().alternate_screen()?.raw_mode()?;
|
let mut terminal = self.lock().alternate_screen()?.raw_mode()?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue