mirror of
https://github.com/nushell/nushell
synced 2024-12-27 21:43:09 +00:00
Port skip
, skip while
and skip until
commands (#380)
* Add `Skip` command * Add `SkipUntil` sub-command * Add `SkipWhile` sub-command * Add and use `Expression::as_row_condition_block`
This commit is contained in:
parent
ee239a0d37
commit
bab8f6bd28
10 changed files with 300 additions and 26 deletions
|
@ -116,6 +116,9 @@ pub fn create_default_context() -> EngineState {
|
|||
Select,
|
||||
Shuffle,
|
||||
Size,
|
||||
Skip,
|
||||
SkipUntil,
|
||||
SkipWhile,
|
||||
Sleep,
|
||||
Source,
|
||||
Split,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use nu_engine::eval_block;
|
||||
use nu_protocol::{
|
||||
ast::{Call, Expr, Expression},
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
|
||||
};
|
||||
|
@ -52,13 +52,9 @@ impl Command for All {
|
|||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let predicate = &call.positional[0];
|
||||
let block_id = match predicate {
|
||||
Expression {
|
||||
expr: Expr::RowCondition(block_id),
|
||||
..
|
||||
} => *block_id,
|
||||
_ => return Err(ShellError::InternalError("Expected row condition".into())),
|
||||
};
|
||||
let block_id = predicate
|
||||
.as_row_condition_block()
|
||||
.ok_or_else(|| ShellError::InternalError("Expected row condition".to_owned()))?;
|
||||
|
||||
let span = call.head;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use nu_engine::eval_block;
|
||||
use nu_protocol::{
|
||||
ast::{Call, Expr, Expression},
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
|
||||
};
|
||||
|
@ -52,13 +52,9 @@ impl Command for Any {
|
|||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let predicate = &call.positional[0];
|
||||
let block_id = match predicate {
|
||||
Expression {
|
||||
expr: Expr::RowCondition(block_id),
|
||||
..
|
||||
} => *block_id,
|
||||
_ => return Err(ShellError::InternalError("Expected row condition".into())),
|
||||
};
|
||||
let block_id = predicate
|
||||
.as_row_condition_block()
|
||||
.ok_or_else(|| ShellError::InternalError("Expected row condition".to_owned()))?;
|
||||
|
||||
let span = call.head;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ mod range;
|
|||
mod reverse;
|
||||
mod select;
|
||||
mod shuffle;
|
||||
mod skip;
|
||||
mod update;
|
||||
mod where_;
|
||||
mod wrap;
|
||||
|
@ -35,6 +36,7 @@ pub use range::Range;
|
|||
pub use reverse::Reverse;
|
||||
pub use select::Select;
|
||||
pub use shuffle::Shuffle;
|
||||
pub use skip::*;
|
||||
pub use update::Update;
|
||||
pub use where_::Where;
|
||||
pub use wrap::Wrap;
|
||||
|
|
90
crates/nu-command/src/filters/skip/command.rs
Normal file
90
crates/nu-command/src/filters/skip/command.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::convert::TryInto;
|
||||
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Skip;
|
||||
|
||||
impl Command for Skip {
|
||||
fn name(&self) -> &str {
|
||||
"skip"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build(self.name())
|
||||
.optional("n", SyntaxShape::Int, "the number of elements to skip")
|
||||
.category(Category::Filters)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Skip the first n elements of the input."
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Skip two elements",
|
||||
example: "echo [[editions]; [2015] [2018] [2021]] | skip 2",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols: vec!["editions".to_owned()],
|
||||
vals: vec![Value::from(2021)],
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Skip the first value",
|
||||
example: "echo [2 4 6 8] | skip",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::from(4), Value::from(6), Value::from(8)],
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let n: Option<Value> = call.opt(engine_state, stack, 0)?;
|
||||
|
||||
let n: usize = match n {
|
||||
Some(Value::Int { val, span }) => val.try_into().map_err(|err| {
|
||||
ShellError::UnsupportedInput(
|
||||
format!("Could not convert {} to unsigned integer: {}", val, err),
|
||||
span,
|
||||
)
|
||||
})?,
|
||||
Some(_) => return Err(ShellError::InternalError("Expected integer".into())),
|
||||
None => 1,
|
||||
};
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
|
||||
Ok(input.into_iter().skip(n).into_pipeline_data(ctrlc))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(Skip {})
|
||||
}
|
||||
}
|
7
crates/nu-command/src/filters/skip/mod.rs
Normal file
7
crates/nu-command/src/filters/skip/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
mod command;
|
||||
mod until;
|
||||
mod while_;
|
||||
|
||||
pub use command::Skip;
|
||||
pub use until::SkipUntil;
|
||||
pub use while_::SkipWhile;
|
89
crates/nu-command/src/filters/skip/until.rs
Normal file
89
crates/nu-command/src/filters/skip/until.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
use nu_engine::eval_block;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SkipUntil;
|
||||
|
||||
impl Command for SkipUntil {
|
||||
fn name(&self) -> &str {
|
||||
"skip until"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build(self.name())
|
||||
.required(
|
||||
"predicate",
|
||||
SyntaxShape::RowCondition,
|
||||
"the predicate that skipped element must not match",
|
||||
)
|
||||
.category(Category::Filters)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Skip elements of the input until a predicate is true."
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Skip until the element is positive",
|
||||
example: "echo [-2 0 2 -1] | skip until $it > 0",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::from(2), Value::from(-1)],
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
}]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let predicate = &call.positional[0];
|
||||
let block_id = predicate
|
||||
.as_row_condition_block()
|
||||
.ok_or_else(|| ShellError::InternalError("Expected row condition".to_owned()))?;
|
||||
|
||||
let span = call.head;
|
||||
|
||||
let block = engine_state.get_block(block_id).clone();
|
||||
let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id);
|
||||
let mut stack = stack.collect_captures(&block.captures);
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let engine_state = engine_state.clone();
|
||||
|
||||
Ok(input
|
||||
.into_iter()
|
||||
.skip_while(move |value| {
|
||||
if let Some(var_id) = var_id {
|
||||
stack.add_var(var_id, value.clone());
|
||||
}
|
||||
|
||||
!eval_block(&engine_state, &mut stack, &block, PipelineData::new(span))
|
||||
.map_or(false, |pipeline_data| {
|
||||
pipeline_data.into_value(span).is_true()
|
||||
})
|
||||
})
|
||||
.into_pipeline_data(ctrlc))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(SkipUntil)
|
||||
}
|
||||
}
|
89
crates/nu-command/src/filters/skip/while_.rs
Normal file
89
crates/nu-command/src/filters/skip/while_.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
use nu_engine::eval_block;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SkipWhile;
|
||||
|
||||
impl Command for SkipWhile {
|
||||
fn name(&self) -> &str {
|
||||
"skip while"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build(self.name())
|
||||
.required(
|
||||
"predicate",
|
||||
SyntaxShape::RowCondition,
|
||||
"the predicate that skipped element must match",
|
||||
)
|
||||
.category(Category::Filters)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Skip elements of the input while a predicate is true."
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Skip while the element is negative",
|
||||
example: "echo [-2 0 2 -1] | skip while $it < 0",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::from(0), Value::from(2), Value::from(-1)],
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
}]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let predicate = &call.positional[0];
|
||||
let block_id = predicate
|
||||
.as_row_condition_block()
|
||||
.ok_or_else(|| ShellError::InternalError("Expected row condition".to_owned()))?;
|
||||
|
||||
let span = call.head;
|
||||
|
||||
let block = engine_state.get_block(block_id).clone();
|
||||
let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id);
|
||||
let mut stack = stack.collect_captures(&block.captures);
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let engine_state = engine_state.clone();
|
||||
|
||||
Ok(input
|
||||
.into_iter()
|
||||
.skip_while(move |value| {
|
||||
if let Some(var_id) = var_id {
|
||||
stack.add_var(var_id, value.clone());
|
||||
}
|
||||
|
||||
eval_block(&engine_state, &mut stack, &block, PipelineData::new(span))
|
||||
.map_or(false, |pipeline_data| {
|
||||
pipeline_data.into_value(span).is_true()
|
||||
})
|
||||
})
|
||||
.into_pipeline_data(ctrlc))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(SkipWhile)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use nu_engine::eval_block;
|
||||
use nu_protocol::ast::{Call, Expr, Expression};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Category, PipelineData, ShellError, Signature, SyntaxShape};
|
||||
|
||||
|
@ -29,19 +29,14 @@ impl Command for Where {
|
|||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let span = call.head;
|
||||
let cond = call.positional[0].clone();
|
||||
let cond = &call.positional[0];
|
||||
let block_id = cond
|
||||
.as_row_condition_block()
|
||||
.ok_or_else(|| ShellError::InternalError("Expected row condition".to_owned()))?;
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let engine_state = engine_state.clone();
|
||||
|
||||
let block_id = match cond {
|
||||
Expression {
|
||||
expr: Expr::RowCondition(block_id),
|
||||
..
|
||||
} => block_id,
|
||||
_ => return Err(ShellError::InternalError("Expected row condition".into())),
|
||||
};
|
||||
|
||||
let block = engine_state.get_block(block_id).clone();
|
||||
let mut stack = stack.collect_captures(&block.captures);
|
||||
|
||||
|
|
|
@ -53,6 +53,13 @@ impl Expression {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_row_condition_block(&self) -> Option<BlockId> {
|
||||
match self.expr {
|
||||
Expr::RowCondition(block_id) => Some(block_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_signature(&self) -> Option<Box<Signature>> {
|
||||
match &self.expr {
|
||||
Expr::Signature(sig) => Some(sig.clone()),
|
||||
|
|
Loading…
Reference in a new issue