use crate::call_info::UnevaluatedCallInfo; use crate::command_args::RawCommandArgs; use crate::evaluation_context::EvaluationContext; use crate::filesystem::filesystem_shell::{FilesystemShell, FilesystemShellMode}; use crate::shell::value_shell::ValueShell; use log::{log_enabled, trace}; use nu_errors::ShellError; use nu_protocol::hir::{ Expression, ExternalRedirection, InternalCommand, SpannedExpression, Synthetic, }; use nu_protocol::{CommandAction, ReturnSuccess, UntaggedValue, Value}; use nu_source::{PrettyDebug, Span, Tag}; use nu_stream::{ActionStream, InputStream}; pub(crate) fn run_internal_command( command: InternalCommand, context: &EvaluationContext, input: InputStream, ) -> Result { if log_enabled!(log::Level::Trace) { trace!(target: "nu::run::internal", "->"); trace!(target: "nu::run::internal", "{}", command.name); } let objects: InputStream = input; let internal_command = context.scope.expect_command(&command.name); let internal_command = internal_command?; let result = context.run_command( internal_command, Tag::unknown_anchor(command.name_span), command.args, objects, )?; Ok(InputStream::from_stream(InternalIteratorSimple { context: context.clone(), input: result, })) } struct InternalIteratorSimple { context: EvaluationContext, input: InputStream, } impl Iterator for InternalIteratorSimple { type Item = Value; fn next(&mut self) -> Option { match self.input.next() { Some(Value { value: UntaggedValue::Error(err), .. }) => { self.context.error(err); None } x => x, } } } pub struct InternalIterator { pub context: EvaluationContext, pub leftovers: Vec, pub input: ActionStream, } impl Iterator for InternalIterator { type Item = Value; fn next(&mut self) -> Option { if !self.leftovers.is_empty() { let output = self.leftovers.remove(0); return Some(output); } while let Some(item) = self.input.next() { match item { Ok(ReturnSuccess::Action(action)) => match action { CommandAction::ChangePath(path) => { self.context.shell_manager.set_path(path); } CommandAction::Exit(code) => std::process::exit(code), // TODO: save history.txt CommandAction::Error(err) => { self.context.error(err); return None; } CommandAction::AutoConvert(tagged_contents, extension) => { let contents_tag = tagged_contents.tag.clone(); let command_name = format!("from {}", extension); if let Some(converter) = self.context.scope.get_command(&command_name) { let new_args = RawCommandArgs { host: self.context.host.clone(), ctrl_c: self.context.ctrl_c.clone(), configs: self.context.configs.clone(), current_errors: self.context.current_errors.clone(), shell_manager: self.context.shell_manager.clone(), call_info: UnevaluatedCallInfo { args: nu_protocol::hir::Call { head: Box::new(SpannedExpression { expr: Expression::Synthetic(Synthetic::String( command_name.clone(), )), span: tagged_contents.tag().span, }), positional: None, named: None, span: Span::unknown(), external_redirection: ExternalRedirection::Stdout, }, name_tag: tagged_contents.tag(), }, scope: self.context.scope.clone(), }; let result = converter .run_with_actions(new_args.with_input(vec![tagged_contents])); match result { Ok(mut result) => { let result_vec: Vec> = result.drain_vec(); let mut output = vec![]; for res in result_vec { match res { Ok(ReturnSuccess::Value(Value { value: UntaggedValue::Table(list), .. })) => { for l in list { output.push(l); } } Ok(ReturnSuccess::Value(Value { value, .. })) => { output.push(value.into_value(contents_tag.clone())); } Err(e) => output.push( UntaggedValue::Error(e).into_untagged_value(), ), _ => {} } } let mut output = output.into_iter(); if let Some(x) = output.next() { self.leftovers = output.collect(); return Some(x); } } Err(err) => { self.context.error(err); } } } else { return Some(tagged_contents); } } CommandAction::EnterValueShell(value) => { self.context .shell_manager .insert_at_current(Box::new(ValueShell::new(value))); } CommandAction::EnterShell(location) => { let mode = if self.context.shell_manager.is_interactive() { FilesystemShellMode::Cli } else { FilesystemShellMode::Script }; self.context.shell_manager.insert_at_current(Box::new( match FilesystemShell::with_location(location, mode) { Ok(v) => v, Err(err) => { self.context.error(err.into()); break; } }, )); } CommandAction::AddPlugins(path) => { match crate::plugin::build_plugin::scan(vec![std::path::PathBuf::from( path, )]) { Ok(plugins) => { self.context.add_commands( plugins .into_iter() .filter(|p| !self.context.is_command_registered(p.name())) .collect(), ); } Err(reason) => { self.context.error(reason); } } } CommandAction::PreviousShell => { self.context.shell_manager.prev(); } CommandAction::NextShell => { self.context.shell_manager.next(); } CommandAction::LeaveShell(code) => { self.context.shell_manager.remove_at_current(); if self.context.shell_manager.is_empty() { std::process::exit(code); // TODO: save history.txt } } CommandAction::UnloadConfig(cfg_path) => { self.context.unload_config(&cfg_path); } CommandAction::LoadConfig(cfg_path) => { if let Err(e) = self.context.load_config(&cfg_path) { return Some(UntaggedValue::Error(e).into_untagged_value()); } } }, Ok(ReturnSuccess::Value(Value { value: UntaggedValue::Error(err), .. })) => { self.context.error(err); return None; } Ok(ReturnSuccess::Value(v)) => return Some(v), Ok(ReturnSuccess::DebugValue(v)) => { let doc = PrettyDebug::pretty_doc(&v); let mut buffer = termcolor::Buffer::ansi(); let _ = doc.render_raw( self.context.with_host(|host| host.width() - 5), &mut nu_source::TermColored::new(&mut buffer), ); let value = String::from_utf8_lossy(buffer.as_slice()); return Some(UntaggedValue::string(value).into_untagged_value()); } Err(err) => { self.context.error(err); } } } None } }