From c4cd1e2c165c4f34ebf67fa9350f8732b2aeca13 Mon Sep 17 00:00:00 2001 From: Rory Dudley Date: Tue, 4 Jun 2024 16:25:32 -0600 Subject: Updated the way built-in commands are called/used Previously, built-in commands were fairly primitive, merely outputting STDOUT and STDERR with the print! macros. However, we need them to behave like normal programs, that is: - Acknowledge their verse's meter (forking, piping, etc.), - Ability to capture STDOUT and STDERR (>, 2>), - and Affect the currently running environment. For these reasons, the anthology was reworked, and now contains the Anthology struct, which mimics both std::process::{Child, Command}. The AnthologyStdin helper struct was also created, for built-ins to take input on STDIN, though no built-in is currently using it. Each built-ins' incant functions were updated to return a std::process::Output. It contains output from STDOUT, output from STDERR, and the exit code of the "process". A fix was also implemented for aliases, where the STDOUT and STDERR vectors were not being copied to the newly constructed verse. --- src/poem/anthology.rs | 142 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 129 insertions(+), 13 deletions(-) (limited to 'src/poem/anthology.rs') diff --git a/src/poem/anthology.rs b/src/poem/anthology.rs index 8ff70f9..a35eafb 100644 --- a/src/poem/anthology.rs +++ b/src/poem/anthology.rs @@ -5,7 +5,8 @@ mod export; mod source; mod which; use crate::compose::Environment; -use crate::poem::Verse; +use std::io; +use std::process::{Output, Stdio}; /// A static list of all the built-in commands static INDEX: [&str; 8] = [ @@ -44,17 +45,132 @@ pub fn lookup(verb: &str) -> Option { /// ... /// } /// ``` -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!(), +// 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")), + } } } -- cgit v1.2.3