#![warn(missing_docs, rust_2018_compatibility, rust_2018_idioms, unused)]
mod error;
pub mod backends;
use dirs;
use std::{
env,
path::{Path, PathBuf},
};
pub use crate::error::{Error, Result};
pub trait DialogBox {
type Output;
fn show(&self) -> Result<Self::Output> {
self.show_with(default_backend())
}
fn show_with<B>(&self, backend: impl AsRef<B>) -> Result<Self::Output>
where
B: backends::Backend + ?Sized;
}
pub struct Message {
text: String,
title: Option<String>,
}
impl Message {
pub fn new(text: impl Into<String>) -> Message {
Message {
text: text.into(),
title: None,
}
}
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<B>(&self, backend: impl AsRef<B>) -> Result<Self::Output>
where
B: backends::Backend + ?Sized,
{
backend.as_ref().show_message(self)
}
}
pub struct Input {
text: String,
title: Option<String>,
default: Option<String>,
}
impl Input {
pub fn new(text: impl Into<String>) -> Input {
Input {
text: text.into(),
title: None,
default: None,
}
}
pub fn title(&mut self, title: impl Into<String>) -> &mut Input {
self.title = Some(title.into());
self
}
pub fn default(&mut self, default: impl Into<String>) -> &mut Input {
self.default = Some(default.into());
self
}
}
impl DialogBox for Input {
type Output = Option<String>;
fn show_with<B>(&self, backend: impl AsRef<B>) -> Result<Self::Output>
where
B: backends::Backend + ?Sized,
{
backend.as_ref().show_input(self)
}
}
pub struct Password {
text: String,
title: Option<String>,
}
impl Password {
pub fn new(text: impl Into<String>) -> Password {
Password {
text: text.into(),
title: None,
}
}
pub fn title(&mut self, title: impl Into<String>) -> &mut Password {
self.title = Some(title.into());
self
}
}
impl DialogBox for Password {
type Output = Option<String>;
fn show_with<B>(&self, backend: impl AsRef<B>) -> Result<Self::Output>
where
B: backends::Backend + ?Sized,
{
backend.as_ref().show_password(self)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Choice {
Yes,
No,
Cancel,
}
pub struct Question {
text: String,
title: Option<String>,
}
impl Question {
pub fn new(text: impl Into<String>) -> Question {
Question {
text: text.into(),
title: None,
}
}
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<B>(&self, backend: impl AsRef<B>) -> Result<Self::Output>
where
B: backends::Backend + ?Sized,
{
backend.as_ref().show_question(self)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FileSelectionMode {
Open,
Save,
}
pub struct FileSelection {
text: String,
title: Option<String>,
path: Option<PathBuf>,
mode: FileSelectionMode,
}
impl FileSelection {
pub fn new(text: impl Into<String>) -> FileSelection {
FileSelection {
text: text.into(),
title: None,
path: dirs::home_dir(),
mode: FileSelectionMode::Open,
}
}
pub fn title(&mut self, title: impl Into<String>) -> &mut FileSelection {
self.title = Some(title.into());
self
}
pub fn path(&mut self, path: impl AsRef<Path>) -> &mut FileSelection {
self.path = Some(path.as_ref().to_path_buf());
self
}
pub fn path_to_string(&self) -> Option<String> {
match self.path {
Some(ref path) if path.is_dir() => {
path.to_str().map(|s| s.to_string() + "/")
}
_ => None,
}
}
pub fn mode(&mut self, mode: FileSelectionMode) -> &mut FileSelection {
self.mode = mode;
self
}
}
impl DialogBox for FileSelection {
type Output = Option<String>;
fn show_with<B>(&self, backend: impl AsRef<B>) -> Result<Self::Output>
where
B: backends::Backend + ?Sized,
{
backend.as_ref().show_file_selection(self)
}
}
pub fn default_backend() -> Box<dyn backends::Backend> {
if let Ok(backend) = env::var("DIALOG") {
if let Some(backend) = backends::from_str(&backend) {
return backend;
}
}
if let Ok(display) = env::var("DISPLAY") {
if !display.is_empty() {
let kdialog_available = backends::KDialog::is_available();
if let Ok(desktop) = env::var("XDG_CURRENT_DESKTOP") {
if kdialog_available && desktop == "KDE" {
return Box::new(backends::KDialog::new());
}
}
if backends::Zenity::is_available() {
return Box::new(backends::Zenity::new());
}
if kdialog_available {
return Box::new(backends::KDialog::new());
}
}
}
if backends::Dialog::is_available() {
Box::new(backends::Dialog::new())
} else {
Box::new(backends::Stdio::new())
}
}