Skip to content

Commit

Permalink
Add ask password prompt
Browse files Browse the repository at this point in the history
  • Loading branch information
Trivernis committed Apr 8, 2023
1 parent e588990 commit 0b05714
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 16 deletions.
4 changes: 2 additions & 2 deletions src/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl DialogPlugin {
}

if call.has_flag("abortable") {
let result = confirm.prompt_opt()?;
let result = confirm.ask_opt(call.head)?;

if let Some(val) = result {
Ok(Value::Bool {
Expand All @@ -31,7 +31,7 @@ impl DialogPlugin {
Ok(Value::Nothing { span: call.head })
}
} else {
let result = confirm.prompt()?;
let result = confirm.ask(call.head)?;
Ok(Value::Bool {
val: result,
span: call.head,
Expand Down
21 changes: 21 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use nu_plugin::{LabeledError, Plugin};
use nu_protocol::{PluginSignature, SyntaxShape};

mod confirm;
mod password;
mod prompt;
mod select;

Expand Down Expand Up @@ -63,6 +64,25 @@ impl Plugin for DialogPlugin {
None,
)
.category(nu_protocol::Category::Misc),
PluginSignature::build("ask password")
.usage("Prompt the user with a password input.")
.named(
"prompt",
SyntaxShape::String,
"The prompt to this password input",
None,
)
.switch(
"confirm",
"Prompts the user twice for matching password inputs",
None,
)
.switch(
"allow-empty",
"Allows the user to input an empty password",
None,
)
.category(nu_protocol::Category::Misc),
]
}

Expand All @@ -75,6 +95,7 @@ impl Plugin for DialogPlugin {
match name {
"ask confirm" => self.confirm(call, input),
"ask select" => self.select(call, input),
"ask password" => self.password(call, input),
"ask" =>
Err(LabeledError {
label: "Missing subcommand".into(),
Expand Down
27 changes: 27 additions & 0 deletions src/password.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use nu_plugin::{EvaluatedCall, LabeledError};
use nu_protocol::Value;

use crate::{prompt::UserPrompt, DialogPlugin};

impl DialogPlugin {
pub(crate) fn password(
&self,
call: &EvaluatedCall,
_input: &Value,
) -> Result<Value, LabeledError> {
let mut pw_input = dialoguer::Password::with_theme(&*self.theme);

pw_input.allow_empty_password(call.has_flag("allow-empty"));

if call.has_flag("confirm") {
pw_input.with_confirmation("Repeat your input", "Error: The inputs don't match");
}

let password = pw_input.ask(call.head)?;

Ok(Value::String {
val: password,
span: call.head,
})
}
}
9 changes: 5 additions & 4 deletions src/prompt/generic_select.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use dialoguer::{theme::Theme, FuzzySelect, Select};
use nu_protocol::Span;

use super::{create_labeled_error, UserPrompt};

Expand Down Expand Up @@ -44,20 +45,20 @@ impl<'a> GenericSelect<'a> {
impl<'a> UserPrompt for GenericSelect<'a> {
type Output = usize;

fn prompt(&self) -> Result<Self::Output, nu_plugin::LabeledError> {
fn ask(&self, span: Span) -> Result<Self::Output, nu_plugin::LabeledError> {
match self {
GenericSelect::Fuzzy(f) => f.interact(),
GenericSelect::Normal(n) => n.interact(),
}
.map_err(create_labeled_error)
.map_err(|e| create_labeled_error(e, span))
}

fn prompt_opt(&self) -> Result<Option<Self::Output>, nu_plugin::LabeledError> {
fn ask_opt(&self, span: Span) -> Result<Option<Self::Output>, nu_plugin::LabeledError> {
match self {
GenericSelect::Fuzzy(f) => f.interact_opt(),
GenericSelect::Normal(n) => n.interact_opt(),
}
.map_err(create_labeled_error)
.map_err(|e| create_labeled_error(e, span))
}
}

Expand Down
31 changes: 23 additions & 8 deletions src/prompt/mod.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,48 @@
use std::io;

use dialoguer::Password;
use nu_plugin::LabeledError;
mod generic_select;
pub use generic_select::GenericSelect;
use nu_protocol::Span;

pub trait UserPrompt {
type Output;

fn prompt(&self) -> Result<Self::Output, LabeledError>;
fn ask(&self, span: Span) -> Result<Self::Output, LabeledError>;

fn prompt_opt(&self) -> Result<Option<Self::Output>, LabeledError>;
fn ask_opt(&self, span: Span) -> Result<Option<Self::Output>, LabeledError>;
}

impl<'a> UserPrompt for dialoguer::Confirm<'a> {
type Output = bool;

fn prompt(&self) -> Result<Self::Output, LabeledError> {
self.interact().map_err(create_labeled_error)
fn ask(&self, span: Span) -> Result<Self::Output, LabeledError> {
self.interact().map_err(|e| create_labeled_error(e, span))
}

fn prompt_opt(&self) -> Result<Option<Self::Output>, LabeledError> {
self.interact_opt().map_err(create_labeled_error)
fn ask_opt(&self, span: Span) -> Result<Option<Self::Output>, LabeledError> {
self.interact_opt()
.map_err(|e| create_labeled_error(e, span))
}
}

fn create_labeled_error(e: io::Error) -> LabeledError {
impl<'a> UserPrompt for Password<'a> {
type Output = String;

fn ask(&self, span: Span) -> Result<Self::Output, LabeledError> {
self.interact().map_err(|e| create_labeled_error(e, span))
}

fn ask_opt(&self, span: Span) -> Result<Option<Self::Output>, LabeledError> {
self.ask(span).map(Option::Some)
}
}

fn create_labeled_error(e: io::Error, span: Span) -> LabeledError {
LabeledError {
label: "Failed to prompt user".into(),
msg: e.to_string(),
span: None,
span: Some(span),
}
}
4 changes: 2 additions & 2 deletions src/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl DialogPlugin {
}

if call.has_flag("abortable") {
if let Some(selection) = select.prompt_opt()? {
if let Some(selection) = select.ask_opt(call.head)? {
let selected_item = options.remove(selection);

Ok(Value::String {
Expand All @@ -40,7 +40,7 @@ impl DialogPlugin {
Ok(Value::Nothing { span: call.head })
}
} else {
let selection = select.prompt()?;
let selection = select.ask(call.head)?;
let selected_item = options.remove(selection);

Ok(Value::String {
Expand Down

0 comments on commit 0b05714

Please sign in to comment.