mirror of
https://github.com/nushell/nushell.git
synced 2024-11-23 00:43:33 +01:00
Port merge command from Nushell (#808)
* Add example test to zip * Port merge command from Nushell On top of the original merge, this one should not collect a stream returned from the merged block and allows merging records.
This commit is contained in:
parent
e1272f3b73
commit
564c2dd7d1
@ -67,6 +67,7 @@ pub fn create_default_context(cwd: impl AsRef<Path>) -> EngineState {
|
||||
Get,
|
||||
GroupBy,
|
||||
Keep,
|
||||
Merge,
|
||||
KeepUntil,
|
||||
KeepWhile,
|
||||
Last,
|
||||
|
@ -14,7 +14,7 @@ use crate::To;
|
||||
#[cfg(test)]
|
||||
use super::{
|
||||
Ansi, Date, From, If, Into, Math, Path, Random, Split, Str, StrCollect, StrFindReplace,
|
||||
StrLength, Url,
|
||||
StrLength, Url, Wrap,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
@ -44,6 +44,7 @@ pub fn test_examples(cmd: impl Command + 'static) {
|
||||
working_set.add_decl(Box::new(Date));
|
||||
working_set.add_decl(Box::new(Url));
|
||||
working_set.add_decl(Box::new(Ansi));
|
||||
working_set.add_decl(Box::new(Wrap));
|
||||
|
||||
use super::Echo;
|
||||
working_set.add_decl(Box::new(Echo));
|
||||
|
197
crates/nu-command/src/filters/merge.rs
Normal file
197
crates/nu-command/src/filters/merge.rs
Normal file
@ -0,0 +1,197 @@
|
||||
use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Merge;
|
||||
|
||||
impl Command for Merge {
|
||||
fn name(&self) -> &str {
|
||||
"merge"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Merge a table into an input table"
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("merge")
|
||||
.required(
|
||||
"block",
|
||||
SyntaxShape::Block(Some(vec![])),
|
||||
"the block to run and merge into the table",
|
||||
)
|
||||
.category(Category::Filters)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
example: "[a b c] | wrap name | merge { [1 2 3] | wrap index }",
|
||||
description: "Merge an index column into the input table",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(
|
||||
vec!["name", "index"],
|
||||
vec![Value::test_string("a"), Value::test_int(1)],
|
||||
),
|
||||
Value::test_record(
|
||||
vec!["name", "index"],
|
||||
vec![Value::test_string("b"), Value::test_int(2)],
|
||||
),
|
||||
Value::test_record(
|
||||
vec!["name", "index"],
|
||||
vec![Value::test_string("c"), Value::test_int(3)],
|
||||
),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
example: "{a: 1, b: 2} | merge { {c: 3} }",
|
||||
description: "Merge two records",
|
||||
result: Some(Value::test_record(
|
||||
vec!["a", "b", "c"],
|
||||
vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
|
||||
)),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let block: CaptureBlock = call.req(engine_state, stack, 0)?;
|
||||
let mut stack = stack.captures_to_stack(&block.captures);
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let block = engine_state.get_block(block.block_id);
|
||||
let call = call.clone();
|
||||
|
||||
let result = eval_block(
|
||||
engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
PipelineData::new(call.head),
|
||||
);
|
||||
|
||||
let table = match result {
|
||||
Ok(res) => res,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
match (&input, &table) {
|
||||
// table (list of records)
|
||||
(
|
||||
PipelineData::Value(Value::List { .. }, ..) | PipelineData::ListStream { .. },
|
||||
PipelineData::Value(Value::List { .. }, ..) | PipelineData::ListStream { .. },
|
||||
) => {
|
||||
let mut table_iter = table.into_iter();
|
||||
|
||||
Ok(input
|
||||
.into_iter()
|
||||
.map(move |inp| match (inp.as_record(), table_iter.next()) {
|
||||
(Ok((inp_cols, inp_vals)), Some(to_merge)) => match to_merge.as_record() {
|
||||
Ok((to_merge_cols, to_merge_vals)) => {
|
||||
let cols = [inp_cols, to_merge_cols].concat();
|
||||
let vals = [inp_vals, to_merge_vals].concat();
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: call.head,
|
||||
}
|
||||
}
|
||||
Err(error) => Value::Error { error },
|
||||
},
|
||||
(_, None) => inp,
|
||||
(Err(error), _) => Value::Error { error },
|
||||
})
|
||||
.into_pipeline_data(ctrlc))
|
||||
}
|
||||
// record
|
||||
(
|
||||
PipelineData::Value(
|
||||
Value::Record {
|
||||
cols: inp_cols,
|
||||
vals: inp_vals,
|
||||
..
|
||||
},
|
||||
..,
|
||||
),
|
||||
PipelineData::Value(
|
||||
Value::Record {
|
||||
cols: to_merge_cols,
|
||||
vals: to_merge_vals,
|
||||
..
|
||||
},
|
||||
..,
|
||||
),
|
||||
) => {
|
||||
let mut cols = inp_cols.to_vec();
|
||||
cols.extend(to_merge_cols.to_vec());
|
||||
|
||||
let mut vals = inp_vals.to_vec();
|
||||
vals.extend(to_merge_vals.to_vec());
|
||||
|
||||
Ok(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
(_, PipelineData::Value(val, ..)) | (PipelineData::Value(val, ..), _) => {
|
||||
let span = if val.span()? == Span::test_data() {
|
||||
Span::new(call.head.start, call.head.start)
|
||||
} else {
|
||||
val.span()?
|
||||
};
|
||||
|
||||
Err(ShellError::PipelineMismatch(
|
||||
"record or table in both the input and the argument block".to_string(),
|
||||
call.head,
|
||||
span,
|
||||
))
|
||||
}
|
||||
_ => Err(ShellError::PipelineMismatch(
|
||||
"record or table in both the input and the argument block".to_string(),
|
||||
call.head,
|
||||
Span::new(call.head.start, call.head.start),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn merge_values(
|
||||
left: &UntaggedValue,
|
||||
right: &UntaggedValue,
|
||||
) -> Result<UntaggedValue, (&'static str, &'static str)> {
|
||||
match (left, right) {
|
||||
(UntaggedValue::Row(columns), UntaggedValue::Row(columns_b)) => {
|
||||
Ok(UntaggedValue::Row(columns.merge_from(columns_b)))
|
||||
}
|
||||
(left, right) => Err((left.type_name(), right.type_name())),
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(Merge {})
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ mod keep;
|
||||
mod last;
|
||||
mod length;
|
||||
mod lines;
|
||||
mod merge;
|
||||
mod nth;
|
||||
mod par_each;
|
||||
mod prepend;
|
||||
@ -51,6 +52,7 @@ pub use keep::*;
|
||||
pub use last::Last;
|
||||
pub use length::Length;
|
||||
pub use lines::Lines;
|
||||
pub use merge::Merge;
|
||||
pub use nth::Nth;
|
||||
pub use par_each::ParEach;
|
||||
pub use prepend::Prepend;
|
||||
|
@ -3,7 +3,7 @@ use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature,
|
||||
SyntaxShape, Value,
|
||||
Span, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -25,10 +25,55 @@ impl Command for Zip {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
let test_row_1 = Value::List {
|
||||
vals: vec![
|
||||
Value::Int {
|
||||
val: 1,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Int {
|
||||
val: 4,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
|
||||
let test_row_2 = Value::List {
|
||||
vals: vec![
|
||||
Value::Int {
|
||||
val: 2,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Int {
|
||||
val: 5,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
|
||||
let test_row_3 = Value::List {
|
||||
vals: vec![
|
||||
Value::Int {
|
||||
val: 3,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Int {
|
||||
val: 6,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
|
||||
vec![Example {
|
||||
example: "1..3 | zip 4..6",
|
||||
description: "Zip multiple streams and get one of the results",
|
||||
result: None,
|
||||
result: Some(Value::List {
|
||||
vals: vec![test_row_1, test_row_2, test_row_3],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
}]
|
||||
}
|
||||
|
||||
|
@ -797,6 +797,15 @@ impl Value {
|
||||
span: Span::test_data(),
|
||||
}
|
||||
}
|
||||
|
||||
// Only use these for test data. Should not be used in user data
|
||||
pub fn test_record(cols: Vec<impl Into<String>>, vals: Vec<Value>) -> Value {
|
||||
Value::Record {
|
||||
cols: cols.into_iter().map(|s| s.into()).collect(),
|
||||
vals,
|
||||
span: Span::test_data(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
|
Loading…
Reference in New Issue
Block a user