mirror of
https://github.com/nushell/nushell.git
synced 2025-03-30 18:46:29 +02: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,
|
Get,
|
||||||
GroupBy,
|
GroupBy,
|
||||||
Keep,
|
Keep,
|
||||||
|
Merge,
|
||||||
KeepUntil,
|
KeepUntil,
|
||||||
KeepWhile,
|
KeepWhile,
|
||||||
Last,
|
Last,
|
||||||
|
@ -14,7 +14,7 @@ use crate::To;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use super::{
|
use super::{
|
||||||
Ansi, Date, From, If, Into, Math, Path, Random, Split, Str, StrCollect, StrFindReplace,
|
Ansi, Date, From, If, Into, Math, Path, Random, Split, Str, StrCollect, StrFindReplace,
|
||||||
StrLength, Url,
|
StrLength, Url, Wrap,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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(Date));
|
||||||
working_set.add_decl(Box::new(Url));
|
working_set.add_decl(Box::new(Url));
|
||||||
working_set.add_decl(Box::new(Ansi));
|
working_set.add_decl(Box::new(Ansi));
|
||||||
|
working_set.add_decl(Box::new(Wrap));
|
||||||
|
|
||||||
use super::Echo;
|
use super::Echo;
|
||||||
working_set.add_decl(Box::new(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 last;
|
||||||
mod length;
|
mod length;
|
||||||
mod lines;
|
mod lines;
|
||||||
|
mod merge;
|
||||||
mod nth;
|
mod nth;
|
||||||
mod par_each;
|
mod par_each;
|
||||||
mod prepend;
|
mod prepend;
|
||||||
@ -51,6 +52,7 @@ pub use keep::*;
|
|||||||
pub use last::Last;
|
pub use last::Last;
|
||||||
pub use length::Length;
|
pub use length::Length;
|
||||||
pub use lines::Lines;
|
pub use lines::Lines;
|
||||||
|
pub use merge::Merge;
|
||||||
pub use nth::Nth;
|
pub use nth::Nth;
|
||||||
pub use par_each::ParEach;
|
pub use par_each::ParEach;
|
||||||
pub use prepend::Prepend;
|
pub use prepend::Prepend;
|
||||||
|
@ -3,7 +3,7 @@ use nu_protocol::ast::Call;
|
|||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature,
|
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature,
|
||||||
SyntaxShape, Value,
|
Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -25,10 +25,55 @@ impl Command for Zip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
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 {
|
vec![Example {
|
||||||
example: "1..3 | zip 4..6",
|
example: "1..3 | zip 4..6",
|
||||||
description: "Zip multiple streams and get one of the results",
|
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(),
|
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 {
|
impl Default for Value {
|
||||||
|
Loading…
Reference in New Issue
Block a user