From a0b0a3c6a57097d56c5c471e5cb72bbde8198da8 Mon Sep 17 00:00:00 2001
From: Robin Krahl <robin.krahl@ireas.org>
Date: Tue, 8 Jan 2019 02:11:55 +0000
Subject: Implement message boxes using the dialog backend

This patch adds a first dialog box type, message boxes, and a first
backend, the dialog(1) tool.  It does not yet address the problems of
output handling and backend selection.
---
 src/backends/dialog.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/backends/mod.rs    | 22 ++++++++++++
 2 files changed, 112 insertions(+)
 create mode 100644 src/backends/dialog.rs
 create mode 100644 src/backends/mod.rs

(limited to 'src/backends')

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<()>;
+}
-- 
cgit v1.2.3