keyfork/crates/util/keyfork-prompt/src/lib.rs

160 lines
5.9 KiB
Rust
Raw Normal View History

2024-01-16 02:44:48 +00:00
//! Prompt display and interaction management.
#[cfg(feature = "mnemonic")]
use keyfork_mnemonic::Wordlist;
2024-01-16 02:44:48 +00:00
///
pub mod terminal;
2024-01-09 07:21:46 +00:00
pub mod validators;
pub use terminal::{default_terminal, DefaultTerminal, Terminal};
2024-01-09 07:21:46 +00:00
2024-01-16 02:44:48 +00:00
/// An error occurred while displaying a prompt.
#[derive(thiserror::Error, Debug)]
pub enum Error {
2024-01-16 02:44:48 +00:00
/// The given handler is not a TTY and can't be used to display prompts.
#[error("The given handler is not a TTY")]
NotATTY,
2024-01-16 02:44:48 +00:00
/// Validating user input failed.
2024-01-09 07:21:46 +00:00
#[error("Validation of the input failed after {0} retries (last error: {1})")]
Validation(u8, String),
/// A ctrl-c interrupt was caught by the handler.
#[error("User pressed ctrl-c, terminating the session")]
CtrlC,
2024-01-16 02:44:48 +00:00
/// An error occurred while interacting with a terminal.
#[error("IO Error: {0}")]
IO(#[from] std::io::Error),
}
2024-01-16 02:44:48 +00:00
#[allow(missing_docs)]
pub type Result<T, E = Error> = std::result::Result<T, E>;
2024-01-16 02:44:48 +00:00
/// A message displayed by [`PromptHandler::prompt_message`].
pub enum Message {
2024-01-16 02:44:48 +00:00
/// A textual message, wrapping at space boundaries when reaching the end of the terminal.
Text(String),
2024-01-16 02:44:48 +00:00
/// A data message, with no word wrapping, and automatic hiding of the message when a terminal
/// is too small.
Data(String),
}
#[doc(hidden)]
pub type BoxResult = std::result::Result<(), Box<dyn std::error::Error>>;
2024-01-16 02:44:48 +00:00
/// A trait to allow displaying prompts and accepting input.
2024-01-11 04:28:56 +00:00
pub trait PromptHandler {
2024-01-16 02:44:48 +00:00
/// Prompt the user for input.
///
/// # Errors
/// The method may return an error if the message was not able to be displayed or if the input
/// could not be read.
2024-01-11 04:28:56 +00:00
fn prompt_input(&mut self, prompt: &str) -> Result<String>;
/// Prompt the user for input based on a wordlist. A language must be specified as the generic
/// parameter `X` (any type implementing [`Wordlist`]) when parsing a wordlist.
2024-01-16 02:44:48 +00:00
///
/// # Errors
/// The method may return an error if the message was not able to be displayed or if the input
/// could not be read.
fn prompt_wordlist(&mut self, prompt: &str, wordlist: &[&str]) -> Result<String>;
/// Prompt the user for a passphrase, which is hidden while typing.
///
/// # Errors
/// The method may return an error if the message was not able to be displayed or if the input
/// could not be read.
fn prompt_passphrase(&mut self, prompt: &str) -> Result<String>;
/// Prompt the user with a [`Message`].
///
/// # Errors
/// The method may return an error if the message was not able to be displayed or if an error
/// occurred while waiting for the user to dismiss the message.
fn prompt_message(&mut self, prompt: Message) -> Result<()>;
2024-01-11 04:28:56 +00:00
2024-01-16 02:44:48 +00:00
/// 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
/// as the generic parameter `X` (any type implementing [`Wordlist`]) when parsing a wordlist.
2024-01-16 02:44:48 +00:00
///
/// This method MUST NOT be used directly. Instead, use
/// [`prompt_validated_wordlist`].
///
2024-01-16 02:44:48 +00:00
/// # Errors
/// The method may return an error if the message was not able to be displayed, if the input
/// could not be read, or if the parser returned an error.
fn prompt_validated_wordlist(
2024-01-11 04:28:56 +00:00
&mut self,
prompt: &str,
retries: u8,
wordlist: &[&str],
validator_fn: &mut dyn FnMut(String) -> BoxResult,
) -> Result<(), Error>;
2024-01-11 04:28:56 +00:00
2024-01-16 02:44:48 +00:00
/// Prompt the user for a passphrase, which is hidden while typing, and validate the passphrase
/// using a provided parser function, returning the type from the parser.
///
/// This method MUST NOT be used directly. Instead, use
/// [`prompt_validated_wordlist`].
///
2024-01-16 02:44:48 +00:00
/// # Errors
/// The method may return an error if the message was not able to be displayed, if the input
/// could not be read, or if the parser returned an error.
fn prompt_validated_passphrase(
2024-01-11 04:28:56 +00:00
&mut self,
prompt: &str,
retries: u8,
validator_fn: &mut dyn FnMut(String) -> BoxResult,
) -> Result<(), Error>;
}
2024-01-11 04:28:56 +00:00
/// 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
/// as the generic parameter `X` (any type implementing [`Wordlist`]) when parsing a wordlist.
///
/// # Errors
/// The method may return an error if the message was not able to be displayed, if the input
/// could not be read, or if the parser returned an error.
#[cfg(feature = "mnemonic")]
#[allow(clippy::missing_panics_doc)]
pub fn prompt_validated_wordlist<X, V>(
handler: &mut dyn PromptHandler,
prompt: &str,
retries: u8,
validator_fn: &dyn Fn(String) -> Result<V, Box<dyn std::error::Error>>,
) -> Result<V, Error>
where
X: Wordlist,
{
let wordlist = X::get_singleton();
let words = wordlist.to_str_array();
let mut opt: Option<V> = None;
handler.prompt_validated_wordlist(prompt, retries, &words, &mut |string| {
opt = Some(validator_fn(string)?);
Ok(())
})?;
Ok(opt.unwrap())
}
/// Prompt the user for a passphrase, which is hidden while typing, and validate the passphrase
/// using a provided parser function, returning the type from the parser.
///
/// # Errors
/// The method may return an error if the message was not able to be displayed, if the input
/// could not be read, or if the parser returned an error.
#[allow(clippy::missing_panics_doc)]
pub fn prompt_validated_passphrase<V>(
handler: &mut dyn PromptHandler,
prompt: &str,
retries: u8,
validator_fn: impl Fn(String) -> Result<V, Box<dyn std::error::Error>>,
) -> Result<V, Error> {
let mut opt: Option<V> = None;
handler.prompt_validated_passphrase(prompt, retries, &mut |string| {
opt = Some(validator_fn(string)?);
Ok(())
})?;
Ok(opt.unwrap())
2024-01-11 04:28:56 +00:00
}