aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2019-01-11 02:31:29 +0000
committerRobin Krahl <robin.krahl@ireas.org>2019-01-11 03:32:45 +0100
commit84bfea7add93a98f83ad958151cca718c33bc0a4 (patch)
treecd860c19962398c06b17f383931441787a6e786e
parentdc754cbc9763b3ea6979ed55bd4e8030fe073078 (diff)
downloaddialog-rs-84bfea7add93a98f83ad958151cca718c33bc0a4.tar.gz
dialog-rs-84bfea7add93a98f83ad958151cca718c33bc0a4.tar.bz2
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.
-rw-r--r--CHANGELOG.md1
-rw-r--r--Cargo.toml1
-rw-r--r--examples/backend-stdio.rs13
-rw-r--r--src/backends/dialog.rs4
-rw-r--r--src/backends/mod.rs3
-rw-r--r--src/backends/stdio.rs89
-rw-r--r--src/lib.rs12
7 files changed, 121 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b11008c..ff1b4a1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
# Unreleased
- Refactor `default_backend` to return a `Box<dyn Backend>`.
- Check the `DIALOG` and `DISPLAY` environment variables in `default_backend`.
+- Add the `Stdio` backend.
# v0.1.1 (2019-01-11)
- Add the `Password` dialog box.
diff --git a/Cargo.toml b/Cargo.toml
index 1a6c816..a4087a8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,3 +12,4 @@ readme = "README.md"
license = "MIT"
[dependencies]
+rpassword = "2"
diff --git a/examples/backend-stdio.rs b/examples/backend-stdio.rs
new file mode 100644
index 0000000..6dbeb8d
--- /dev/null
+++ b/examples/backend-stdio.rs
@@ -0,0 +1,13 @@
+// Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
+use dialog::backends;
+use dialog::DialogBox;
+
+fn main() -> dialog::Result<()> {
+ let backend = backends::Stdio::new();
+
+ dialog::Message::new("This is a message.")
+ .title("And this is a title:")
+ .show_with(&backend)
+}
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<Box<dyn Backend>> {
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 <robin.krahl@ireas.org>
+// 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<Stdio> for Stdio {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+fn print_title(title: &Option<String>) {
+ if let Some(ref title) = title {
+ println!("{}", title);
+ println!("{}", "=".repeat(title.len()));
+ }
+}
+
+fn read_input() -> Result<String> {
+ 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<Option<String>> {
+ 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<Option<String>> {
+ print_title(&password.title);
+ print!("{}: ", password.text);
+ io::stdout().flush()?;
+ Ok(Some(rpassword::read_password()?))
+ }
+
+ fn show_question(&self, question: &Question) -> Result<Choice> {
+ print_title(&question.title);
+ print!("{} [y/n]: ", question.text);
+ io::stdout().flush()?;
+ Ok(parse_choice(&read_input()?))
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 80cabb5..da89e2f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -15,6 +15,8 @@
//! These dialog boxes can be displayed using various backends:
//! - [`Dialog`][]: uses `dialog` to display ncurses-based dialog boxes (requires the external
//! `dialog` tool)
+//! - [`Stdio`][]: prints messages to the standard output and reads user input form standard input
+//! (intended as a fallback backend)
//! - [`Zenity`][]: uses `zenity` to display GTK-based dialog boxes (requires the external `zenity`
//! tool)
//!
@@ -344,9 +346,11 @@ impl DialogBox for Question {
/// - If the `DISPLAY` environment variable is set, the first available backend from this list is
/// used:
/// - [`Zenity`][]
-/// - Otherwise, a [`Dialog`][] instance is returned.
+/// - If the [`Dialog`][] backend is available, it is used.
+/// - Otherwise, a [`Stdio`][] instance is returned.
///
/// [`Dialog`]: backends/struct.Dialog.html
+/// [`Stdio`]: backends/struct.Stdio.html
/// [`Zenity`]: backends/struct.Zenity.html
pub fn default_backend() -> Box<dyn backends::Backend> {
if let Ok(backend) = env::var("DIALOG") {
@@ -363,5 +367,9 @@ pub fn default_backend() -> Box<dyn backends::Backend> {
}
}
- Box::new(backends::Dialog::new())
+ if backends::Dialog::is_available() {
+ Box::new(backends::Dialog::new())
+ } else {
+ Box::new(backends::Stdio::new())
+ }
}