use std::{
ffi::OsStr,
io,
path::Path,
process::{ChildStdin, Command},
};
use super::ShellOptions;
use crate::traits::{ChildShell, ConfigureCommand, SpawnShell, SpawnedShell};
#[derive(Debug, Clone, Copy)]
enum StdShellType {
Sh,
Bash,
PowerShell,
}
#[derive(Debug)]
pub struct StdShell {
shell_type: StdShellType,
command: Command,
}
impl ConfigureCommand for StdShell {
fn current_dir(&mut self, dir: &Path) {
self.command.current_dir(dir);
}
fn env(&mut self, name: &str, value: &OsStr) {
self.command.env(name, value);
}
}
impl ShellOptions<StdShell> {
pub fn sh() -> Self {
Self::new(StdShell {
shell_type: StdShellType::Sh,
command: Command::new("sh"),
})
}
pub fn bash() -> Self {
Self::new(StdShell {
shell_type: StdShellType::Bash,
command: Command::new("bash"),
})
}
#[allow(clippy::doc_markdown)] pub fn powershell() -> Self {
let mut command = Command::new("powershell");
command.arg("-NoLogo").arg("-NoExit");
let command = StdShell {
shell_type: StdShellType::PowerShell,
command,
};
Self::new(command).with_init_command("function prompt { }")
}
#[allow(clippy::doc_markdown)] #[must_use]
pub fn with_alias(self, name: &str, path_to_bin: &str) -> Self {
let alias_command = match self.command.shell_type {
StdShellType::Sh => {
format!("alias {name}=\"'{path}'\"", name = name, path = path_to_bin)
}
StdShellType::Bash => format!(
"{name}() {{ '{path}' \"$@\"; }}",
name = name,
path = path_to_bin
),
StdShellType::PowerShell => format!(
"function {name} {{ & '{path}' @Args }}",
name = name,
path = path_to_bin
),
};
self.with_init_command(alias_command)
}
}
impl SpawnShell for StdShell {
type ShellProcess = ChildShell;
type Reader = os_pipe::PipeReader;
type Writer = ChildStdin;
fn spawn_shell(&mut self) -> io::Result<SpawnedShell<Self>> {
let SpawnedShell {
mut shell,
reader,
writer,
} = self.command.spawn_shell()?;
if matches!(self.shell_type, StdShellType::PowerShell) {
shell.set_echoing();
}
Ok(SpawnedShell {
shell,
reader,
writer,
})
}
}