//! 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 (&self, env: &mut E) -> Result<()> where E: Env; } /// A shell command. pub enum Command { Program (Program), Builtin (Builtin), } impl Exec for Command { fn exec(&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) -> 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 for Program { fn as_ref (&self) -> &std::path::Path { std::path::Path::new(&self.0) } } impl Exec for Program { fn exec (&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::().unwrap(); /// ``` /// /// Parsing an unknown builtin name yields an error. /// /// ```should_panic /// # use rush::exec::Builtin; /// "sjdkgfhgfkfg" /// .parse::() /// .unwrap(); /// ``` /// /// [`FromStr`]: std::str::FromStr pub struct Builtin (BuiltinName); enum BuiltinName { Disown, Type, Exit, Pwd, Fg, Bg, Cd, } impl std::str::FromStr for Builtin { type Err = NotFound; fn from_str (s: &str) -> Result { let ret = Builtin (match s { "disown" => BuiltinName::Disown, "type" => BuiltinName::Type, "exit" => BuiltinName::Exit, "pwd" => BuiltinName::Pwd, "fg" => BuiltinName::Fg, "bg" => BuiltinName::Bg, "cd" => BuiltinName::Cd, _ => return Err (NotFound) }); Ok (ret) } } impl Exec for Builtin { fn exec (&self, env: &mut E) -> Result<()> where E: Env { use BuiltinName::*; let f = match self.0 { Disown => builtin::disown, Type => builtin::r#type, Exit => builtin::exit, Pwd => builtin::pwd, Fg => builtin::fg, Bg => builtin::bg, Cd => builtin::cd, }; f (env) } } pub mod builtin { //! Shell builtins. use super::{ Env, Result, }; /// Change the current working directory. pub fn cd (env: &mut impl Env) -> Result<()> { let arg = env.args().next().unwrap(); env.set_working_dir(arg) } /// Print the current working directory. pub fn pwd (env: &mut impl 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 impl Env) -> Result<()> { if let Some (code) = env.args().next().and_then(|s| s.parse::().ok()) { std::process::exit(code) } else { std::process::exit(0) } } /// Display information about the type of a command. pub fn r#type (env: &mut impl Env) -> Result<()> { use crate::exec::Builtin; for arg in env.args() { let output = if let Ok (_) = arg.parse::() { 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 impl Env) -> Result<()> { todo!() } /// Bring a job to the foreground. See also [`Job::fg`]. /// /// [`Job::fg`]: crate::job::Job::fg pub fn fg (_: &mut impl Env) -> Result<()> { todo!() } /// Disown all background jobs. pub fn disown (_: &mut impl Env) -> Result<()> { todo!() } }