forked from extern/nushell
If command and touchups (#2106)
This commit is contained in:
parent
0629c896eb
commit
e75c44c95b
@ -330,6 +330,7 @@ pub fn create_default_context(
|
||||
whole_stream_command(Drop),
|
||||
whole_stream_command(Format),
|
||||
whole_stream_command(Where),
|
||||
whole_stream_command(If),
|
||||
whole_stream_command(Compact),
|
||||
whole_stream_command(Default),
|
||||
whole_stream_command(Skip),
|
||||
|
@ -62,6 +62,7 @@ pub(crate) mod headers;
|
||||
pub(crate) mod help;
|
||||
pub(crate) mod histogram;
|
||||
pub(crate) mod history;
|
||||
pub(crate) mod if_;
|
||||
pub(crate) mod insert;
|
||||
pub(crate) mod is_empty;
|
||||
pub(crate) mod keep;
|
||||
@ -157,6 +158,7 @@ pub(crate) use drop::Drop;
|
||||
pub(crate) use du::Du;
|
||||
pub(crate) use each::Each;
|
||||
pub(crate) use echo::Echo;
|
||||
pub(crate) use if_::If;
|
||||
pub(crate) use is_empty::IsEmpty;
|
||||
pub(crate) use update::Update;
|
||||
pub(crate) mod kill;
|
||||
|
188
crates/nu-cli/src/commands/if_.rs
Normal file
188
crates/nu-cli/src/commands/if_.rs
Normal file
@ -0,0 +1,188 @@
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::evaluate::evaluate_baseline_expr;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::Block, hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue};
|
||||
|
||||
pub struct If;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct IfArgs {
|
||||
condition: Block,
|
||||
then_case: Block,
|
||||
else_case: Block,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for If {
|
||||
fn name(&self) -> &str {
|
||||
"if"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("if")
|
||||
.required(
|
||||
"condition",
|
||||
SyntaxShape::Math,
|
||||
"the condition that must match",
|
||||
)
|
||||
.required(
|
||||
"then_case",
|
||||
SyntaxShape::Block,
|
||||
"block to run if condition is true",
|
||||
)
|
||||
.required(
|
||||
"else_case",
|
||||
SyntaxShape::Block,
|
||||
"block to run if condition is false",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Filter table to match the condition."
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
if_command(args, registry).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Run a block if a condition is true",
|
||||
example: "echo 10 | if $it > 5 { echo 'greater than 5' } { echo 'less than or equal to 5' }",
|
||||
result: Some(vec![UntaggedValue::string("greater than 5").into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Run a block if a condition is false",
|
||||
example: "echo 1 | if $it > 5 { echo 'greater than 5' } { echo 'less than or equal to 5' }",
|
||||
result: Some(vec![UntaggedValue::string("less than or equal to 5").into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
async fn if_command(
|
||||
raw_args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = Arc::new(registry.clone());
|
||||
let scope = Arc::new(raw_args.call_info.scope.clone());
|
||||
let tag = raw_args.call_info.name_tag.clone();
|
||||
let context = Arc::new(Context::from_raw(&raw_args, ®istry));
|
||||
|
||||
let (
|
||||
IfArgs {
|
||||
condition,
|
||||
then_case,
|
||||
else_case,
|
||||
},
|
||||
input,
|
||||
) = raw_args.process(®istry).await?;
|
||||
let condition = {
|
||||
if condition.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
match condition.block[0].list.get(0) {
|
||||
Some(item) => match item {
|
||||
ClassifiedCommand::Expr(expr) => expr.clone(),
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(input
|
||||
.then(move |input| {
|
||||
let condition = condition.clone();
|
||||
let then_case = then_case.clone();
|
||||
let else_case = else_case.clone();
|
||||
let registry = registry.clone();
|
||||
let scope = scope.clone();
|
||||
let mut context = context.clone();
|
||||
|
||||
async move {
|
||||
//FIXME: should we use the scope that's brought in as well?
|
||||
let condition =
|
||||
evaluate_baseline_expr(&condition, &*registry, &input, &scope.vars, &scope.env)
|
||||
.await;
|
||||
|
||||
match condition {
|
||||
Ok(condition) => match condition.as_bool() {
|
||||
Ok(b) => {
|
||||
if b {
|
||||
match run_block(
|
||||
&then_case,
|
||||
Arc::make_mut(&mut context),
|
||||
InputStream::empty(),
|
||||
&input,
|
||||
&scope.vars,
|
||||
&scope.env,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(stream) => stream.to_output_stream(),
|
||||
Err(e) => futures::stream::iter(vec![Err(e)].into_iter())
|
||||
.to_output_stream(),
|
||||
}
|
||||
} else {
|
||||
match run_block(
|
||||
&else_case,
|
||||
Arc::make_mut(&mut context),
|
||||
InputStream::empty(),
|
||||
&input,
|
||||
&scope.vars,
|
||||
&scope.env,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(stream) => stream.to_output_stream(),
|
||||
Err(e) => futures::stream::iter(vec![Err(e)].into_iter())
|
||||
.to_output_stream(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
futures::stream::iter(vec![Err(e)].into_iter()).to_output_stream()
|
||||
}
|
||||
},
|
||||
Err(e) => futures::stream::iter(vec![Err(e)].into_iter()).to_output_stream(),
|
||||
}
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::If;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(If {})
|
||||
}
|
||||
}
|
@ -70,16 +70,20 @@ async fn nth(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
||||
let row_numbers = vec![vec![row_number], and_rows]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<Tagged<u64>>>();
|
||||
.map(|x| x.item)
|
||||
.collect::<Vec<u64>>();
|
||||
|
||||
let max_row_number = row_numbers
|
||||
.iter()
|
||||
.max()
|
||||
.expect("Internal error: should be > 0 row numbers");
|
||||
|
||||
Ok(input
|
||||
.take(*max_row_number as usize + 1)
|
||||
.enumerate()
|
||||
.filter_map(move |(idx, item)| {
|
||||
futures::future::ready(
|
||||
if row_numbers
|
||||
.iter()
|
||||
.any(|requested| requested.item == idx as u64)
|
||||
{
|
||||
if row_numbers.iter().any(|requested| *requested == idx as u64) {
|
||||
Some(ReturnSuccess::value(item))
|
||||
} else {
|
||||
None
|
||||
|
@ -171,14 +171,22 @@ async fn open(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
||||
.map_err(|e| ShellError::unexpected(format!("AsyncRead failed in open function: {:?}", e)))
|
||||
.into_stream();
|
||||
|
||||
let final_stream = sob_stream.map(|x| match x {
|
||||
Ok(StringOrBinary::String(s)) => {
|
||||
ReturnSuccess::value(UntaggedValue::string(s).into_untagged_value())
|
||||
let final_stream = sob_stream.map(move |x| {
|
||||
// The tag that will used when returning a Value
|
||||
let file_tag = Tag {
|
||||
span: path.tag.span,
|
||||
anchor: Some(AnchorLocation::File(path.to_string_lossy().to_string())),
|
||||
};
|
||||
|
||||
match x {
|
||||
Ok(StringOrBinary::String(s)) => {
|
||||
ReturnSuccess::value(UntaggedValue::string(s).into_value(file_tag))
|
||||
}
|
||||
Ok(StringOrBinary::Binary(b)) => ReturnSuccess::value(
|
||||
UntaggedValue::binary(b.into_iter().collect()).into_value(file_tag),
|
||||
),
|
||||
Err(se) => Err(se),
|
||||
}
|
||||
Ok(StringOrBinary::Binary(b)) => ReturnSuccess::value(
|
||||
UntaggedValue::binary(b.into_iter().collect()).into_untagged_value(),
|
||||
),
|
||||
Err(se) => Err(se),
|
||||
});
|
||||
|
||||
Ok(OutputStream::new(final_stream))
|
||||
|
@ -29,7 +29,7 @@ pub enum InlineShape {
|
||||
Date(DateTime<Utc>),
|
||||
Duration(i64),
|
||||
Path(PathBuf),
|
||||
Binary,
|
||||
Binary(usize),
|
||||
|
||||
Row(BTreeMap<Column, InlineShape>),
|
||||
Table(Vec<InlineShape>),
|
||||
@ -73,7 +73,7 @@ impl InlineShape {
|
||||
Primitive::Date(date) => InlineShape::Date(*date),
|
||||
Primitive::Duration(duration) => InlineShape::Duration(*duration),
|
||||
Primitive::Path(path) => InlineShape::Path(path.clone()),
|
||||
Primitive::Binary(_) => InlineShape::Binary,
|
||||
Primitive::Binary(b) => InlineShape::Binary(b.len()),
|
||||
Primitive::BeginningOfStream => InlineShape::BeginningOfStream,
|
||||
Primitive::EndOfStream => InlineShape::EndOfStream,
|
||||
}
|
||||
@ -182,7 +182,7 @@ impl PrettyDebug for FormatInlineShape {
|
||||
b::description(format_primitive(&Primitive::Duration(*duration), None))
|
||||
}
|
||||
InlineShape::Path(path) => b::primitive(path.display()),
|
||||
InlineShape::Binary => b::opaque("<binary>"),
|
||||
InlineShape::Binary(length) => b::opaque(format!("<binary: {} bytes>", length)),
|
||||
InlineShape::Row(row) => b::delimit(
|
||||
"[",
|
||||
b::kind("row")
|
||||
|
@ -1019,6 +1019,7 @@ fn parse_positional_argument(
|
||||
idx: usize,
|
||||
lite_cmd: &LiteCommand,
|
||||
positional_type: &PositionalType,
|
||||
remaining_positionals: usize,
|
||||
registry: &dyn SignatureRegistry,
|
||||
) -> (usize, SpannedExpression, Option<ParseError>) {
|
||||
let mut idx = idx;
|
||||
@ -1038,8 +1039,12 @@ fn parse_positional_argument(
|
||||
}
|
||||
arg
|
||||
} else {
|
||||
let (new_idx, arg, err) =
|
||||
parse_math_expression(idx, &lite_cmd.args[idx..], registry, true);
|
||||
let (new_idx, arg, err) = parse_math_expression(
|
||||
idx,
|
||||
&lite_cmd.args[idx..(lite_cmd.args.len() - remaining_positionals)],
|
||||
registry,
|
||||
true,
|
||||
);
|
||||
|
||||
let span = arg.span;
|
||||
let mut commands = hir::Commands::new(span);
|
||||
@ -1049,7 +1054,7 @@ fn parse_positional_argument(
|
||||
|
||||
let arg = SpannedExpression::new(Expression::Block(block), span);
|
||||
|
||||
idx = new_idx;
|
||||
idx = new_idx - 1;
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
@ -1165,6 +1170,7 @@ fn parse_internal_command(
|
||||
idx,
|
||||
&lite_cmd,
|
||||
&signature.positional[current_positional].0,
|
||||
signature.positional.len() - current_positional - 1,
|
||||
registry,
|
||||
);
|
||||
idx = new_idx;
|
||||
|
Loading…
Reference in New Issue
Block a user