diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | examples/backend-stdio.rs | 13 | ||||
| -rw-r--r-- | src/backends/dialog.rs | 4 | ||||
| -rw-r--r-- | src/backends/mod.rs | 3 | ||||
| -rw-r--r-- | src/backends/stdio.rs | 89 | ||||
| -rw-r--r-- | src/lib.rs | 12 | 
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. @@ -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()?)) +    } +} @@ -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()) +    }  } | 
