mod alias; mod cd; mod exit; mod export; mod source; mod which; use crate::compose::Environment; use std::io; use std::process::{Output, Stdio}; /// A static list of all the built-in commands static INDEX: [&str; 8] = [ "alias", "cd", "exit", "export", "source", "unalias", "unset", "which", ]; /// Lookup the index of a built-in command /// /// Looks up the index of a built-in command in [INDEX], accounting for aliases. /// /// # Aliases /// * quit -> exit /// * set -> export pub fn lookup(verb: &str) -> Option { let verb = match verb { "quit" => "exit", // Alias 'quit' to 'exit' "set" => "export", // Alias 'set' to 'export' _ => verb, }; INDEX.iter().position(|v| v.to_string() == verb) } /// Run a builtin command, based on its verb /// /// Use [lookup] to check if a verb is in the anthology's index (i.e. is a /// builtin), then call this with the current verse, the index found by /// [lookup], and the shell's global environment state. /// /// # Example /// ``` /// let index = anthology::lookup(verse.verb()); /// if index.is_some() { /// anthology::incant(&verse, index.unwrap(), env); /// } else { /// // Run an external command /// ... /// } /// ``` // pub fn incant(verse: &Verse, out: &mut Vec, index: usize, env: &mut Environment) -> i32 { // // let verb = INDEX[index]; // // match verb { // // "alias" => alias::incant(verse, out, &mut env.aliases), // // "cd" => cd::incant(verse), // // "exit" => exit::incant(), // // "export" => export::incant(verse), // // "source" => source::incant(verse, out, env), // // "unalias" => alias::unincant(verse, &mut env.aliases), // // "unset" => export::unincant(verse), // // "which" => which::incant(verse, out, env), // // _ => unreachable!(), // // } // 0 // } #[derive(Debug, PartialEq, Eq, Clone)] pub struct AnthologyStdin { data: Vec, } impl AnthologyStdin { pub fn new() -> Self { AnthologyStdin { data: Vec::new() } } pub fn as_mut(&mut self) -> Option<&mut Self> { Some(self) } pub fn write_all(&mut self, data: &[u8]) -> Result<(), io::Error> { self.data.append(&mut data.to_vec()); Ok(()) } } #[derive(Debug, Clone)] pub struct Anthology { verb: String, clause: Option>, uin: bool, uout: bool, uerr: bool, pub stdin: AnthologyStdin, output: Option, } impl Anthology { /// Create a new instance of a built-in command /// /// Sets up a built-in command with default values. /// /// # Examples /// ``` /// let mut command = Anthology::new("alias"); /// ``` pub fn new(verb: String) -> Self { Anthology { verb, clause: None, uin: false, uout: false, uerr: false, stdin: AnthologyStdin::new(), output: None, } } /// Setup arguments to the built-in command /// /// Sets the 'clause' field of the [Anthology] struct, which are arguments /// to be passed to the built-in command. pub fn args(&mut self, clause: Vec) { self.clause = match clause.is_empty() { true => None, false => Some(clause.clone()), }; } /// Read to STDIN /// /// None of the built-in commands will currently read from STDIN for any /// reason, so this is just a dummy function. pub fn stdin(&mut self, _stdin: Stdio) { self.uin = true; } /// Capture STDOUT pub fn stdout(&mut self, _stdout: Stdio) { self.uout = true; } /// Capture STDERR pub fn stderr(&mut self, _stderr: Stdio) { self.uerr = true; } pub fn process_group(&mut self, _id: usize) {} pub fn id(&mut self) -> i32 { 0 } pub fn spawn(&mut self, env: &mut Environment) -> Result { let index = lookup(self.verb.as_str()).unwrap(); let verb = INDEX[index]; // Incant the built-in and set the output self.output = Some(match verb { "alias" => alias::incant(&self.clause, self.uout, &mut env.aliases), "cd" => cd::incant(&self.clause, self.uerr), "exit" => exit::incant(), "export" => export::incant(&self.clause, self.uout), "source" => source::incant(&self.clause, self.uout, self.uerr, env), "unalias" => alias::unincant(&self.clause, self.uerr, &mut env.aliases), "unset" => export::unincant(&self.clause, self.uerr), "which" => which::incant(&self.clause, self.uout, self.uerr, env), _ => unreachable!(), }); Ok(self.clone()) } pub fn wait_with_output(&self) -> Result { match &self.output { Some(output) => Ok(output.clone()), None => Err(io::Error::new(io::ErrorKind::Other, "not spawned")), } } }