1use std::{
2 path::{Path, PathBuf},
3 process::{Command, ExitStatus, Stdio},
4};
5
6pub const EXECUTOR: &str = "bash";
8
9#[derive(Debug, thiserror::Error)]
11pub enum RunScriptError {
12 #[error("Given script '{0:?}' is not executable")]
14 NotExecutable(PathBuf),
15 #[error("Given path '{0:?}' is not a file")]
17 NotAFile(PathBuf),
18 #[error("Given path '{0:?}' was not found")]
20 NotFound(PathBuf),
21 #[error("Error reading file '{0:?}': {1}")]
23 ReadFailure(PathBuf, std::io::Error),
24 #[error("Error running the script '{0:?}': {1}")]
26 RunFailure(PathBuf, std::io::Error),
27 #[error("Error parsing script '{0:?}' output")]
29 OutputParse(PathBuf),
30}
31
32pub fn run_script(path: &Path, arguments: &[String]) -> Result<ExitStatus, RunScriptError> {
46 if !path.exists() {
47 return Err(RunScriptError::NotFound(path.to_path_buf()));
48 }
49 if !path.is_file() {
50 return Err(RunScriptError::NotAFile(path.to_path_buf()));
51 }
52 let md = path
53 .metadata()
54 .map_err(|err| RunScriptError::ReadFailure(path.to_path_buf(), err))?;
55 if !super::read::is_path_executable(md.permissions()) {
56 return Err(RunScriptError::NotExecutable(path.to_path_buf()));
57 }
58
59 let status = Command::new(EXECUTOR)
60 .arg(path)
61 .args(arguments)
62 .stdin(Stdio::inherit())
63 .stdout(Stdio::inherit())
64 .stderr(Stdio::inherit())
65 .spawn()
66 .map_err(|err| RunScriptError::RunFailure(path.to_path_buf(), err))?
67 .wait()
68 .map_err(|err| RunScriptError::RunFailure(path.to_path_buf(), err))?;
69
70 Ok(status)
71}