diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 540c4b56fdee27568ccb525b76110ef0a535859f..07de02f14c48042d386991c8bc9796b8ea291d53 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -304,7 +304,7 @@ impl<'a> InteractiveShell<'a> { self.terminated.set(true); { let mut shell = self.shell.borrow_mut(); - match shell.on_command(&cmd) { + match shell.on_command(&cmd, true) { Ok(_) => (), Err(IonError::PipelineExecutionError( PipelineError::CommandNotFound(command), diff --git a/src/binary/prompt.rs b/src/binary/prompt.rs index 31e3bf2b7eae96cdb7a94773a4731c6e4e13de09..e00df1f5c0bc45f7f6343e5c289d6b92c28a02cf 100644 --- a/src/binary/prompt.rs +++ b/src/binary/prompt.rs @@ -13,30 +13,31 @@ impl<'a> InteractiveShell<'a> { let blocks = if self.terminated.get() { shell.block_len() } else { shell.block_len() + 1 }; if blocks == 0 { - let out = shell.command("PROMPT").map(|res| res.to_string()).unwrap_or_else(|err| { - if let expansion::Error::Subprocess(err) = err { - if let IonError::PipelineExecutionError(PipelineError::CommandNotFound(_)) = - *err - { - match shell - .variables() - .get_str("PROMPT") - .and_then(|prompt| shell.get_string(&prompt)) + let out = + shell.command("PROMPT", false).map(|res| res.to_string()).unwrap_or_else(|err| { + if let expansion::Error::Subprocess(err) = err { + if let IonError::PipelineExecutionError(PipelineError::CommandNotFound(_)) = + *err { - Ok(prompt) => prompt.to_string(), - Err(err) => { - eprintln!("ion: prompt expansion failed: {}", err); - ">>> ".into() + match shell + .variables() + .get_str("PROMPT") + .and_then(|prompt| shell.get_string(&prompt)) + { + Ok(prompt) => prompt.to_string(), + Err(err) => { + eprintln!("ion: prompt expansion failed: {}", err); + ">>> ".into() + } } + } else { + eprintln!("ion: prompt expansion failed: {}", err); + ">>> ".into() } } else { - eprintln!("ion: prompt expansion failed: {}", err); - ">>> ".into() + panic!("Only a subprocess error should happen inside the pipeline"); } - } else { - panic!("Only a subprocess error should happen inside the pipeline"); - } - }); + }); shell.set_previous_status(previous_status); // Set the previous exit code again let key_bindings = self.context.borrow().key_bindings; match key_bindings { diff --git a/src/lib/expansion/mod.rs b/src/lib/expansion/mod.rs index 0d079570598809f00a60ad256ce9f4ff0bc048b3..9819776d2e93fce260937c8b40e338e6a7cb8389 100644 --- a/src/lib/expansion/mod.rs +++ b/src/lib/expansion/mod.rs @@ -127,7 +127,11 @@ pub trait Expander: Sized { /// Expand a string variable given if it's quoted / unquoted fn string(&self, _name: &str) -> Result<types::Str, Self::Error>; /// Expand a subshell expression. - fn command(&mut self, _command: &str) -> Result<types::Str, Self::Error>; + fn command( + &mut self, + _command: &str, + _set_cmd_duration: bool, + ) -> Result<types::Str, Self::Error>; /// Iterating upon key-value maps. fn map_keys(&self, _name: &str) -> Result<Args, Self::Error>; /// Iterating upon key-value maps. @@ -171,7 +175,7 @@ trait ExpanderInternal: Expander { command: &str, selection: &Option<&'a str>, ) -> Result<(), Self::Error> { - let result = self.command(command)?; + let result = self.command(command, true)?; self.slice(current, result.trim_end_matches('\n'), selection) } @@ -703,7 +707,13 @@ pub(crate) mod test { } } - fn command(&mut self, cmd: &str) -> Result<types::Str, Self::Error> { Ok(cmd.into()) } + fn command( + &mut self, + cmd: &str, + _set_cmd_duration: bool, + ) -> Result<types::Str, Self::Error> { + Ok(cmd.into()) + } fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) } diff --git a/src/lib/shell/flow.rs b/src/lib/shell/flow.rs index e142f8490ec9617784b5787e6c2830a496faf694..1ba50333fdedd5ae4b0c1707e76b7dbec10dc85c 100644 --- a/src/lib/shell/flow.rs +++ b/src/lib/shell/flow.rs @@ -17,7 +17,7 @@ use crate::{ use err_derive::Error; use itertools::Itertools; use nix::unistd::Pid; -use std::rc::Rc; +use std::{rc::Rc, time::SystemTime}; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub enum Condition { @@ -531,7 +531,7 @@ impl<'a> Shell<'a> { }); if let Some(statement) = case.conditional.as_ref() { - self.on_command(statement)?; + self.on_command(statement, true)?; if self.previous_status.is_failure() { continue; } @@ -558,7 +558,13 @@ impl<'a> Shell<'a> { } /// Receives a command and attempts to execute the contents. - pub fn on_command(&mut self, command_string: &str) -> std::result::Result<(), IonError> { + pub fn on_command( + &mut self, + command_string: &str, + set_cmd_duration: bool, + ) -> std::result::Result<(), IonError> { + let command_start_time = if set_cmd_duration { Some(SystemTime::now()) } else { None }; + for stmt in command_string.bytes().batching(|cmd| Terminator::new(cmd).terminate()) { // Go through all of the statements and build up the block stack // When block is done return statement for execution. @@ -569,6 +575,13 @@ impl<'a> Shell<'a> { } } } + + if let Some(start_time) = command_start_time { + if let Ok(elapsed_time) = start_time.elapsed() { + self.variables_mut().set("CMD_DURATION", elapsed_time.as_secs().to_string()); + } + } + Ok(()) } } diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs index 6679d51a225354ba1104e43ba5d03628db9c1725..4ec001379bf816e4cdef05d0cce1db33fb1201b0 100644 --- a/src/lib/shell/mod.rs +++ b/src/lib/shell/mod.rs @@ -271,7 +271,7 @@ impl<'a> Shell<'a> { .filter_map(Result::ok) .batching(|bytes| Terminator::new(bytes).terminate()) { - self.on_command(&cmd)?; + self.on_command(&cmd, true)?; } if let Some(block) = self.flow_control.last().map(Statement::to_string) { diff --git a/src/lib/shell/shell_expand.rs b/src/lib/shell/shell_expand.rs index 0562fef78d296a666c4d00554065677d7f1b06ee..283ba715152466e56dcb81ff934d015ab43fe313 100644 --- a/src/lib/shell/shell_expand.rs +++ b/src/lib/shell/shell_expand.rs @@ -16,7 +16,11 @@ impl<'a, 'b> Expander for Shell<'b> { type Error = IonError; /// Uses a subshell to expand a given command. - fn command(&mut self, command: &str) -> Result<types::Str, Self::Error> { + fn command( + &mut self, + command: &str, + set_cmd_duration: bool, + ) -> Result<types::Str, Self::Error> { let (mut reader, writer) = create_pipe() .map_err(|err| Error::Subprocess(Box::new(IonError::PipelineExecutionError(err))))?; let null_file = File::open(NULL_PATH).map_err(|err| { @@ -30,7 +34,9 @@ impl<'a, 'b> Expander for Shell<'b> { let prev_stderr = self.stderr(null_file); // Execute the command - let result = self.on_command(command).map_err(|err| Error::Subprocess(Box::new(err))); + let result = self + .on_command(command, set_cmd_duration) + .map_err(|err| Error::Subprocess(Box::new(err))); // Reset the pipes, droping the stdout self.stdout(prev_stdout); diff --git a/src/lib/shell/variables.rs b/src/lib/shell/variables.rs index cf363aa61f4900c8d66afcd692d2e0bea1c4a73e..efcef5ab7262b094249f6a30e51ee197e344edcf 100644 --- a/src/lib/shell/variables.rs +++ b/src/lib/shell/variables.rs @@ -286,7 +286,13 @@ pub(crate) mod tests { Err(expansion::Error::VarNotFound) } - fn command(&mut self, cmd: &str) -> Result<types::Str, Self::Error> { Ok(cmd.into()) } + fn command( + &mut self, + cmd: &str, + _set_cmd_duration: bool, + ) -> Result<types::Str, Self::Error> { + Ok(cmd.into()) + } fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) }