diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/backends/dialog.rs | 90 | ||||
| -rw-r--r-- | src/backends/mod.rs | 22 | ||||
| -rw-r--r-- | src/lib.rs | 134 | 
3 files changed, 246 insertions, 0 deletions
| diff --git a/src/backends/dialog.rs b/src/backends/dialog.rs new file mode 100644 index 0000000..b448c05 --- /dev/null +++ b/src/backends/dialog.rs @@ -0,0 +1,90 @@ +// Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT + +use std::io; +use std::io::Result; +use std::process; + +use crate::Message; + +/// The `dialog` backend. +/// +/// This backend uses the external `dialog` program (not to be confused with this crate also called +/// `dialog`) to display text-based dialog boxes in the terminal. +#[derive(Debug)] +pub struct Dialog { +    backtitle: Option<String>, +    width: String, +    height: String, +} + +impl Dialog { +    /// Creates a new `Dialog` instance without configuration. +    pub fn new() -> Dialog { +        Dialog { +            backtitle: None, +            height: "0".to_string(), +            width: "0".to_string(), +        } +    } + +    fn execute(&self, args: Vec<&str>) -> Result<process::ExitStatus> { +        let mut args = args; +        if let Some(ref backtitle) = self.backtitle { +            args.insert(0, "--backtitle"); +            args.insert(1, backtitle); +        } +        println!("{:?}", args); +        process::Command::new("dialog").args(args).status() +    } + +    /// Sets the backtitle for the dialog boxes. +    /// +    /// The backtitle is displayed on the backdrop, at the top of the screen. +    pub fn set_backtitle(&mut self, backtitle: impl Into<String>) { +        self.backtitle = Some(backtitle.into()); +    } + +    /// Sets the height of the dialog boxes. +    /// +    /// The height is given in characters.  The actual height of the dialog box might be higher +    /// than the given height if the content would not fit otherwise.  The default height is zero. +    pub fn set_height(&mut self, height: u32) { +        self.height = height.to_string(); +    } + +    /// Sets the width of the dialog boxes. +    /// +    /// The width is given in characters.  The actual width of the dialog box might be higher than +    /// the given width if the content would not fit otherwise.  The default width is zero. +    pub fn set_width(&mut self, width: u32) { +        self.width = width.to_string(); +    } + +    fn show_box(&self, args: Vec<&str>, title: &Option<String>) -> Result<process::ExitStatus> { +        let mut args = args; +        if let Some(ref title) = title { +            args.insert(0, "--title"); +            args.insert(1, title); +        } +        args.push(&self.height); +        args.push(&self.width); +        self.execute(args) +    } +} + +fn require_success(status: process::ExitStatus) -> Result<()> { +    if status.success() { +        Ok(()) +    } else { +        Err(io::Error::new(io::ErrorKind::Other, "dialog failed")) +    } +} + +impl super::Backend for Dialog { +    fn show_message(&self, message: &Message) -> Result<()> { +        let args = vec!["--msgbox", &message.text]; +        self.show_box(args, &message.title) +            .and_then(require_success) +    } +} diff --git a/src/backends/mod.rs b/src/backends/mod.rs new file mode 100644 index 0000000..c316307 --- /dev/null +++ b/src/backends/mod.rs @@ -0,0 +1,22 @@ +// Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT + +mod dialog; + +pub use crate::backends::dialog::Dialog; + +use std::io::Result; + +/// A dialog backend. +/// +/// A dialog backend is a program that can be used to display dialog boxes.  Use the +/// [`default_backend`][] function to create a new instance of the default backend, or choose a +/// backend and create an instance manually.  To use a backend, pass it to the [`show_with`][] +/// method of a dialog box. +/// +/// [`default_backend`]: ../function.default_backend.html +/// [`show_with`]: ../trait.DialogBox.html#method.show_with +pub trait Backend { +    /// Shows the given message dialog. +    fn show_message(&self, message: &super::Message) -> Result<()>; +} @@ -1,2 +1,136 @@  // Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>  // SPDX-License-Identifier: MIT + +#![warn(missing_docs, rust_2018_compatibility, rust_2018_idioms, unused)] + +//! Displays dialog boxes using various backends. +//! +//! The `dialog` crate can be used to display different types of dialog boxes.  The supported types +//! are: +//! - [`Message`][]: a simple message box +//! +//! These dialog boxes can be displayed using various backends: +//! - [`Dialog`][]: uses `dialog` to display ncurses-based dialog boxes (requires the external +//!   `dialog` tool) +//! +//! You can let `dialog` choose the backend by calling the [`show`][] method on a dialog box.  If +//! you want to choose the backend yourself, create a backend instance and pass it to +//! [`show_with`][].  You can also use the [`default_backend`][] function to create a backend. +//! +//! # Examples +//! +//! Show a message box using the default backend: +//! +//! ```no_run +//! use dialog::DialogBox; +//! +//! dialog::Message::new("Did you know that I am using the dialog crate?") +//!     .title("Public Service Announcement") +//!     .show() +//!     .expect("Could not display dialog box"); +//! ``` +//! +//! Show a message box using the [`Dialog`][] backend with customized settings: +//! +//! ```no_run +//! use dialog::DialogBox; +//! +//! let mut backend = dialog::backends::Dialog::new(); +//! backend.set_backtitle("dialog demo"); +//! backend.set_width(100); +//! backend.set_height(10); +//! dialog::Message::new("Did you know that I am using the dialog crate?") +//!     .title("Public Service Announcement") +//!     .show_with(&backend) +//!     .expect("Could not display dialog box"); +//! ``` +//! +//! [`Message`]: struct.Message.html +//! [`Dialog`]: backends/struct.Dialog.html +//! [`default_backend`]: fn.default_backend.html +//! [`show`]: trait.DialogBox.html#method.show +//! [`show_with`]: trait.DialogBox.html#method.show_with + +/// Backends that display dialog boxes. +/// +/// All backends implement the [`Backend`][] trait.  Some backends might provide additional +/// settings.  For a list of supported backends, see the [top-level crate documentation](./..) or +/// the [list of structs in this module](#structs). +/// +/// [`Backend`]: trait.Backend.html +pub mod backends; + +use std::io::Result; + +/// A dialog box that can be shown using a backend. +/// +/// Some dialog boxes might return data of the type `Output`. +pub trait DialogBox { +    /// The type of the data returned by the dialog box. +    type Output; + +    /// Shows this dialog box using the default backend. +    /// +    /// `box.show()` is a shorthand for `box.show_with(&default_backend())`. +    fn show(&self) -> Result<Self::Output> { +        self.show_with(&default_backend()) +    } + +    /// Shows this dialog box using the given backend. +    fn show_with(&self, backend: &impl backends::Backend) -> Result<Self::Output>; +} + +/// A message box. +/// +/// This dialog box displays a text and an optional title and has a single OK button.  It does not +/// produce any output. +/// +/// # Example +/// +/// ```no_run +/// use dialog::DialogBox; +/// +/// dialog::Message::new("The operation was successful.") +///     .title("Success") +///     .show() +///     .expect("Could not display dialog box"); +/// ``` +pub struct Message { +    text: String, +    title: Option<String>, +} + +impl Message { +    /// Creates a new message box with the given text. +    pub fn new(text: impl Into<String>) -> Message { +        Message { +            text: text.into(), +            title: None, +        } +    } + +    /// Sets the title of this message box. +    /// +    /// This method returns a reference to `self` to enable chaining. +    pub fn title(&mut self, title: impl Into<String>) -> &mut Message { +        self.title = Some(title.into()); +        self +    } +} + +impl DialogBox for Message { +    type Output = (); + +    fn show_with(&self, backend: &impl backends::Backend) -> Result<Self::Output> { +        backend.show_message(self) +    } +} + +/// Creates a new instance of the default backend. +/// +/// The current implementation always returns a [`Dialog`][] instance. +/// +/// [`Dialog`]: backends/struct.Dialog.html +pub fn default_backend() -> impl backends::Backend { +    backends::Dialog::new() +} | 
