use std::io::{Error, ErrorKind}; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; #[derive(Debug)] pub enum Redirect { Std, FileOverwrite(String), FileAppend(String), } #[derive(Debug, PartialEq)] pub enum RunOn { Always, ExitSuccess, ExitFailure, } pub enum RunResult { Command(ExitStatus), Builtin, } #[derive(Debug)] pub struct CommandInfo { pub args: Vec, pub stdout: Redirect, pub when: RunOn, } impl CommandInfo { pub fn new() -> CommandInfo { CommandInfo { args: Vec::new(), stdout: Redirect::Std, when: RunOn::Always, } } pub fn run( &self, home: &PathBuf, cwd: &mut PathBuf, status: &Option, ) -> Result { match self.args[0].as_str() { "!" => { println!("{:?}", status); Ok(RunResult::Builtin) } "cd" => cd(&self.args[1..], home, cwd), "exit" => { std::process::exit(0); } "set" => set(&self.args[1..]), "unset" => unset(&self.args[1..]), _ => { let mut child = Command::new(&self.args[0]).args(&self.args[1..]).spawn()?; Ok(RunResult::Command(child.wait().unwrap())) } } } } fn cd(args: &[String], home: &PathBuf, cwd: &mut PathBuf) -> Result { if args.len() > 1 { return Err(Error::new( ErrorKind::InvalidInput, "Too many arguments passed to cd", )); } let root = if args.len() == 0 { home.to_path_buf() } else { std::fs::canonicalize(Path::new(args[0].as_str()))? }; match std::env::set_current_dir(&root) { Ok(_) => { *cwd = root; Ok(RunResult::Builtin) } Err(e) => Err(e), } } fn set(args: &[String]) -> Result { if args.len() != 2 { return Err(Error::new( ErrorKind::InvalidInput, format!("set requires 2 arguments, got {}", args.len()), )); } std::env::set_var(&args[0], &args[1]); Ok(RunResult::Builtin) } fn unset(args: &[String]) -> Result { if args.len() != 1 { return Err(Error::new( ErrorKind::InvalidInput, format!("unset requires 1 argument, got {}", args.len()), )); } std::env::remove_var(&args[0]); Ok(RunResult::Builtin) }