use crate::error::{Error, ErrorKind::PathError};
use std::{
borrow::Cow,
env,
ffi::OsStr,
fmt::{self, Display, Formatter},
ops::Deref,
path::{Path, PathBuf},
};
#[derive(Debug)]
pub struct Binary {
path: PathBuf,
}
impl Binary {
pub fn new<P>(path: P) -> Result<Self, Error>
where
P: AsRef<Path>,
{
let this = Self { path: path.as_ref().to_path_buf() };
if !this.path.is_file() {
return Err(error!(PathError, "binary does not exist: {}", this));
}
Ok(this)
}
pub fn find<B>(binary: B) -> Result<Self, Error>
where
B: AsRef<str>,
{
let binary = binary.as_ref();
let binary = Self::binary_name(binary);
let paths = env::var("PATH").map_err(|e| error!(PathError, with: e, "Failed to access PATH variable"))?;
let path = env::split_paths(&paths)
.map(|p| p.join(binary.as_ref()))
.find(|p| p.exists())
.ok_or(error!(PathError, "failed to find binary in PATH: {}", binary))?;
Ok(Self { path })
}
fn binary_name(binary: &str) -> Cow<'_, str> {
#[cfg(target_family = "windows")]
match binary.ends_with(".exe") {
true => return Cow::Borrowed(binary),
false => return Cow::Owned(format!("{}.exe", binary)),
}
#[cfg(not(target_family = "windows"))]
Cow::Borrowed(binary)
}
}
impl Display for Binary {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.path.to_string_lossy())
}
}
impl Deref for Binary {
type Target = OsStr;
fn deref(&self) -> &Self::Target {
self.path.as_os_str()
}
}
impl AsRef<OsStr> for Binary {
fn as_ref(&self) -> &OsStr {
self
}
}
impl AsRef<Path> for Binary {
fn as_ref(&self) -> &Path {
&self.path
}
}
#[derive(Debug)]
pub struct Shell {
shell: Binary,
}
impl Shell {
pub fn find() -> Result<Self, Error> {
let shell = match env::var("SHELL") {
Ok(shell) => Binary::new(shell)?,
Err(_) => Binary::find(Self::shell_name())?,
};
Ok(Self { shell })
}
pub fn execstring_args(&self) -> Result<&'static [&'static str], Error> {
let shell_path: &Path = self.shell.as_ref();
let shell_name = shell_path
.file_name()
.and_then(|n| n.to_str())
.ok_or(error!(PathError, "failed to get the file name for the selected shell: {}", self.shell))?;
match shell_name {
"powershell.exe" => Ok(&["-executionpolicy", "bypass", "&"]),
"bash" | "zsh" | "sh" => Ok(&["-c"]),
_ => Err(error!(PathError, "cannot get execstring arguments for unknown shell: {}", shell_name)),
}
}
fn shell_name() -> &'static str {
#[cfg(target_family = "windows")]
return "powershell.exe";
#[cfg(not(target_family = "windows"))]
return "sh";
}
}
impl Display for Shell {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.shell)
}
}
impl Deref for Shell {
type Target = OsStr;
fn deref(&self) -> &Self::Target {
&self.shell
}
}
impl AsRef<OsStr> for Shell {
fn as_ref(&self) -> &OsStr {
&self.shell
}
}
impl AsRef<Path> for Shell {
fn as_ref(&self) -> &Path {
self.shell.as_ref()
}
}