1use crate::{Command, ErrorKind, Result, ResultExt as _};
2use std::{ffi, fmt};
3
4#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub struct Program(String);
7
8impl Program {
9 pub(super) fn new_unchecked(program: String) -> Self {
12 Program(program)
13 }
14
15 pub fn new<P: AsRef<str>>(program: P) -> Result<Self> {
43 let program = Program::new_unchecked(program.as_ref().to_owned());
44 let mut cmd = Command::new(program.clone());
45 cmd.arguments(&["--help"]);
46 let child = cmd
47 .spawn()
48 .chain_err(|| ErrorKind::InvalidProgramName(program.clone()))?;
49
50 std::mem::drop(child);
54
55 Ok(program)
56 }
57}
58
59impl AsRef<str> for Program {
60 fn as_ref(&self) -> &str {
61 self.0.as_str()
62 }
63}
64
65impl AsRef<ffi::OsStr> for Program {
66 fn as_ref(&self) -> &ffi::OsStr {
67 self.0.as_ref()
68 }
69}
70
71impl fmt::Display for Program {
72 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73 self.0.fmt(f)
74 }
75}
76
77#[cfg(test)]
78mod test {
79 use super::*;
80
81 #[cfg(unix)]
82 #[test]
83 fn program_exists() {
84 use crate::error_chain::ChainedError as _;
85
86 const PROGRAM_NAME: &str = "sh";
87
88 if let Err(error) = Program::new(PROGRAM_NAME.to_owned()) {
89 eprintln!("{}", error.display_chain().to_string());
90 panic!("The program does not seem to exist, we are expected it to");
91 }
92 }
93
94 #[test]
95 fn program_does_not_exists() {
96 use crate::error_chain::ChainedError as _;
97
98 const PROGRAM_NAME: &str = "the-impossible-program-that-does-not-exist";
99
100 let error = Program::new(PROGRAM_NAME.to_owned()).expect_err("program should not exist");
101
102 match error.kind() {
103 ErrorKind::InvalidProgramName(program) => assert_eq!(program.0.as_str(), PROGRAM_NAME),
104 _ => panic!("unexpected error: {}", error.display_chain().to_string()),
105 }
106 }
107}