use std::collections::HashSet;
use std::fs;
use std::io;
use std::io::ErrorKind;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::process::ExitStatus;
use thiserror::Error;
use crate::config::Config;
use crate::path;
#[derive(Debug, Error)]
pub enum RunError {
#[error("I/O operation failed: {0}")]
Io(#[from] io::Error),
#[error("Script {0:?} could not be found.")]
ScriptNotFound(String),
}
pub fn run(name: &str, config: &Config, args: &[&str]) -> Result<ExitStatus, RunError> {
let script =
find_script(name, config)?.ok_or_else(|| RunError::ScriptNotFound(name.to_string()))?;
let mut child = Command::new(script).args(args).spawn()?;
Ok(child.wait()?)
}
#[inline]
pub fn script_exists(name: &str, config: &Config) -> bool {
find_script(name, config).ok().flatten().is_some()
}
pub fn find_script(name: &str, config: &Config) -> Result<Option<PathBuf>, io::Error> {
let dirs = path::directories_iter(config)?;
for dir in dirs {
match search_dir(name, &dir) {
Ok(file) => {
if file.is_some() {
return Ok(file);
}
}
Err(e) => {
if let ErrorKind::NotFound = e.kind() {
continue;
} else {
return Err(e);
}
}
}
}
Ok(None)
}
pub fn all_scripts(config: &Config) -> Result<Vec<String>, io::Error> {
let mut set = HashSet::new();
let dirs = path::directories_iter(config)?;
for dir in dirs {
let read_dir = match fs::read_dir(dir) {
Ok(rd) => rd,
Err(e) => {
if let ErrorKind::NotFound = e.kind() {
continue;
} else {
return Err(e);
}
}
};
for entry in read_dir {
let entry = entry?;
if entry.file_type()?.is_file() {
let file_name = match entry.path().file_stem() {
Some(stem) => stem.to_string_lossy().into_owned(),
None => continue,
};
if file_name.starts_with('.') {
continue;
}
set.insert(file_name);
}
}
}
let mut scripts = set.drain().collect::<Vec<_>>();
scripts.sort();
Ok(scripts)
}
fn search_dir(name: &str, dir: &Path) -> Result<Option<PathBuf>, io::Error> {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let file_name = match entry.path().file_stem() {
Some(stem) => stem.to_string_lossy().into_owned(),
None => continue,
};
if file_name == name && entry.file_type()?.is_file() && !file_name.starts_with('.') {
return Ok(Some(entry.path()));
}
}
Ok(None)
}