diff --git a/crates/nu-cli/src/print.rs b/crates/nu-cli/src/print.rs index ffb0366242..e8326fb1fe 100644 --- a/crates/nu-cli/src/print.rs +++ b/crates/nu-cli/src/print.rs @@ -1,5 +1,6 @@ use nu_engine::command_prelude::*; use nu_protocol::ByteStreamSource; +use nu_protocol::{DataSource, PipelineMetadata}; #[derive(Clone)] pub struct Print; @@ -57,15 +58,22 @@ Since this command has no output, there is no point in piping it with other comm let no_newline = call.has_flag(engine_state, stack, "no-newline")?; let to_stderr = call.has_flag(engine_state, stack, "stderr")?; let raw = call.has_flag(engine_state, stack, "raw")?; + let metadata = match input.metadata() { + Some(md) => md, + None => PipelineMetadata { + data_source: DataSource::Ls, + content_type: None, + }, + }; // This will allow for easy printing of pipelines as well if !args.is_empty() { for arg in args { if raw { - arg.into_pipeline_data() + arg.into_pipeline_data_with_metadata(metadata.clone()) .print_raw(engine_state, no_newline, to_stderr)?; } else { - arg.into_pipeline_data() + arg.into_pipeline_data_with_metadata(metadata.clone()) .print(engine_state, stack, no_newline, to_stderr)?; } } diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 43fb8394c5..8a17ac7f7e 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -116,6 +116,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { bind_command! { Complete, External, + Internal, Exec, NuCheck, Sys, diff --git a/crates/nu-command/src/system/mod.rs b/crates/nu-command/src/system/mod.rs index a87aac8a07..852d5a30b6 100644 --- a/crates/nu-command/src/system/mod.rs +++ b/crates/nu-command/src/system/mod.rs @@ -14,6 +14,7 @@ mod ps; #[cfg(windows)] mod registry_query; mod run_external; +mod run_internal; mod sys; mod uname; mod which_; @@ -34,6 +35,7 @@ pub use ps::Ps; #[cfg(windows)] pub use registry_query::RegistryQuery; pub use run_external::{command_not_found, eval_arguments_from_call, which, External}; +pub use run_internal::Internal; pub use sys::*; pub use uname::UName; pub use which_::Which; diff --git a/crates/nu-command/src/system/run_internal.rs b/crates/nu-command/src/system/run_internal.rs new file mode 100644 index 0000000000..61e377ec9a --- /dev/null +++ b/crates/nu-command/src/system/run_internal.rs @@ -0,0 +1,179 @@ +use log::info; +use nu_engine::command_prelude::*; +use nu_engine::{convert_env_values, eval_block}; +use nu_parser::parse; +use nu_protocol::{ + cli_error::report_compile_error, + debugger::WithoutDebug, + engine::{EngineState, Stack, StateWorkingSet}, + report_parse_error, report_parse_warning, IntoValue, PipelineData, ShellError, Spanned, Value, +}; +use std::sync::Arc; + +#[derive(Clone)] +pub struct Internal; + +impl Command for Internal { + fn name(&self) -> &str { + "run-internal" + } + + fn description(&self) -> &str { + "Runs internal command." + } + + fn signature(&self) -> nu_protocol::Signature { + Signature::build(self.name()) + .input_output_types(vec![(Type::Any, Type::Any)]) + .required("command", SyntaxShape::String, "Internal command to run.") + .category(Category::System) + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Run an internal command", + example: r#"run-internal "ls""#, + result: None, + }, + Example { + description: "Run a pipeline", + example: r#"run-internal "print (ls | first 5);print (ps | first 5)"#, + result: None, + }, + ] + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let config = engine_state.get_config(); + let table_mode = config.table.mode.into_value(call.head); + let error_style = config.error_style.into_value(call.head); + let no_newline = false; + let commands: Spanned = call.req(engine_state, stack, 0)?; + let _ = evaluate_commands( + &commands, + &mut engine_state.clone(), + &mut stack.clone(), + input, + EvaluateCommandsOpts { + table_mode: Some(table_mode), + error_style: Some(error_style), + no_newline, + }, + ); + Ok(PipelineData::Empty) + } +} + +// This code is ripped off from nu-cli. It's duplicated here because I didn't +// want to add a dependency on nu-cli in nu-command. +#[derive(Default)] +pub struct EvaluateCommandsOpts { + pub table_mode: Option, + pub error_style: Option, + pub no_newline: bool, +} + +/// Run a command (or commands) given to us by the user +pub fn evaluate_commands( + commands: &Spanned, + engine_state: &mut EngineState, + stack: &mut Stack, + input: PipelineData, + opts: EvaluateCommandsOpts, +) -> Result<(), ShellError> { + let EvaluateCommandsOpts { + table_mode, + error_style, + no_newline, + } = opts; + + // Handle the configured error style early + if let Some(e_style) = error_style { + match e_style.coerce_str()?.parse() { + Ok(e_style) => { + Arc::make_mut(&mut engine_state.config).error_style = e_style; + } + Err(err) => { + return Err(ShellError::GenericError { + error: "Invalid value for `--error-style`".into(), + msg: err.into(), + span: Some(e_style.span()), + help: None, + inner: vec![], + }); + } + } + } + + // Translate environment variables from Strings to Values + convert_env_values(engine_state, stack)?; + + // Parse the source code + let (block, delta) = { + if let Some(ref t_mode) = table_mode { + Arc::make_mut(&mut engine_state.config).table.mode = + t_mode.coerce_str()?.parse().unwrap_or_default(); + } + + let mut working_set = StateWorkingSet::new(engine_state); + + let output = parse(&mut working_set, None, commands.item.as_bytes(), false); + if let Some(warning) = working_set.parse_warnings.first() { + report_parse_warning(&working_set, warning); + } + + if let Some(err) = working_set.parse_errors.first() { + report_parse_error(&working_set, err); + std::process::exit(1); + } + + if let Some(err) = working_set.compile_errors.first() { + report_compile_error(&working_set, err); + // Not a fatal error, for now + } + + (output, working_set.render()) + }; + + // Update permanent state + engine_state.merge_delta(delta)?; + + // Run the block + let pipeline = eval_block::(engine_state, stack, &block, input)?; + + if let PipelineData::Value(Value::Error { error, .. }, ..) = pipeline { + return Err(*error); + } + + if let Some(t_mode) = table_mode { + Arc::make_mut(&mut engine_state.config).table.mode = + t_mode.coerce_str()?.parse().unwrap_or_default(); + } + + pipeline.print(engine_state, stack, no_newline, false)?; + + info!("evaluate {}:{}:{}", file!(), line!(), column!()); + + Ok(()) +} + +#[cfg(test)] +mod test { + // use super::*; + // use nu_test_support::{fs::Stub, playground::Playground}; + + // #[test] + // fn test_some_test() { + // } + + // #[test] + // fn test_some_other_test() { + // } +}