2021-01-10 02:50:49 +00:00
|
|
|
use crate::call_info::UnevaluatedCallInfo;
|
|
|
|
use crate::command_args::RawCommandArgs;
|
|
|
|
use crate::evaluation_context::EvaluationContext;
|
2021-03-31 05:52:34 +00:00
|
|
|
use crate::filesystem::filesystem_shell::{FilesystemShell, FilesystemShellMode};
|
2021-01-10 02:50:49 +00:00
|
|
|
use crate::shell::value_shell::ValueShell;
|
2019-11-24 22:19:12 +00:00
|
|
|
use log::{log_enabled, trace};
|
2019-11-21 17:18:00 +00:00
|
|
|
use nu_errors::ShellError;
|
2020-08-07 04:53:37 +00:00
|
|
|
use nu_protocol::hir::{ExternalRedirection, InternalCommand};
|
2021-04-03 23:56:46 +00:00
|
|
|
use nu_protocol::{CommandAction, ReturnSuccess, UntaggedValue, Value};
|
2021-01-10 02:50:49 +00:00
|
|
|
use nu_source::{PrettyDebug, Span, Tag};
|
2021-04-11 01:31:08 +00:00
|
|
|
use nu_stream::{InputStream, OutputStream};
|
2019-11-24 22:19:12 +00:00
|
|
|
|
2021-04-06 16:19:43 +00:00
|
|
|
pub(crate) fn run_internal_command(
|
2019-11-24 22:19:12 +00:00
|
|
|
command: InternalCommand,
|
2020-12-18 07:53:49 +00:00
|
|
|
context: &EvaluationContext,
|
Move external closer to internal (#1611)
* Refactor InputStream and affected commands.
First, making `values` private and leaning on the `Stream` implementation makes
consumes of `InputStream` less likely to have to change in the future, if we
change what an `InputStream` is internally.
Second, we're dropping `Option<InputStream>` as the input to pipelines,
internals, and externals. Instead, `InputStream.is_empty` can be used to check
for "emptiness". Empty streams are typically only ever used as the first input
to a pipeline.
* Add run_external internal command.
We want to push external commands closer to internal commands, eventually
eliminating the concept of "external" completely. This means we can consolidate
a couple of things:
- Variable evaluation (for example, `$it`, `$nu`, alias vars)
- Behaviour of whole stream vs per-item external execution
It should also make it easier for us to start introducing argument signatures
for external commands,
* Update run_external.rs
* Update run_external.rs
* Update run_external.rs
* Update run_external.rs
Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2020-04-20 03:30:44 +00:00
|
|
|
input: InputStream,
|
|
|
|
) -> Result<InputStream, ShellError> {
|
2019-11-24 22:19:12 +00:00
|
|
|
if log_enabled!(log::Level::Trace) {
|
|
|
|
trace!(target: "nu::run::internal", "->");
|
|
|
|
trace!(target: "nu::run::internal", "{}", command.name);
|
2019-11-24 22:19:12 +00:00
|
|
|
}
|
|
|
|
|
2021-04-06 16:19:43 +00:00
|
|
|
let objects: InputStream = input;
|
2020-12-18 07:53:49 +00:00
|
|
|
|
|
|
|
let internal_command = context.scope.expect_command(&command.name);
|
2019-11-24 22:19:12 +00:00
|
|
|
|
2020-06-14 21:00:42 +00:00
|
|
|
let result = {
|
2021-04-06 16:19:43 +00:00
|
|
|
context.run_command(
|
|
|
|
internal_command?,
|
|
|
|
Tag::unknown_anchor(command.name_span),
|
|
|
|
command.args.clone(),
|
|
|
|
objects,
|
|
|
|
)?
|
2019-11-24 22:19:12 +00:00
|
|
|
};
|
2019-11-24 22:19:12 +00:00
|
|
|
|
2020-06-14 21:00:42 +00:00
|
|
|
Ok(InputStream::from_stream(
|
2021-04-11 01:31:08 +00:00
|
|
|
InternalIterator {
|
|
|
|
command,
|
|
|
|
context: context.clone(),
|
|
|
|
leftovers: vec![],
|
|
|
|
input: result,
|
|
|
|
}
|
|
|
|
.take_while(|x| !x.is_error()),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
struct InternalIterator {
|
|
|
|
context: EvaluationContext,
|
|
|
|
command: InternalCommand,
|
|
|
|
leftovers: Vec<Value>,
|
|
|
|
input: OutputStream,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for InternalIterator {
|
|
|
|
type Item = Value;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
// let head = Arc::new(command.args.head.clone());
|
|
|
|
// let context = context.clone();
|
|
|
|
// let command = Arc::new(command);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
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: self.command.args.head.clone(),
|
|
|
|
positional: None,
|
|
|
|
named: None,
|
|
|
|
span: Span::unknown(),
|
|
|
|
external_redirection: ExternalRedirection::Stdout,
|
2021-04-06 16:19:43 +00:00
|
|
|
},
|
2021-04-11 01:31:08 +00:00
|
|
|
name_tag: Tag::unknown_anchor(self.command.name_span),
|
|
|
|
},
|
|
|
|
scope: self.context.scope.clone(),
|
|
|
|
};
|
|
|
|
let result = converter.run(new_args.with_input(vec![tagged_contents]));
|
|
|
|
|
|
|
|
match result {
|
|
|
|
Ok(mut result) => {
|
|
|
|
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
|
|
|
|
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);
|
2021-04-06 16:19:43 +00:00
|
|
|
}
|
2020-06-14 21:00:42 +00:00
|
|
|
}
|
2021-04-11 01:31:08 +00:00
|
|
|
Ok(ReturnSuccess::Value(Value { value, .. })) => {
|
|
|
|
output.push(value.into_value(contents_tag.clone()));
|
|
|
|
}
|
|
|
|
Err(e) => output.push(
|
|
|
|
UntaggedValue::Error(e).into_untagged_value(),
|
|
|
|
),
|
|
|
|
_ => {}
|
2019-12-07 03:46:05 +00:00
|
|
|
}
|
2020-09-14 14:07:02 +00:00
|
|
|
}
|
2021-04-11 01:31:08 +00:00
|
|
|
|
|
|
|
let mut output = output.into_iter();
|
|
|
|
|
|
|
|
if let Some(x) = output.next() {
|
|
|
|
self.leftovers = output.collect();
|
|
|
|
|
|
|
|
return Some(x);
|
2020-09-14 14:07:02 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-11 01:31:08 +00:00
|
|
|
Err(err) => {
|
|
|
|
self.context.error(err);
|
|
|
|
}
|
2020-09-14 14:07:02 +00:00
|
|
|
}
|
2021-04-11 01:31:08 +00:00
|
|
|
} else {
|
|
|
|
return Some(tagged_contents);
|
2021-04-06 16:19:43 +00:00
|
|
|
}
|
2021-04-11 01:31:08 +00:00
|
|
|
}
|
|
|
|
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;
|
2021-03-31 05:52:34 +00:00
|
|
|
}
|
2021-04-11 01:31:08 +00:00
|
|
|
},
|
|
|
|
));
|
|
|
|
}
|
|
|
|
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(),
|
|
|
|
);
|
2021-03-31 05:52:34 +00:00
|
|
|
}
|
2021-04-11 01:31:08 +00:00
|
|
|
Err(reason) => {
|
|
|
|
self.context.error(reason);
|
2021-04-06 16:19:43 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-11 01:31:08 +00:00
|
|
|
}
|
|
|
|
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
|
2021-04-06 16:19:43 +00:00
|
|
|
}
|
2021-04-11 01:31:08 +00:00
|
|
|
}
|
|
|
|
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());
|
2021-04-06 16:19:43 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-11 01:31:08 +00:00
|
|
|
},
|
2019-11-24 22:19:12 +00:00
|
|
|
|
2021-04-11 01:31:08 +00:00
|
|
|
Ok(ReturnSuccess::Value(Value {
|
|
|
|
value: UntaggedValue::Error(err),
|
|
|
|
..
|
|
|
|
})) => {
|
|
|
|
self.context.error(err);
|
|
|
|
}
|
2019-11-24 22:19:12 +00:00
|
|
|
|
2021-04-11 01:31:08 +00:00
|
|
|
Ok(ReturnSuccess::Value(v)) => return Some(v),
|
2019-11-24 22:19:12 +00:00
|
|
|
|
2021-04-11 01:31:08 +00:00
|
|
|
Ok(ReturnSuccess::DebugValue(v)) => {
|
|
|
|
let doc = PrettyDebug::pretty_doc(&v);
|
|
|
|
let mut buffer = termcolor::Buffer::ansi();
|
2019-11-24 22:19:12 +00:00
|
|
|
|
2021-04-11 01:31:08 +00:00
|
|
|
let _ = doc.render_raw(
|
|
|
|
self.context.with_host(|host| host.width() - 5),
|
|
|
|
&mut nu_source::TermColored::new(&mut buffer),
|
|
|
|
);
|
2019-11-24 22:19:12 +00:00
|
|
|
|
2021-04-11 01:31:08 +00:00
|
|
|
let value = String::from_utf8_lossy(buffer.as_slice());
|
2019-11-24 22:19:12 +00:00
|
|
|
|
2021-04-11 01:31:08 +00:00
|
|
|
return Some(UntaggedValue::string(value).into_untagged_value());
|
2019-11-24 22:19:12 +00:00
|
|
|
}
|
2021-04-11 01:31:08 +00:00
|
|
|
|
|
|
|
Err(err) => {
|
|
|
|
self.context.error(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
2019-11-24 22:19:12 +00:00
|
|
|
}
|