rush/src/exec.rs

232 lines
5.3 KiB
Rust

//! Execution of commands, builtins and code.
use super::{
Result,
env::Env,
};
/// Execute the code in the given environment.
pub trait Exec {
/// Run the executable.
fn exec <E> (&self, env: &mut E) -> Result<()>
where
E: Env;
}
impl<F> Exec for F
where
F: Fn (&mut dyn Env) -> Result<()>
{
fn exec <E> (&self, env: &mut E) -> Result<()>
where
E: Env
{
self (env)
}
}
/// A shell command.
pub enum Command {
Program (Program),
Builtin (Builtin),
}
impl Exec for Command {
fn exec<E>(&self, env: &mut E) -> Result<()>
where
E: Env
{
match self {
Command::Builtin (builtin) => builtin.exec(env),
Command::Program (program) => program.exec(env),
}
}
}
/// A path to an executable file representing an external program.
pub struct Program (std::ffi::OsString);
impl Program {
/// Create a new [`Program`] struct.
pub (super) fn new (str: impl AsRef<std::ffi::OsStr>) -> Program {
Program (str.as_ref().to_owned())
}
/// Get the command name as an [`OsStr`](std::ffi::OsStr)
pub fn as_os_str (&self) -> &std::ffi::OsStr {
&self.0
}
/// Get the [`Path`](std::path::Path) of the file the name references.
pub fn as_path (&self) -> &std::path::Path {
self.as_ref()
}
}
impl AsRef<std::path::Path> for Program {
fn as_ref (&self) -> &std::path::Path {
std::path::Path::new(&self.0)
}
}
impl Exec for Program {
fn exec <E> (&self, env: &mut E) -> Result<()>
where
E: Env
{
use ptyprocess::PtyProcess;
use std::io::{ BufRead, Read };
let mut cmd = std::process::Command::new(self.as_os_str());
cmd.current_dir(env.working_dir()?);
cmd.args(env.args());
let proc = PtyProcess::spawn(cmd)
.unwrap();
let mut pty = proc
.get_pty_handle()
.unwrap();
while let Ok (true) = proc.is_alive() {
let mut buf = String::new();
pty.read_to_string(&mut buf);
env.stdout(&buf);
}
Ok (())
}
}
/// Represents the case that a string fails to parse to a [`Builtin`]
/// because the associated builtin does not exist. Valid builtin names
/// are listed in the [`builtin`] module.
#[derive(Debug)]
pub struct NotFound;
/// A shell builtin. See also the [`builtin`] module.
///
/// # Examples
///
/// The only way to get a `Builtin` is through [`FromStr`].
///
/// ```
/// # use rush::exec::Builtin;
/// "type".parse::<Builtin>().unwrap();
/// ```
///
/// Parsing an unknown builtin name yields an error.
///
/// ```should_panic
/// # use rush::exec::Builtin;
/// "sjdkgfhgfkfg"
/// .parse::<Builtin>()
/// .unwrap();
/// ```
///
/// [`FromStr`]: std::str::FromStr
pub struct Builtin (fn (&mut dyn Env) -> Result<()>);
impl std::str::FromStr for Builtin {
type Err = NotFound;
fn from_str (s: &str) -> Result<Self, Self::Err> {
let ret = Builtin (match s {
"disown" => builtin::disown,
"type" => builtin::r#type,
"exit" => builtin::exit,
"pwd" => builtin::pwd,
"fg" => builtin::fg,
"bg" => builtin::bg,
"cd" => builtin::cd,
_ => return Err (NotFound)
});
Ok (ret)
}
}
impl Exec for Builtin {
fn exec <E> (&self, env: &mut E) -> Result<()>
where
E: Env
{
self.0.exec(env)
}
}
pub mod builtin {
//! Shell builtins.
use super::{ Env, Result, };
/// Change the current working directory.
pub fn cd (env: &mut dyn Env) -> Result<()> {
let arg = env.args().next().unwrap();
env.set_working_dir(arg.as_ref())
}
/// Print the current working directory.
pub fn pwd (env: &mut dyn Env) -> Result<()> {
let cwd = env.working_dir()?;
env.stdout(format!("{}", cwd.display()).as_str());
Ok (())
}
/// Stop the program. Optionally takes an exit code.
pub fn exit (env: &mut dyn Env) -> Result<()> {
if let Some (code) = env.args().next().and_then(|s| s.parse::<i32>().ok()) {
std::process::exit(code)
} else {
std::process::exit(0)
}
}
/// Display information about the type of a command.
pub fn r#type (env: &mut dyn Env) -> Result<()> {
use crate::exec::Builtin;
for arg in env.args() {
let output = if let Ok (_) = arg.parse::<Builtin>() {
format!("{} is a shell builtin", arg)
} else if let Some (path) = env.search(&arg) {
format!("{} is {}", arg, path.as_path().display())
} else {
format!("type: {}: not found", arg)
};
env.stdout((output + "\n").as_str())?;
}
Ok (())
}
/// Send a job to the background. See also [`Job::bg`].
///
/// [`Job::bg`]: crate::job::Job::bg
pub fn bg (_: &mut dyn Env) -> Result<()> {
todo!()
}
/// Bring a job to the foreground. See also [`Job::fg`].
///
/// [`Job::fg`]: crate::job::Job::fg
pub fn fg (_: &mut dyn Env) -> Result<()> {
todo!()
}
/// Disown all background jobs.
pub fn disown (_: &mut dyn Env) -> Result<()> {
todo!()
}
}