use std::process;
use crate::{
Choice, Error, FileSelection, FileSelectionMode, Input, Message, Password, Question, Result,
};
#[derive(Debug, Default)]
pub struct Zenity {
icon: Option<String>,
width: Option<String>,
height: Option<String>,
timeout: Option<String>,
}
impl Zenity {
pub fn new() -> Zenity {
Default::default()
}
pub fn set_icon(&mut self, icon: impl Into<String>) {
self.icon = Some(icon.into());
}
pub fn set_height(&mut self, height: u32) {
self.height = Some(height.to_string());
}
pub fn set_width(&mut self, width: u32) {
self.width = Some(width.to_string());
}
pub fn set_timeout(&mut self, timeout: u32) {
self.timeout = Some(timeout.to_string());
}
pub(crate) fn is_available() -> bool {
super::is_available("zenity")
}
fn execute(&self, args: Vec<&str>, title: &Option<String>) -> Result<process::Output> {
let mut command = process::Command::new("zenity");
if let Some(ref icon) = self.icon {
command.arg("--window-icon");
command.arg(icon);
}
if let Some(ref width) = self.width {
command.arg("--width");
command.arg(width);
}
if let Some(ref height) = self.height {
command.arg("--height");
command.arg(height);
}
if let Some(ref timeout) = self.timeout {
command.arg("--timeout");
command.arg(timeout);
}
if let Some(ref title) = title {
command.arg("--title");
command.arg(title);
}
command.args(args);
command.output().map_err(Error::IoError)
}
}
impl AsRef<Zenity> for Zenity {
fn as_ref(&self) -> &Self {
self
}
}
fn require_success(status: process::ExitStatus) -> Result<()> {
if status.success() {
Ok(())
} else if let Some(code) = status.code() {
match code {
5 => Ok(()),
_ => Err(Error::from(("zenity", status))),
}
} else {
Err(Error::from(("zenity", status)))
}
}
fn get_choice(status: process::ExitStatus) -> Result<Choice> {
if let Some(code) = status.code() {
match code {
0 => Ok(Choice::Yes),
1 => Ok(Choice::No),
5 => Ok(Choice::Cancel),
_ => Err(Error::from(("zenity", status))),
}
} else {
Err(Error::from(("zenity", status)))
}
}
fn get_stdout(output: process::Output) -> Result<Option<String>> {
if output.status.success() {
String::from_utf8(output.stdout)
.map(|s| Some(s.trim_end_matches('\n').to_string()))
.map_err(Error::from)
} else if let Some(code) = output.status.code() {
match code {
0 => Ok(None),
1 => Ok(None),
5 => Ok(None),
_ => Err(Error::from(("zenity", output.status))),
}
} else {
Err(Error::from(("zenity", output.status)))
}
}
impl super::Backend for Zenity {
fn show_input(&self, input: &Input) -> Result<Option<String>> {
let mut args = vec!["--entry", "--text", &input.text];
if let Some(ref default) = input.default {
args.push("--entry-text");
args.push(default);
}
self.execute(args, &input.title).and_then(get_stdout)
}
fn show_message(&self, message: &Message) -> Result<()> {
let args = vec!["--info", "--text", &message.text];
self.execute(args, &message.title)
.and_then(|output| require_success(output.status))
.map(|_| ())
}
fn show_password(&self, password: &Password) -> Result<Option<String>> {
let args = vec!["--password"];
self.execute(args, &password.title).and_then(get_stdout)
}
fn show_question(&self, question: &Question) -> Result<Choice> {
let args = vec!["--question", "--text", &question.text];
self.execute(args, &question.title)
.and_then(|output| get_choice(output.status))
}
fn show_file_selection(&self, file_selection: &FileSelection) -> Result<Option<String>> {
let dir = file_selection.path_to_string().ok_or("path not valid")?;
let mut args = vec!["--file-selection", "--filename", &dir];
if file_selection.mode == FileSelectionMode::Save {
args.push("--save");
}
self.execute(args, &file_selection.title)
.and_then(get_stdout)
}
}