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 05:30:44 +02:00
|
|
|
use crate::commands::classified::external;
|
|
|
|
use crate::commands::WholeStreamCommand;
|
|
|
|
use crate::prelude::*;
|
|
|
|
|
|
|
|
use derive_new::new;
|
|
|
|
use parking_lot::Mutex;
|
|
|
|
|
|
|
|
use nu_errors::ShellError;
|
2020-04-20 23:45:11 +02:00
|
|
|
use nu_protocol::hir::{Expression, ExternalArgs, ExternalCommand, Literal, SpannedExpression};
|
|
|
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape};
|
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 05:30:44 +02:00
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
pub struct RunExternalArgs {}
|
|
|
|
|
|
|
|
#[derive(new)]
|
|
|
|
pub struct RunExternalCommand;
|
|
|
|
|
2020-04-20 23:45:11 +02:00
|
|
|
fn spanned_expression_to_string(expr: SpannedExpression) -> String {
|
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 05:30:44 +02:00
|
|
|
if let SpannedExpression {
|
|
|
|
expr: Expression::Literal(Literal::String(s)),
|
|
|
|
..
|
|
|
|
} = expr
|
|
|
|
{
|
2020-04-20 23:45:11 +02:00
|
|
|
s
|
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 05:30:44 +02:00
|
|
|
} else {
|
|
|
|
"notacommand!!!".to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl WholeStreamCommand for RunExternalCommand {
|
|
|
|
fn name(&self) -> &str {
|
|
|
|
"run_external"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signature(&self) -> Signature {
|
|
|
|
Signature::build(self.name()).rest(SyntaxShape::Any, "external command arguments")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn usage(&self) -> &str {
|
|
|
|
""
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run(
|
|
|
|
&self,
|
|
|
|
args: CommandArgs,
|
|
|
|
registry: &CommandRegistry,
|
|
|
|
) -> Result<OutputStream, ShellError> {
|
|
|
|
let positionals = args.call_info.args.positional.ok_or_else(|| {
|
|
|
|
ShellError::untagged_runtime_error("positional arguments unexpectedly empty")
|
|
|
|
})?;
|
|
|
|
|
2020-04-20 23:45:11 +02:00
|
|
|
let mut positionals = positionals.into_iter();
|
|
|
|
|
|
|
|
let name = positionals
|
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 05:30:44 +02:00
|
|
|
.next()
|
|
|
|
.map(spanned_expression_to_string)
|
|
|
|
.ok_or_else(|| {
|
|
|
|
ShellError::untagged_runtime_error(
|
|
|
|
"run_external unexpectedly missing external name positional arg",
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let command = ExternalCommand {
|
|
|
|
name,
|
2020-04-20 09:44:19 +02:00
|
|
|
name_tag: args.call_info.name_tag.clone(),
|
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 05:30:44 +02:00
|
|
|
args: ExternalArgs {
|
2020-04-20 23:45:11 +02:00
|
|
|
list: positionals.collect(),
|
2020-04-20 09:44:19 +02:00
|
|
|
span: args.call_info.args.span,
|
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 05:30:44 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut external_context;
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
external_context = Context {
|
|
|
|
registry: registry.clone(),
|
|
|
|
host: args.host.clone(),
|
|
|
|
shell_manager: args.shell_manager.clone(),
|
|
|
|
ctrl_c: args.ctrl_c.clone(),
|
|
|
|
current_errors: Arc::new(Mutex::new(vec![])),
|
|
|
|
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
{
|
|
|
|
external_context = Context {
|
|
|
|
registry: registry.clone(),
|
|
|
|
host: args.host.clone(),
|
|
|
|
shell_manager: args.shell_manager.clone(),
|
|
|
|
ctrl_c: args.ctrl_c.clone(),
|
|
|
|
current_errors: Arc::new(Mutex::new(vec![])),
|
|
|
|
};
|
|
|
|
}
|
2020-04-20 23:45:11 +02:00
|
|
|
let scope = args.call_info.scope.clone();
|
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 05:30:44 +02:00
|
|
|
|
|
|
|
let is_last = args.call_info.args.is_last;
|
|
|
|
let input = args.input;
|
|
|
|
let stream = async_stream! {
|
|
|
|
let result = external::run_external_command(
|
|
|
|
command,
|
|
|
|
&mut external_context,
|
|
|
|
input,
|
|
|
|
&scope,
|
|
|
|
is_last,
|
|
|
|
).await;
|
|
|
|
|
|
|
|
match result {
|
|
|
|
Ok(mut stream) => {
|
|
|
|
while let Some(value) = stream.next().await {
|
|
|
|
yield Ok(ReturnSuccess::Value(value));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
yield Err(e);
|
|
|
|
},
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(stream.to_output_stream())
|
|
|
|
}
|
|
|
|
}
|