232 lines
5.3 KiB
Rust
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!()
|
|
}
|
|
|
|
}
|
|
|
|
|