diff options
| -rw-r--r-- | examples/question.rs | 12 | ||||
| -rw-r--r-- | src/backends/dialog.rs | 33 | ||||
| -rw-r--r-- | src/backends/mod.rs | 3 | ||||
| -rw-r--r-- | src/lib.rs | 60 | 
4 files changed, 104 insertions, 4 deletions
| diff --git a/examples/question.rs b/examples/question.rs new file mode 100644 index 0000000..d9c0e91 --- /dev/null +++ b/examples/question.rs @@ -0,0 +1,12 @@ +// Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT + +use std::io::Result; + +use dialog::DialogBox; + +fn main() -> Result<()> { +    let choice = dialog::Question::new("Do you want to continue?").show()?; +    println!("The user chose: {:?}", choice); +    Ok(()) +} diff --git a/src/backends/dialog.rs b/src/backends/dialog.rs index 8cde8cc..fb0f73f 100644 --- a/src/backends/dialog.rs +++ b/src/backends/dialog.rs @@ -5,7 +5,7 @@ use std::io;  use std::io::Result;  use std::process; -use crate::{Input, Message}; +use crate::{Choice, Input, Message, Question};  /// The `dialog` backend.  /// @@ -87,6 +87,25 @@ fn require_success(status: process::ExitStatus) -> Result<()> {      }  } +fn get_choice(status: process::ExitStatus) -> Result<Choice> { +    if let Some(code) = status.code() { +        match code { +            0 => Ok(Choice::Yes), +            1 => Ok(Choice::No), +            255 => Ok(Choice::Cancel), +            code => Err(io::Error::new( +                io::ErrorKind::Other, +                format!("Could not execute dialog: {}", code), +            )), +        } +    } else { +        Err(io::Error::new( +            io::ErrorKind::Other, +            "dialog was terminated by a signal", +        )) +    } +} +  fn get_stderr(output: process::Output) -> Result<Option<String>> {      if output.status.success() {          String::from_utf8(output.stderr) @@ -99,10 +118,10 @@ fn get_stderr(output: process::Output) -> Result<Option<String>> {              match code {                  0 => Ok(None),                  1 => Ok(None), -                -1 => Ok(None), -                _ => Err(io::Error::new( +                255 => Ok(None), +                code => Err(io::Error::new(                      io::ErrorKind::Other, -                    "Could not execute dialog", +                    format!("Could not execute dialog: {}", code),                  )),              }          } else { @@ -131,4 +150,10 @@ impl super::Backend for Dialog {              .and_then(|output| require_success(output.status))              .map(|_| ())      } + +    fn show_question(&self, question: &Question) -> Result<Choice> { +        let args = vec!["--yesno", &question.text]; +        self.execute(args, vec![], &question.title) +            .and_then(|output| get_choice(output.status)) +    }  } diff --git a/src/backends/mod.rs b/src/backends/mod.rs index 1abb8d1..12a178b 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -22,4 +22,7 @@ pub trait Backend {      /// Shows the given message dialog.      fn show_message(&self, message: &super::Message) -> Result<()>; + +    /// Shows the given question dialog and returns the choice. +    fn show_question(&self, question: &super::Question) -> Result<super::Choice>;  } @@ -9,6 +9,7 @@  //! are:  //! - [`Input`][]: a text input dialog  //! - [`Message`][]: a simple message box +//! - [`Question`][]: a question dialog box  //!  //! These dialog boxes can be displayed using various backends:  //! - [`Dialog`][]: uses `dialog` to display ncurses-based dialog boxes (requires the external @@ -64,6 +65,7 @@  //! [`Dialog`]: backends/struct.Dialog.html  //! [`Input`]: struct.Input.html  //! [`Message`]: struct.Message.html +//! [`Question`]: struct.Question.html  //! [`default_backend`]: fn.default_backend.html  //! [`show`]: trait.DialogBox.html#method.show  //! [`show_with`]: trait.DialogBox.html#method.show_with @@ -203,6 +205,64 @@ impl DialogBox for Input {      }  } +/// A user choise in a dialog box. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Choice { +    /// The yes button. +    Yes, +    /// The no button. +    No, +    /// The cancel button or a cancelled dialog. +    Cancel, +} + +/// A question dialog box. +/// +/// This dialog box displays a text and an optional title and has a yes and a no button.  The +/// output is the button presed by the user, or Cancel if the dialog has been cancelled. +/// +/// # Example +/// +/// ```no_run +/// use dialog::DialogBox; +/// +/// let choice = dialog::Question::new("Do you want to continue?") +///     .title("Question") +///     .show() +///     .expect("Could not display dialog box"); +/// println!("The user chose: {:?}", choice); +/// ``` +pub struct Question { +    text: String, +    title: Option<String>, +} + +impl Question { +    /// Creates a new question dialog with the given text. +    pub fn new(text: impl Into<String>) -> Question { +        Question { +            text: text.into(), +            title: None, +        } +    } + +    /// Sets the title of this question dialog box. +    /// +    /// This method returns a reference to `self` to enable chaining. +    pub fn title(&mut self, title: impl Into<String>) -> &mut Question { +        self.title = Some(title.into()); +        self +    } +} + +impl DialogBox for Question { +    type Output = Choice; + +    fn show_with(&self, backend: &impl backends::Backend) -> Result<Self::Output> { +        backend.show_question(self) +    } +} +  /// Creates a new instance of the default backend.  ///  /// The current implementation always returns a [`Dialog`][] instance. | 
