From 84bfea7add93a98f83ad958151cca718c33bc0a4 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Fri, 11 Jan 2019 02:31:29 +0000 Subject: Add the stdio backend This patch adds the stdio backend which acts as a fallback backend and uses standard input and output. For password queries, the rpassword crate is used to suppress output. Also, default_backend is changed to return Stdio if Dialog is not available. --- src/backends/dialog.rs | 4 +++ src/backends/mod.rs | 3 ++ src/backends/stdio.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 src/backends/stdio.rs (limited to 'src/backends') diff --git a/src/backends/dialog.rs b/src/backends/dialog.rs index 6f078e6..e681caf 100644 --- a/src/backends/dialog.rs +++ b/src/backends/dialog.rs @@ -49,6 +49,10 @@ impl Dialog { self.width = width.to_string(); } + pub(crate) fn is_available() -> bool { + super::is_available("dialog") + } + fn execute( &self, args: Vec<&str>, diff --git a/src/backends/mod.rs b/src/backends/mod.rs index f5af7ef..5ae3cef 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -2,9 +2,11 @@ // SPDX-License-Identifier: MIT mod dialog; +mod stdio; mod zenity; pub use crate::backends::dialog::Dialog; +pub use crate::backends::stdio::Stdio; pub use crate::backends::zenity::Zenity; use std::env; @@ -49,6 +51,7 @@ pub(crate) fn is_available(name: &str) -> bool { pub(crate) fn from_str(s: &str) -> Option> { match s.to_lowercase().as_ref() { "dialog" => Some(Box::new(Dialog::new())), + "stdio" => Some(Box::new(Stdio::new())), "zenity" => Some(Box::new(Zenity::new())), _ => None, } diff --git a/src/backends/stdio.rs b/src/backends/stdio.rs new file mode 100644 index 0000000..6838ef1 --- /dev/null +++ b/src/backends/stdio.rs @@ -0,0 +1,89 @@ +// Copyright (C) 2019 Robin Krahl +// SPDX-License-Identifier: MIT + +use std::io::{self, Write}; + +use crate::{Choice, Input, Message, Password, Question, Result}; + +/// The fallback backend using standard input and output. +/// +/// This backend is intended as a fallback backend to use if no other backend is available. The +/// dialogs are printed to the standard output and user input is read from the standard input. +#[derive(Debug)] +pub struct Stdio {} + +impl Stdio { + /// Creates a new `Stdio` instance. + pub fn new() -> Stdio { + Stdio {} + } +} + +impl AsRef for Stdio { + fn as_ref(&self) -> &Self { + self + } +} + +fn print_title(title: &Option) { + if let Some(ref title) = title { + println!("{}", title); + println!("{}", "=".repeat(title.len())); + } +} + +fn read_input() -> Result { + let mut input = String::new(); + io::stdin().read_line(&mut input)?; + Ok(input.trim_end_matches("\n").to_string()) +} + +fn parse_choice(input: &str) -> Choice { + match input.to_lowercase().as_ref() { + "y" => Choice::Yes, + "yes" => Choice::Yes, + "n" => Choice::No, + "no" => Choice::No, + _ => Choice::Cancel, + } +} + +impl super::Backend for Stdio { + fn show_input(&self, input: &Input) -> Result> { + print_title(&input.title); + if let Some(ref default) = input.default { + print!("{} [default: {}]: ", input.text, default); + } else { + print!("{}: ", input.text); + } + io::stdout().flush()?; + + let user_input = read_input()?; + if user_input.is_empty() { + if let Some(ref default) = input.default { + return Ok(Some(default.to_string())); + } + } + Ok(Some(user_input)) + } + + fn show_message(&self, message: &Message) -> Result<()> { + print_title(&message.title); + println!("{}", message.text); + Ok(()) + } + + fn show_password(&self, password: &Password) -> Result> { + print_title(&password.title); + print!("{}: ", password.text); + io::stdout().flush()?; + Ok(Some(rpassword::read_password()?)) + } + + fn show_question(&self, question: &Question) -> Result { + print_title(&question.title); + print!("{} [y/n]: ", question.text); + io::stdout().flush()?; + Ok(parse_choice(&read_input()?)) + } +} -- cgit v1.2.1