use crate::{Command, ErrorKind, Result, ResultExt as _};
use std::{ffi, fmt};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Program(String);
impl Program {
pub(super) fn new_unchecked(program: String) -> Self {
Program(program)
}
pub fn new<P: AsRef<str>>(program: P) -> Result<Self> {
let program = Program::new_unchecked(program.as_ref().to_owned());
let mut cmd = Command::new(program.clone());
cmd.arguments(&["--help"]);
let child = cmd
.spawn()
.chain_err(|| ErrorKind::InvalidProgramName(program.clone()))?;
std::mem::drop(child);
Ok(program)
}
}
impl AsRef<str> for Program {
fn as_ref(&self) -> &str {
self.0.as_str()
}
}
impl AsRef<ffi::OsStr> for Program {
fn as_ref(&self) -> &ffi::OsStr {
self.0.as_ref()
}
}
impl fmt::Display for Program {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(test)]
mod test {
use super::*;
#[cfg(unix)]
#[test]
fn program_exists() {
use crate::error_chain::ChainedError as _;
const PROGRAM_NAME: &str = "sh";
if let Err(error) = Program::new(PROGRAM_NAME.to_owned()) {
eprintln!("{}", error.display_chain().to_string());
panic!("The program does not seem to exist, we are expected it to");
}
}
#[test]
fn program_does_not_exists() {
use crate::error_chain::ChainedError as _;
const PROGRAM_NAME: &str = "the-impossible-program-that-does-not-exist";
let error = Program::new(PROGRAM_NAME.to_owned()).expect_err("program should not exist");
match error.kind() {
ErrorKind::InvalidProgramName(program) => assert_eq!(program.0.as_str(), PROGRAM_NAME),
_ => panic!("unexpected error: {}", error.display_chain().to_string()),
}
}
}