mod argv;
mod backend;
mod error;
mod outcome;
mod request;
pub use backend::Backend;
pub use error::Error;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use argv::argv;
use backend::find_on_path;
use outcome::{Outcome, classify};
use request::Request;
#[derive(Clone, Debug)]
pub struct Dialogs {
backend: Option<(Backend, PathBuf)>,
}
impl Dialogs {
pub fn new() -> Dialogs {
Dialogs::with_paths(find_on_path("kdialog"), find_on_path("zenity"))
}
pub fn with_paths(kdialog: Option<PathBuf>, zenity: Option<PathBuf>) -> Dialogs {
let backend = kdialog
.map(|path| (Backend::Kdialog, path))
.or_else(|| zenity.map(|path| (Backend::Zenity, path)));
Dialogs { backend }
}
pub fn available(&self) -> bool {
self.backend.is_some()
}
pub fn prompt_text(
&self,
title: &str,
label: &str,
initial: Option<&str>,
) -> Result<Option<String>, Error> {
self.run(Request::Text {
title,
label,
initial,
})
}
#[allow(dead_code)] pub fn prompt_choice(
&self,
title: &str,
label: &str,
choices: &[&str],
current: Option<&str>,
) -> Result<Option<String>, Error> {
self.run(Request::Choice {
title,
label,
choices,
current,
})
}
pub fn show_error(&self, title: &str, message: &str) -> Result<(), Error> {
self.run(Request::Error { title, message }).map(|_| ())
}
fn run(&self, request: Request<'_>) -> Result<Option<String>, Error> {
let Some((backend, program)) = &self.backend else {
return Err(Error::NoBackend);
};
let output = Command::new(program)
.args(argv(*backend, &request))
.stdin(Stdio::null())
.output()
.map_err(|source| Error::Spawn {
program: program.clone(),
source,
})?;
match classify(
output.status.code(),
&String::from_utf8_lossy(&output.stdout),
) {
Outcome::Answered(text) => Ok(text),
Outcome::Cancelled => Ok(None),
Outcome::Failed => Err(Error::Exit {
program: program.clone(),
status: output.status,
}),
}
}
}
impl Default for Dialogs {
fn default() -> Self {
Dialogs::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn explicit_paths_choose_the_backend() {
assert!(!Dialogs::with_paths(None, None).available());
assert!(Dialogs::with_paths(Some(PathBuf::from("/bin/kdialog")), None).available());
assert!(Dialogs::with_paths(None, Some(PathBuf::from("/bin/zenity"))).available());
}
}