mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 07:00:37 +02:00
Create Record
type (#10103)
# Description This PR creates a new `Record` type to reduce duplicate code and possibly bugs as well. (This is an edited version of #9648.) - `Record` implements `FromIterator` and `IntoIterator` and so can be iterated over or collected into. For example, this helps with conversions to and from (hash)maps. (Also, no more `cols.iter().zip(vals)`!) - `Record` has a `push(col, val)` function to help insure that the number of columns is equal to the number of values. I caught a few potential bugs thanks to this (e.g. in the `ls` command). - Finally, this PR also adds a `record!` macro that helps simplify record creation. It is used like so: ```rust record! { "key1" => some_value, "key2" => Value::string("text", span), "key3" => Value::int(optional_int.unwrap_or(0), span), "key4" => Value::bool(config.setting, span), } ``` Since macros hinder formatting, etc., the right hand side values should be relatively short and sweet like the examples above. Where possible, prefer `record!` or `.collect()` on an iterator instead of multiple `Record::push`s, since the first two automatically set the record capacity and do less work overall. # User-Facing Changes Besides the changes in `nu-protocol` the only other breaking changes are to `nu-table::{ExpandedTable::build_map, JustTable::kv_table}`.
This commit is contained in:
@ -4,7 +4,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, Range, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Range, Record, ShellError, Signature, Span, SyntaxShape, Type,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -109,10 +110,10 @@ impl Command for BytesAt {
|
||||
Example {
|
||||
description: "Get the remaining characters from a starting index",
|
||||
example: " { data: 0x[33 44 55 10 01 13] } | bytes at 3.. data",
|
||||
result: Some(Value::test_record(
|
||||
vec!["data"],
|
||||
vec![Value::test_binary(vec![0x10, 0x01, 0x13])],
|
||||
)),
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["data".to_string()],
|
||||
vals: vec![Value::test_binary(vec![0x10, 0x01, 0x13])],
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Get the characters from the beginning until ending index",
|
||||
@ -127,7 +128,7 @@ impl Command for BytesAt {
|
||||
"Or the characters from the beginning until ending index inside a table",
|
||||
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes at 1.. ColB ColC"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::Binary {
|
||||
@ -143,8 +144,7 @@ impl Command for BytesAt {
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::{Call, CellPath};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
@ -112,7 +112,7 @@ impl Command for BytesIndexOf {
|
||||
description: "Returns index of pattern for specific column",
|
||||
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes index-of 0x[11] ColA ColC"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::test_int(0),
|
||||
@ -122,8 +122,7 @@ impl Command for BytesIndexOf {
|
||||
},
|
||||
Value::test_int(-1),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -3,8 +3,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
||||
Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
@ -95,8 +95,10 @@ impl Command for BytesRemove {
|
||||
Example {
|
||||
description: "Remove all occurrences of find binary in record field",
|
||||
example: "{ data: 0x[10 AA 10 BB 10] } | bytes remove -a 0x[10] data",
|
||||
result: Some(Value::test_record(vec!["data"],
|
||||
vec![Value::test_binary(vec![0xAA, 0xBB])])),
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["data".to_string()],
|
||||
vals: vec![Value::test_binary(vec![0xAA, 0xBB])]
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Remove occurrences of find binary from end",
|
||||
@ -110,7 +112,7 @@ impl Command for BytesRemove {
|
||||
description: "Remove all occurrences of find binary in table",
|
||||
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes remove 0x[11] ColA ColC",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::Binary {
|
||||
@ -125,9 +127,8 @@ impl Command for BytesRemove {
|
||||
val: vec![0x17, 0x18, 0x19],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
]
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -3,8 +3,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
||||
Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
@ -104,7 +104,7 @@ impl Command for BytesReplace {
|
||||
description: "Find and replace all occurrences of find binary in table",
|
||||
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes replace -a 0x[11] 0x[13] ColA ColC",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::Binary {
|
||||
@ -120,8 +120,7 @@ impl Command for BytesReplace {
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -4,11 +4,10 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::iter;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Histogram;
|
||||
@ -53,7 +52,7 @@ impl Command for Histogram {
|
||||
description: "Compute a histogram for a list of numbers",
|
||||
example: "[1 2 1] | histogram",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()],
|
||||
vals: vec![
|
||||
Value::test_int(1),
|
||||
@ -62,9 +61,8 @@ impl Command for Histogram {
|
||||
Value::test_string("66.67%"),
|
||||
Value::test_string("******************************************************************"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()],
|
||||
vals: vec![
|
||||
Value::test_int(2),
|
||||
@ -73,8 +71,7 @@ impl Command for Histogram {
|
||||
Value::test_string("33.33%"),
|
||||
Value::test_string("*********************************"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}
|
||||
),
|
||||
@ -195,8 +192,8 @@ fn run_histogram(
|
||||
for v in values {
|
||||
match v {
|
||||
// parse record, and fill valid value to actual input.
|
||||
Value::Record { cols, vals, .. } => {
|
||||
for (c, v) in iter::zip(cols, vals) {
|
||||
Value::Record { val, .. } => {
|
||||
for (c, v) in val {
|
||||
if &c == col_name {
|
||||
if let Ok(v) = HashableValue::from_value(v, head_span) {
|
||||
inputs.push(v);
|
||||
@ -272,23 +269,25 @@ fn histogram_impl(
|
||||
|
||||
result.push((
|
||||
count, // attach count first for easily sorting.
|
||||
Value::Record {
|
||||
cols: result_cols.clone(),
|
||||
vals: vec![
|
||||
val.into_value(),
|
||||
Value::Int { val: count, span },
|
||||
Value::Float {
|
||||
val: quantile,
|
||||
span,
|
||||
},
|
||||
Value::String {
|
||||
val: percentage,
|
||||
span,
|
||||
},
|
||||
Value::String { val: freq, span },
|
||||
],
|
||||
Value::record(
|
||||
Record {
|
||||
cols: result_cols.clone(),
|
||||
vals: vec![
|
||||
val.into_value(),
|
||||
Value::Int { val: count, span },
|
||||
Value::Float {
|
||||
val: quantile,
|
||||
span,
|
||||
},
|
||||
Value::String {
|
||||
val: percentage,
|
||||
span,
|
||||
},
|
||||
Value::String { val: freq, span },
|
||||
],
|
||||
},
|
||||
span,
|
||||
},
|
||||
),
|
||||
));
|
||||
}
|
||||
result.sort_by(|a, b| b.0.cmp(&a.0));
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -60,31 +60,26 @@ impl Command for SubCommand {
|
||||
example: "[[value]; ['false'] ['1'] [0] [1.0] [true]] | into bool value",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::bool(false, span)],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::bool(true, span)],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::bool(false, span)],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::bool(true, span)],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::bool(true, span)],
|
||||
span,
|
||||
},
|
||||
}),
|
||||
],
|
||||
span,
|
||||
}),
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -63,11 +63,10 @@ impl Command for SubCommand {
|
||||
description: "Convert string to decimal in table",
|
||||
example: "[[num]; ['5.01']] | into decimal num",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["num".to_string()],
|
||||
vals: vec![Value::test_float(5.01)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -3,7 +3,8 @@ use nu_parser::{parse_unit_value, DURATION_UNIT_GROUPS};
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath, Expr},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Unit, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Unit,
|
||||
Value,
|
||||
};
|
||||
|
||||
const NS_PER_SEC: i64 = 1_000_000_000;
|
||||
@ -80,46 +81,41 @@ impl Command for SubCommand {
|
||||
"[[value]; ['1sec'] ['2min'] ['3hr'] ['4day'] ['5wk']] | into duration value",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::Duration {
|
||||
val: NS_PER_SEC,
|
||||
span,
|
||||
}],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::Duration {
|
||||
val: 2 * 60 * NS_PER_SEC,
|
||||
span,
|
||||
}],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::Duration {
|
||||
val: 3 * 60 * 60 * NS_PER_SEC,
|
||||
span,
|
||||
}],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::Duration {
|
||||
val: 4 * 24 * 60 * 60 * NS_PER_SEC,
|
||||
span,
|
||||
}],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::Duration {
|
||||
val: 5 * 7 * 24 * 60 * 60 * NS_PER_SEC,
|
||||
span,
|
||||
}],
|
||||
span,
|
||||
},
|
||||
}),
|
||||
],
|
||||
span,
|
||||
}),
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -81,7 +81,7 @@ impl Command for SubCommand {
|
||||
example: r#"[[device size]; ["/dev/sda1" "200"] ["/dev/loop0" "50"]] | into filesize size"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["device".to_string(), "size".to_string()],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
@ -93,9 +93,8 @@ impl Command for SubCommand {
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["device".to_string(), "size".to_string()],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
@ -107,8 +106,7 @@ impl Command for SubCommand {
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
|
@ -1,10 +1,11 @@
|
||||
use chrono::{DateTime, Datelike, FixedOffset, Timelike};
|
||||
use nu_protocol::format_duration_as_timeperiod;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
format_duration_as_timeperiod, record, Category, Example, IntoPipelineData, PipelineData,
|
||||
Record, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
|
||||
@ -50,42 +51,39 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Convert from one row table to record",
|
||||
example: "[[value]; [false]] | into record",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::bool(false, span)],
|
||||
span,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Convert from list to record",
|
||||
example: "[1 2 3] | into record",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["0".to_string(), "1".to_string(), "2".to_string()],
|
||||
vals: vec![
|
||||
Value::Int { val: 1, span },
|
||||
Value::Int { val: 2, span },
|
||||
Value::Int { val: 3, span },
|
||||
],
|
||||
span,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Convert from range to record",
|
||||
example: "0..2 | into record",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["0".to_string(), "1".to_string(), "2".to_string()],
|
||||
vals: vec![
|
||||
Value::Int { val: 0, span },
|
||||
Value::Int { val: 1, span },
|
||||
Value::Int { val: 2, span },
|
||||
],
|
||||
span,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "convert duration to record (weeks max)",
|
||||
example: "(-500day - 4hr - 5sec) | into record",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"week".into(),
|
||||
"day".into(),
|
||||
@ -103,22 +101,20 @@ impl Command for SubCommand {
|
||||
span,
|
||||
},
|
||||
],
|
||||
span,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "convert record to record",
|
||||
example: "{a: 1, b: 2} | into record",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::Int { val: 1, span }, Value::Int { val: 2, span }],
|
||||
span,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "convert date to record",
|
||||
example: "2020-04-12T22:10:57+02:00 | into record",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"year".into(),
|
||||
"month".into(),
|
||||
@ -140,8 +136,7 @@ impl Command for SubCommand {
|
||||
span,
|
||||
},
|
||||
],
|
||||
span,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -155,34 +150,26 @@ fn into_record(
|
||||
let input = input.into_value(call.head);
|
||||
let input_type = input.get_type();
|
||||
let res = match input {
|
||||
Value::Date { val, span } => parse_date_into_record(Ok(val), span),
|
||||
Value::Date { val, span } => parse_date_into_record(val, span),
|
||||
Value::Duration { val, span } => parse_duration_into_record(val, span),
|
||||
Value::List { mut vals, span } => match input_type {
|
||||
Type::Table(..) if vals.len() == 1 => vals.pop().expect("already checked 1 item"),
|
||||
_ => {
|
||||
let mut cols = vec![];
|
||||
let mut values = vec![];
|
||||
for (idx, val) in vals.into_iter().enumerate() {
|
||||
cols.push(format!("{idx}"));
|
||||
values.push(val);
|
||||
}
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: values,
|
||||
span,
|
||||
}
|
||||
}
|
||||
_ => Value::record(
|
||||
vals.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, val)| (format!("{idx}"), val))
|
||||
.collect(),
|
||||
span,
|
||||
),
|
||||
},
|
||||
Value::Range { val, span } => {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
for (idx, val) in val.into_range_iter(engine_state.ctrlc.clone())?.enumerate() {
|
||||
cols.push(format!("{idx}"));
|
||||
vals.push(val);
|
||||
}
|
||||
Value::Record { cols, vals, span }
|
||||
}
|
||||
Value::Record { cols, vals, span } => Value::Record { cols, vals, span },
|
||||
Value::Range { val, span } => Value::record(
|
||||
val.into_range_iter(engine_state.ctrlc.clone())?
|
||||
.enumerate()
|
||||
.map(|(idx, val)| (format!("{idx}"), val))
|
||||
.collect(),
|
||||
span,
|
||||
),
|
||||
Value::Record { val, span } => Value::Record { val, span },
|
||||
Value::Error { .. } => input,
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
@ -196,87 +183,50 @@ fn into_record(
|
||||
Ok(res.into_pipeline_data())
|
||||
}
|
||||
|
||||
fn parse_date_into_record(date: Result<DateTime<FixedOffset>, Value>, span: Span) -> Value {
|
||||
let cols = vec![
|
||||
"year".into(),
|
||||
"month".into(),
|
||||
"day".into(),
|
||||
"hour".into(),
|
||||
"minute".into(),
|
||||
"second".into(),
|
||||
"timezone".into(),
|
||||
];
|
||||
match date {
|
||||
Ok(x) => {
|
||||
let vals = vec![
|
||||
Value::Int {
|
||||
val: x.year() as i64,
|
||||
span,
|
||||
},
|
||||
Value::Int {
|
||||
val: x.month() as i64,
|
||||
span,
|
||||
},
|
||||
Value::Int {
|
||||
val: x.day() as i64,
|
||||
span,
|
||||
},
|
||||
Value::Int {
|
||||
val: x.hour() as i64,
|
||||
span,
|
||||
},
|
||||
Value::Int {
|
||||
val: x.minute() as i64,
|
||||
span,
|
||||
},
|
||||
Value::Int {
|
||||
val: x.second() as i64,
|
||||
span,
|
||||
},
|
||||
Value::String {
|
||||
val: x.offset().to_string(),
|
||||
span,
|
||||
},
|
||||
];
|
||||
Value::Record { cols, vals, span }
|
||||
}
|
||||
Err(e) => e,
|
||||
}
|
||||
fn parse_date_into_record(date: DateTime<FixedOffset>, span: Span) -> Value {
|
||||
Value::record(
|
||||
record! {
|
||||
"year" => Value::int(date.year() as i64, span),
|
||||
"month" => Value::int(date.month() as i64, span),
|
||||
"day" => Value::int(date.day() as i64, span),
|
||||
"hour" => Value::int(date.hour() as i64, span),
|
||||
"minute" => Value::int(date.minute() as i64, span),
|
||||
"second" => Value::int(date.second() as i64, span),
|
||||
"timezone" => Value::string(date.offset().to_string(), span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_duration_into_record(duration: i64, span: Span) -> Value {
|
||||
let (sign, periods) = format_duration_as_timeperiod(duration);
|
||||
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
for p in periods {
|
||||
let num_with_unit = p.to_text().to_string();
|
||||
let split = num_with_unit.split(' ').collect::<Vec<&str>>();
|
||||
cols.push(match split[1] {
|
||||
"ns" => "nanosecond".into(),
|
||||
"µs" => "microsecond".into(),
|
||||
"ms" => "millisecond".into(),
|
||||
"sec" => "second".into(),
|
||||
"min" => "minute".into(),
|
||||
"hr" => "hour".into(),
|
||||
"day" => "day".into(),
|
||||
"wk" => "week".into(),
|
||||
_ => "unknown".into(),
|
||||
});
|
||||
|
||||
vals.push(Value::Int {
|
||||
val: split[0].parse::<i64>().unwrap_or(0),
|
||||
span,
|
||||
});
|
||||
record.push(
|
||||
match split[1] {
|
||||
"ns" => "nanosecond",
|
||||
"µs" => "microsecond",
|
||||
"ms" => "millisecond",
|
||||
"sec" => "second",
|
||||
"min" => "minute",
|
||||
"hr" => "hour",
|
||||
"day" => "day",
|
||||
"wk" => "week",
|
||||
_ => "unknown",
|
||||
},
|
||||
Value::int(split[0].parse().unwrap_or(0), span),
|
||||
);
|
||||
}
|
||||
|
||||
cols.push("sign".into());
|
||||
vals.push(Value::String {
|
||||
val: if sign == -1 { "-".into() } else { "+".into() },
|
||||
span,
|
||||
});
|
||||
record.push(
|
||||
"sign",
|
||||
Value::string(if sign == -1 { "-" } else { "+" }, span),
|
||||
);
|
||||
|
||||
Value::Record { cols, vals, span }
|
||||
Value::record(record, span)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -257,11 +257,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
val: "".to_string(),
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
cols: _,
|
||||
vals: _,
|
||||
span: _,
|
||||
} => Value::Error {
|
||||
Value::Record { .. } => Value::Error {
|
||||
// Watch out for CantConvert's argument order
|
||||
error: Box::new(ShellError::CantConvert {
|
||||
to_type: "string".into(),
|
||||
|
@ -7,7 +7,6 @@ use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
use std::iter;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -127,12 +126,9 @@ fn action(
|
||||
format!(
|
||||
"({})",
|
||||
match list_value {
|
||||
Value::Record {
|
||||
cols: _,
|
||||
vals,
|
||||
span: _,
|
||||
} => {
|
||||
vals.iter()
|
||||
Value::Record { val, .. } => {
|
||||
val.vals
|
||||
.iter()
|
||||
.map(|rec_val| {
|
||||
format!("'{}'", nu_value_to_string(rec_val.clone(), ""))
|
||||
})
|
||||
@ -249,14 +245,13 @@ fn nu_value_to_string(value: Value, separator: &str) -> String {
|
||||
nu_utils::strip_ansi_unlikely(&val).replace('\'', "''")
|
||||
}
|
||||
Value::List { vals: val, .. } => val
|
||||
.iter()
|
||||
.map(|x| nu_value_to_string(x.clone(), ", "))
|
||||
.into_iter()
|
||||
.map(|x| nu_value_to_string(x, ", "))
|
||||
.collect::<Vec<_>>()
|
||||
.join(separator),
|
||||
Value::Record { cols, vals, .. } => cols
|
||||
.iter()
|
||||
.zip(vals.iter())
|
||||
.map(|(x, y)| format!("{}: {}", x, nu_value_to_string(y.clone(), ", ")))
|
||||
Value::Record { val, .. } => val
|
||||
.into_iter()
|
||||
.map(|(x, y)| format!("{}: {}", x, nu_value_to_string(y, ", ")))
|
||||
.collect::<Vec<_>>()
|
||||
.join(separator),
|
||||
Value::LazyRecord { val, .. } => match val.collect() {
|
||||
@ -305,8 +300,8 @@ fn get_columns_with_sqlite_types(input: &[Value]) -> Vec<(String, String)> {
|
||||
// sqlite_type
|
||||
// );
|
||||
|
||||
if let Value::Record { cols, vals, .. } = item {
|
||||
for (c, v) in iter::zip(cols, vals) {
|
||||
if let Value::Record { val, .. } = item {
|
||||
for (c, v) in val {
|
||||
if !columns.iter().any(|(name, _)| name == c) {
|
||||
columns.push((
|
||||
c.to_string(),
|
||||
|
@ -3,7 +3,7 @@ use crate::database::values::definitions::{db_row::DbRow, db_table::DbTable};
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
record, Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
use rusqlite::Connection;
|
||||
#[derive(Clone)]
|
||||
@ -43,8 +43,6 @@ impl Command for SchemaDb {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let span = call.head;
|
||||
|
||||
let sqlite_db = SQLiteDatabase::try_from_pipeline(input, span)?;
|
||||
@ -59,58 +57,32 @@ impl Command for SchemaDb {
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut table_names = vec![];
|
||||
let mut table_values = vec![];
|
||||
let mut tables_record = Record::new();
|
||||
for table in tables {
|
||||
let column_info = get_table_columns(&sqlite_db, &conn, &table, span)?;
|
||||
let constraint_info = get_table_constraints(&sqlite_db, &conn, &table, span)?;
|
||||
let foreign_key_info = get_table_foreign_keys(&sqlite_db, &conn, &table, span)?;
|
||||
let index_info = get_table_indexes(&sqlite_db, &conn, &table, span)?;
|
||||
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
cols.push("columns".into());
|
||||
vals.push(Value::List {
|
||||
vals: column_info,
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("constraints".into());
|
||||
vals.push(Value::List {
|
||||
vals: constraint_info,
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("foreign_keys".into());
|
||||
vals.push(Value::List {
|
||||
vals: foreign_key_info,
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("indexes".into());
|
||||
vals.push(Value::List {
|
||||
vals: index_info,
|
||||
span,
|
||||
});
|
||||
|
||||
table_names.push(table.name);
|
||||
table_values.push(Value::Record { cols, vals, span });
|
||||
tables_record.push(
|
||||
table.name,
|
||||
Value::record(
|
||||
record! {
|
||||
"columns" => Value::list(column_info, span),
|
||||
"constraints" => Value::list(constraint_info, span),
|
||||
"foreign_keys" => Value::list(foreign_key_info, span),
|
||||
"indexes" => Value::list(index_info, span),
|
||||
},
|
||||
span,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
cols.push("tables".into());
|
||||
vals.push(Value::Record {
|
||||
cols: table_names,
|
||||
vals: table_values,
|
||||
span,
|
||||
});
|
||||
let record = record! { "tables" => Value::record(tables_record, span) };
|
||||
|
||||
// TODO: add views and triggers
|
||||
|
||||
Ok(PipelineData::Value(
|
||||
Value::Record { cols, vals, span },
|
||||
None,
|
||||
))
|
||||
Ok(PipelineData::Value(Value::record(record, span), None))
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,19 +117,14 @@ fn get_table_columns(
|
||||
// a record of column name = column value
|
||||
let mut column_info = vec![];
|
||||
for t in columns {
|
||||
let mut col_names = vec![];
|
||||
let mut col_values = vec![];
|
||||
let fields = t.fields();
|
||||
let columns = t.columns();
|
||||
for (k, v) in fields.iter().zip(columns.iter()) {
|
||||
col_names.push(k.clone());
|
||||
col_values.push(Value::string(v.clone(), span));
|
||||
}
|
||||
column_info.push(Value::Record {
|
||||
cols: col_names.clone(),
|
||||
vals: col_values.clone(),
|
||||
column_info.push(Value::record(
|
||||
t.fields()
|
||||
.into_iter()
|
||||
.zip(t.columns())
|
||||
.map(|(k, v)| (k, Value::string(v, span)))
|
||||
.collect(),
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
Ok(column_info)
|
||||
@ -180,19 +147,15 @@ fn get_table_constraints(
|
||||
})?;
|
||||
let mut constraint_info = vec![];
|
||||
for constraint in constraints {
|
||||
let mut con_cols = vec![];
|
||||
let mut con_vals = vec![];
|
||||
let fields = constraint.fields();
|
||||
let columns = constraint.columns();
|
||||
for (k, v) in fields.iter().zip(columns.iter()) {
|
||||
con_cols.push(k.clone());
|
||||
con_vals.push(Value::string(v.clone(), span));
|
||||
}
|
||||
constraint_info.push(Value::Record {
|
||||
cols: con_cols.clone(),
|
||||
vals: con_vals.clone(),
|
||||
constraint_info.push(Value::record(
|
||||
constraint
|
||||
.fields()
|
||||
.into_iter()
|
||||
.zip(constraint.columns())
|
||||
.map(|(k, v)| (k, Value::string(v, span)))
|
||||
.collect(),
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
Ok(constraint_info)
|
||||
@ -215,19 +178,14 @@ fn get_table_foreign_keys(
|
||||
})?;
|
||||
let mut foreign_key_info = vec![];
|
||||
for fk in foreign_keys {
|
||||
let mut fk_cols = vec![];
|
||||
let mut fk_vals = vec![];
|
||||
let fields = fk.fields();
|
||||
let columns = fk.columns();
|
||||
for (k, v) in fields.iter().zip(columns.iter()) {
|
||||
fk_cols.push(k.clone());
|
||||
fk_vals.push(Value::string(v.clone(), span));
|
||||
}
|
||||
foreign_key_info.push(Value::Record {
|
||||
cols: fk_cols.clone(),
|
||||
vals: fk_vals.clone(),
|
||||
foreign_key_info.push(Value::record(
|
||||
fk.fields()
|
||||
.into_iter()
|
||||
.zip(fk.columns())
|
||||
.map(|(k, v)| (k, Value::string(v, span)))
|
||||
.collect(),
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
Ok(foreign_key_info)
|
||||
@ -250,19 +208,15 @@ fn get_table_indexes(
|
||||
})?;
|
||||
let mut index_info = vec![];
|
||||
for index in indexes {
|
||||
let mut idx_cols = vec![];
|
||||
let mut idx_vals = vec![];
|
||||
let fields = index.fields();
|
||||
let columns = index.columns();
|
||||
for (k, v) in fields.iter().zip(columns.iter()) {
|
||||
idx_cols.push(k.clone());
|
||||
idx_vals.push(Value::string(v.clone(), span));
|
||||
}
|
||||
index_info.push(Value::Record {
|
||||
cols: idx_cols.clone(),
|
||||
vals: idx_vals.clone(),
|
||||
index_info.push(Value::record(
|
||||
index
|
||||
.fields()
|
||||
.into_iter()
|
||||
.zip(index.columns())
|
||||
.map(|(k, v)| (k, Value::string(v, span)))
|
||||
.collect(),
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
Ok(index_info)
|
||||
|
@ -3,7 +3,7 @@ use super::definitions::{
|
||||
db_index::DbIndex, db_table::DbTable,
|
||||
};
|
||||
|
||||
use nu_protocol::{CustomValue, PipelineData, ShellError, Span, Spanned, Value};
|
||||
use nu_protocol::{CustomValue, PipelineData, Record, ShellError, Span, Spanned, Value};
|
||||
use rusqlite::{types::ValueRef, Connection, Row};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
@ -415,8 +415,7 @@ fn read_entire_sqlite_db(
|
||||
call_span: Span,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
) -> Result<Value, rusqlite::Error> {
|
||||
let mut table_names: Vec<String> = Vec::new();
|
||||
let mut tables: Vec<Value> = Vec::new();
|
||||
let mut tables = Record::new();
|
||||
|
||||
let mut get_table_names =
|
||||
conn.prepare("SELECT name FROM sqlite_master WHERE type = 'table'")?;
|
||||
@ -424,18 +423,12 @@ fn read_entire_sqlite_db(
|
||||
|
||||
for row in rows {
|
||||
let table_name: String = row?;
|
||||
table_names.push(table_name.clone());
|
||||
|
||||
let table_stmt = conn.prepare(&format!("select * from [{table_name}]"))?;
|
||||
let rows = prepared_statement_to_nu_list(table_stmt, call_span, ctrlc.clone())?;
|
||||
tables.push(rows);
|
||||
tables.push(table_name, rows);
|
||||
}
|
||||
|
||||
Ok(Value::Record {
|
||||
cols: table_names,
|
||||
vals: tables,
|
||||
span: call_span,
|
||||
})
|
||||
Ok(Value::record(tables, call_span))
|
||||
}
|
||||
|
||||
pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, column_names: Vec<String>) -> Value {
|
||||
@ -446,11 +439,13 @@ pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, column_names: Vec<S
|
||||
vals.push(val);
|
||||
}
|
||||
|
||||
Value::Record {
|
||||
cols: column_names,
|
||||
vals,
|
||||
Value::record(
|
||||
Record {
|
||||
cols: column_names,
|
||||
vals,
|
||||
},
|
||||
span,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn convert_sqlite_value_to_nu_value(value: ValueRef, span: Span) -> Value {
|
||||
@ -488,11 +483,7 @@ mod test {
|
||||
let db = open_connection_in_memory().unwrap();
|
||||
let converted_db = read_entire_sqlite_db(db, Span::test_data(), None).unwrap();
|
||||
|
||||
let expected = Value::Record {
|
||||
cols: vec![],
|
||||
vals: vec![],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let expected = Value::test_record(Record::new());
|
||||
|
||||
assert_eq!(converted_db, expected);
|
||||
}
|
||||
@ -512,14 +503,13 @@ mod test {
|
||||
.unwrap();
|
||||
let converted_db = read_entire_sqlite_db(db, Span::test_data(), None).unwrap();
|
||||
|
||||
let expected = Value::Record {
|
||||
let expected = Value::test_record(Record {
|
||||
cols: vec!["person".to_string()],
|
||||
vals: vec![Value::List {
|
||||
vals: vec![],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
});
|
||||
|
||||
assert_eq!(converted_db, expected);
|
||||
}
|
||||
@ -546,16 +536,15 @@ mod test {
|
||||
|
||||
let converted_db = read_entire_sqlite_db(db, span, None).unwrap();
|
||||
|
||||
let expected = Value::Record {
|
||||
let expected = Value::test_record(Record {
|
||||
cols: vec!["item".to_string()],
|
||||
vals: vec![Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["id".to_string(), "name".to_string()],
|
||||
vals: vec![Value::Int { val: 123, span }, Value::Nothing { span }],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["id".to_string(), "name".to_string()],
|
||||
vals: vec![
|
||||
Value::Int { val: 456, span },
|
||||
@ -564,13 +553,11 @@ mod test {
|
||||
span,
|
||||
},
|
||||
],
|
||||
span,
|
||||
},
|
||||
}),
|
||||
],
|
||||
span,
|
||||
}],
|
||||
span,
|
||||
};
|
||||
});
|
||||
|
||||
assert_eq!(converted_db, expected);
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use chrono_tz::TZ_VARIANTS;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
Type, Value,
|
||||
record, Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError,
|
||||
Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -40,12 +40,10 @@ impl Command for SubCommand {
|
||||
Ok(TZ_VARIANTS
|
||||
.iter()
|
||||
.map(move |x| {
|
||||
let cols = vec!["timezone".into()];
|
||||
let vals = vec![Value::String {
|
||||
val: x.name().to_string(),
|
||||
Value::record(
|
||||
record! { "timezone" => Value::string(x.name(), span) },
|
||||
span,
|
||||
}];
|
||||
Value::Record { cols, vals, span }
|
||||
)
|
||||
})
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
@ -55,11 +53,10 @@ impl Command for SubCommand {
|
||||
example: "date list-timezone | where timezone =~ Shanghai",
|
||||
description: "Show timezone(s) that contains 'Shanghai'",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["timezone".into()],
|
||||
vals: vec![Value::test_string("Asia/Shanghai")],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
}]
|
||||
|
@ -3,10 +3,9 @@ use chrono::{DateTime, Datelike, FixedOffset, Local, Timelike};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError::DatetimeParseError, ShellError::PipelineEmpty,
|
||||
Signature, Span, Value,
|
||||
record, Category, Example, PipelineData, Record, ShellError, ShellError::DatetimeParseError,
|
||||
ShellError::PipelineEmpty, Signature, Span, Type, Value,
|
||||
};
|
||||
use nu_protocol::{ShellError, Type};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -78,7 +77,7 @@ impl Command for SubCommand {
|
||||
span,
|
||||
},
|
||||
];
|
||||
Some(Value::Record { cols, vals, span })
|
||||
Some(Value::test_record(Record { cols, vals }))
|
||||
};
|
||||
|
||||
let example_result_2 = || {
|
||||
@ -106,7 +105,7 @@ impl Command for SubCommand {
|
||||
span,
|
||||
},
|
||||
];
|
||||
Some(Value::Record { cols, vals, span })
|
||||
Some(Value::test_record(Record { cols, vals }))
|
||||
};
|
||||
|
||||
vec![
|
||||
@ -134,37 +133,20 @@ impl Command for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_date_into_table(date: Result<DateTime<FixedOffset>, Value>, head: Span) -> Value {
|
||||
let cols = vec![
|
||||
"year".into(),
|
||||
"month".into(),
|
||||
"day".into(),
|
||||
"hour".into(),
|
||||
"minute".into(),
|
||||
"second".into(),
|
||||
"nanosecond".into(),
|
||||
"timezone".into(),
|
||||
];
|
||||
match date {
|
||||
Ok(x) => {
|
||||
let vals = vec![
|
||||
Value::int(x.year() as i64, head),
|
||||
Value::int(x.month() as i64, head),
|
||||
Value::int(x.day() as i64, head),
|
||||
Value::int(x.hour() as i64, head),
|
||||
Value::int(x.minute() as i64, head),
|
||||
Value::int(x.second() as i64, head),
|
||||
Value::int(x.nanosecond() as i64, head),
|
||||
Value::string(x.offset().to_string(), head),
|
||||
];
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
}
|
||||
}
|
||||
Err(e) => e,
|
||||
}
|
||||
fn parse_date_into_table(date: DateTime<FixedOffset>, head: Span) -> Value {
|
||||
Value::record(
|
||||
record! {
|
||||
"year" => Value::int(date.year() as i64, head),
|
||||
"month" => Value::int(date.month() as i64, head),
|
||||
"day" => Value::int(date.day() as i64, head),
|
||||
"hour" => Value::int(date.hour() as i64, head),
|
||||
"minute" => Value::int(date.minute() as i64, head),
|
||||
"second" => Value::int(date.second() as i64, head),
|
||||
"nanosecond" => Value::int(date.nanosecond() as i64, head),
|
||||
"timezone" => Value::string(date.offset().to_string(), head),
|
||||
},
|
||||
head,
|
||||
)
|
||||
}
|
||||
|
||||
fn helper(val: Value, head: Span) -> Value {
|
||||
@ -172,16 +154,16 @@ fn helper(val: Value, head: Span) -> Value {
|
||||
Value::String {
|
||||
val,
|
||||
span: val_span,
|
||||
} => {
|
||||
let date = parse_date_from_string(&val, val_span);
|
||||
parse_date_into_table(date, head)
|
||||
}
|
||||
} => match parse_date_from_string(&val, val_span) {
|
||||
Ok(date) => parse_date_into_table(date, head),
|
||||
Err(e) => e,
|
||||
},
|
||||
Value::Nothing { span: _ } => {
|
||||
let now = Local::now();
|
||||
let n = now.with_timezone(now.offset());
|
||||
parse_date_into_table(Ok(n), head)
|
||||
parse_date_into_table(n, head)
|
||||
}
|
||||
Value::Date { val, span: _ } => parse_date_into_table(Ok(val), head),
|
||||
Value::Date { val, span: _ } => parse_date_into_table(val, head),
|
||||
_ => Value::Error {
|
||||
error: Box::new(DatetimeParseError(val.debug_value(), head)),
|
||||
},
|
||||
|
@ -3,10 +3,9 @@ use chrono::{DateTime, Datelike, FixedOffset, Local, Timelike};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError::DatetimeParseError, ShellError::PipelineEmpty,
|
||||
Signature, Span, Value,
|
||||
record, Category, Example, PipelineData, Record, ShellError, ShellError::DatetimeParseError,
|
||||
ShellError::PipelineEmpty, Signature, Span, Type, Value,
|
||||
};
|
||||
use nu_protocol::{ShellError, Type};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -76,7 +75,7 @@ impl Command for SubCommand {
|
||||
},
|
||||
];
|
||||
Some(Value::List {
|
||||
vals: vec![Value::Record { cols, vals, span }],
|
||||
vals: vec![Value::test_record(Record { cols, vals })],
|
||||
span,
|
||||
})
|
||||
};
|
||||
@ -107,7 +106,7 @@ impl Command for SubCommand {
|
||||
},
|
||||
];
|
||||
Some(Value::List {
|
||||
vals: vec![Value::Record { cols, vals, span }],
|
||||
vals: vec![Value::test_record(Record { cols, vals })],
|
||||
span,
|
||||
})
|
||||
};
|
||||
@ -137,40 +136,19 @@ impl Command for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_date_into_table(date: Result<DateTime<FixedOffset>, Value>, head: Span) -> Value {
|
||||
let cols = vec![
|
||||
"year".into(),
|
||||
"month".into(),
|
||||
"day".into(),
|
||||
"hour".into(),
|
||||
"minute".into(),
|
||||
"second".into(),
|
||||
"nanosecond".into(),
|
||||
"timezone".into(),
|
||||
];
|
||||
match date {
|
||||
Ok(x) => {
|
||||
let vals = vec![
|
||||
Value::int(x.year() as i64, head),
|
||||
Value::int(x.month() as i64, head),
|
||||
Value::int(x.day() as i64, head),
|
||||
Value::int(x.hour() as i64, head),
|
||||
Value::int(x.minute() as i64, head),
|
||||
Value::int(x.second() as i64, head),
|
||||
Value::int(x.nanosecond() as i64, head),
|
||||
Value::string(x.offset().to_string(), head),
|
||||
];
|
||||
Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
}],
|
||||
span: head,
|
||||
}
|
||||
}
|
||||
Err(e) => e,
|
||||
}
|
||||
fn parse_date_into_table(date: DateTime<FixedOffset>, head: Span) -> Value {
|
||||
let record = record! {
|
||||
"year" => Value::int(date.year() as i64, head),
|
||||
"month" => Value::int(date.month() as i64, head),
|
||||
"day" => Value::int(date.day() as i64, head),
|
||||
"hour" => Value::int(date.hour() as i64, head),
|
||||
"minute" => Value::int(date.minute() as i64, head),
|
||||
"second" => Value::int(date.second() as i64, head),
|
||||
"nanosecond" => Value::int(date.nanosecond() as i64, head),
|
||||
"timezone" => Value::string(date.offset().to_string(), head),
|
||||
};
|
||||
|
||||
Value::list(vec![Value::record(record, head)], head)
|
||||
}
|
||||
|
||||
fn helper(val: Value, head: Span) -> Value {
|
||||
@ -178,16 +156,16 @@ fn helper(val: Value, head: Span) -> Value {
|
||||
Value::String {
|
||||
val,
|
||||
span: val_span,
|
||||
} => {
|
||||
let date = parse_date_from_string(&val, val_span);
|
||||
parse_date_into_table(date, head)
|
||||
}
|
||||
} => match parse_date_from_string(&val, val_span) {
|
||||
Ok(date) => parse_date_into_table(date, head),
|
||||
Err(e) => e,
|
||||
},
|
||||
Value::Nothing { span: _ } => {
|
||||
let now = Local::now();
|
||||
let n = now.with_timezone(now.offset());
|
||||
parse_date_into_table(Ok(n), head)
|
||||
parse_date_into_table(n, head)
|
||||
}
|
||||
Value::Date { val, span: _ } => parse_date_into_table(Ok(val), head),
|
||||
Value::Date { val, span: _ } => parse_date_into_table(val, head),
|
||||
_ => Value::Error {
|
||||
error: Box::new(DatetimeParseError(val.debug_value(), head)),
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
use chrono::{DateTime, FixedOffset, Local, LocalResult, TimeZone};
|
||||
use nu_protocol::{ShellError, Span, Value};
|
||||
use nu_protocol::{record, ShellError, Span, Value};
|
||||
|
||||
pub(crate) fn parse_date_from_string(
|
||||
input: &str,
|
||||
@ -31,11 +31,6 @@ pub(crate) fn parse_date_from_string(
|
||||
/// * `head` - use the call's head
|
||||
/// * `show_parse_only_formats` - whether parse-only format specifiers (that can't be outputted) should be shown. Should only be used for `into datetime`, not `format date`
|
||||
pub(crate) fn generate_strftime_list(head: Span, show_parse_only_formats: bool) -> Value {
|
||||
let column_names = vec![
|
||||
"Specification".into(),
|
||||
"Example".into(),
|
||||
"Description".into(),
|
||||
];
|
||||
let now = Local::now();
|
||||
|
||||
struct FormatSpecification<'a> {
|
||||
@ -258,14 +253,15 @@ pub(crate) fn generate_strftime_list(head: Span, show_parse_only_formats: bool)
|
||||
|
||||
let mut records = specifications
|
||||
.iter()
|
||||
.map(|s| Value::Record {
|
||||
cols: column_names.clone(),
|
||||
vals: vec![
|
||||
Value::string(s.spec, head),
|
||||
Value::string(now.format(s.spec).to_string(), head),
|
||||
Value::string(s.description, head),
|
||||
],
|
||||
span: head,
|
||||
.map(|s| {
|
||||
Value::record(
|
||||
record! {
|
||||
"Specification" => Value::string(s.spec, head),
|
||||
"Example" => Value::string(now.format(s.spec).to_string(), head),
|
||||
"Description" => Value::string(s.description, head),
|
||||
},
|
||||
head,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Value>>();
|
||||
|
||||
@ -279,21 +275,16 @@ pub(crate) fn generate_strftime_list(head: Span, show_parse_only_formats: bool)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
|
||||
records.push(Value::Record {
|
||||
cols: column_names,
|
||||
vals: vec![
|
||||
Value::string("%#z", head),
|
||||
Value::String {
|
||||
val: example,
|
||||
span: head,
|
||||
},
|
||||
Value::string(
|
||||
"Parsing only: Same as %z but allows minutes to be missing or present.",
|
||||
head,
|
||||
),
|
||||
],
|
||||
span: head,
|
||||
});
|
||||
let description = "Parsing only: Same as %z but allows minutes to be missing or present.";
|
||||
|
||||
records.push(Value::record(
|
||||
record! {
|
||||
"Specification" => Value::string("%#z", head),
|
||||
"Example" => Value::string(example, head),
|
||||
"Description" => Value::string(description, head),
|
||||
},
|
||||
head,
|
||||
));
|
||||
}
|
||||
|
||||
Value::List {
|
||||
|
@ -3,8 +3,8 @@ use nu_parser::parse;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack, StateWorkingSet},
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
|
||||
Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -88,14 +88,13 @@ impl Command for Ast {
|
||||
};
|
||||
|
||||
// Create a new output record, merging the block and error
|
||||
let output_record = Value::Record {
|
||||
cols: vec!["block".to_string(), "error".to_string()],
|
||||
vals: vec![
|
||||
Value::string(block_json, *block_span),
|
||||
Value::string(error_json, Span::test_data()),
|
||||
],
|
||||
span: pipeline.span,
|
||||
};
|
||||
let output_record = Value::record(
|
||||
record! {
|
||||
"block" => Value::string(block_json, *block_span),
|
||||
"error" => Value::string(error_json, Span::test_data()),
|
||||
},
|
||||
pipeline.span,
|
||||
);
|
||||
Ok(output_record.into_pipeline_data())
|
||||
} else {
|
||||
let block_value = Value::String {
|
||||
@ -114,11 +113,13 @@ impl Command for Ast {
|
||||
},
|
||||
span: pipeline.span,
|
||||
};
|
||||
let output_record = Value::Record {
|
||||
cols: vec!["block".to_string(), "error".to_string()],
|
||||
vals: vec![block_value, error_value],
|
||||
span: pipeline.span,
|
||||
};
|
||||
let output_record = Value::record(
|
||||
record! {
|
||||
"block" => block_value,
|
||||
"error" => error_value
|
||||
},
|
||||
pipeline.span,
|
||||
);
|
||||
Ok(output_record.into_pipeline_data())
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_expression, CallExt};
|
||||
use nu_protocol::ast::{Argument, Block, Call, Expr, Expression};
|
||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
record, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature,
|
||||
Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -91,29 +91,15 @@ pub fn get_pipeline_elements(
|
||||
let value_span_end = value_span.end as i64;
|
||||
let command_name = command_name;
|
||||
|
||||
let rec = Value::Record {
|
||||
cols: vec![
|
||||
"cmd_index".to_string(),
|
||||
"cmd_name".to_string(),
|
||||
"type".to_string(),
|
||||
"cmd_args".to_string(),
|
||||
"span_start".to_string(),
|
||||
"span_end".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(index, span),
|
||||
Value::string(command_name, value_span),
|
||||
Value::string(value_type.to_string(), span),
|
||||
Value::List {
|
||||
vals: command_args_value,
|
||||
span: value_span,
|
||||
},
|
||||
Value::int(value_span_start, span),
|
||||
Value::int(value_span_end, span),
|
||||
],
|
||||
span: value_span,
|
||||
let record = record! {
|
||||
"cmd_index" => Value::string(index, span),
|
||||
"cmd_name" => Value::string(command_name, value_span),
|
||||
"type" => Value::string(value_type.to_string(), span),
|
||||
"cmd_args" => Value::list(command_args_value, value_span),
|
||||
"span_start" => Value::int(value_span_start, span),
|
||||
"span_end" => Value::int(value_span_end, span),
|
||||
};
|
||||
element_values.push(rec);
|
||||
element_values.push(Value::record(record, value_span));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
@ -133,24 +119,14 @@ fn get_arguments(engine_state: &EngineState, stack: &mut Stack, call: Call) -> V
|
||||
let arg_value_name_span_start = name.span.start as i64;
|
||||
let arg_value_name_span_end = name.span.end as i64;
|
||||
|
||||
let rec = Value::Record {
|
||||
cols: vec![
|
||||
"arg_type".to_string(),
|
||||
"name".to_string(),
|
||||
"type".to_string(),
|
||||
"span_start".to_string(),
|
||||
"span_end".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(arg_type, span),
|
||||
Value::string(arg_value_name, name.span),
|
||||
Value::string("string".to_string(), span),
|
||||
Value::int(arg_value_name_span_start, span),
|
||||
Value::int(arg_value_name_span_end, span),
|
||||
],
|
||||
span: name.span,
|
||||
let record = record! {
|
||||
"arg_type" => Value::string(arg_type, span),
|
||||
"name" => Value::string(arg_value_name, name.span),
|
||||
"type" => Value::string("string", span),
|
||||
"span_start" => Value::int(arg_value_name_span_start, span),
|
||||
"span_end" => Value::int(arg_value_name_span_end, span),
|
||||
};
|
||||
arg_value.push(rec);
|
||||
arg_value.push(Value::record(record, name.span));
|
||||
|
||||
if let Some(shortcut) = short {
|
||||
let arg_type = "short";
|
||||
@ -158,24 +134,14 @@ fn get_arguments(engine_state: &EngineState, stack: &mut Stack, call: Call) -> V
|
||||
let arg_value_name_span_start = shortcut.span.start as i64;
|
||||
let arg_value_name_span_end = shortcut.span.end as i64;
|
||||
|
||||
let rec = Value::Record {
|
||||
cols: vec![
|
||||
"arg_type".to_string(),
|
||||
"name".to_string(),
|
||||
"type".to_string(),
|
||||
"span_start".to_string(),
|
||||
"span_end".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(arg_type, span),
|
||||
Value::string(arg_value_name, shortcut.span),
|
||||
Value::string("string".to_string(), span),
|
||||
Value::int(arg_value_name_span_start, span),
|
||||
Value::int(arg_value_name_span_end, span),
|
||||
],
|
||||
span: name.span,
|
||||
let record = record! {
|
||||
"arg_type" => Value::string(arg_type, span),
|
||||
"name" => Value::string(arg_value_name, shortcut.span),
|
||||
"type" => Value::string("string", span),
|
||||
"span_start" => Value::int(arg_value_name_span_start, span),
|
||||
"span_end" => Value::int(arg_value_name_span_end, span),
|
||||
};
|
||||
arg_value.push(rec);
|
||||
arg_value.push(Value::record(record, name.span));
|
||||
};
|
||||
|
||||
if let Some(expression) = opt_expr {
|
||||
@ -188,24 +154,14 @@ fn get_arguments(engine_state: &EngineState, stack: &mut Stack, call: Call) -> V
|
||||
let arg_value_name_span_start = evaled_span.start as i64;
|
||||
let arg_value_name_span_end = evaled_span.end as i64;
|
||||
|
||||
let rec = Value::Record {
|
||||
cols: vec![
|
||||
"arg_type".to_string(),
|
||||
"name".to_string(),
|
||||
"type".to_string(),
|
||||
"span_start".to_string(),
|
||||
"span_end".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(arg_type, span),
|
||||
Value::string(arg_value_name, expression.span),
|
||||
Value::string(arg_value_type, span),
|
||||
Value::int(arg_value_name_span_start, span),
|
||||
Value::int(arg_value_name_span_end, span),
|
||||
],
|
||||
span: expression.span,
|
||||
let record = record! {
|
||||
"arg_type" => Value::string(arg_type, span),
|
||||
"name" => Value::string(arg_value_name, expression.span),
|
||||
"type" => Value::string(arg_value_type, span),
|
||||
"span_start" => Value::int(arg_value_name_span_start, span),
|
||||
"span_end" => Value::int(arg_value_name_span_end, span),
|
||||
};
|
||||
arg_value.push(rec);
|
||||
arg_value.push(Value::record(record, expression.span));
|
||||
};
|
||||
}
|
||||
Argument::Positional(inner_expr) => {
|
||||
@ -217,24 +173,14 @@ fn get_arguments(engine_state: &EngineState, stack: &mut Stack, call: Call) -> V
|
||||
let arg_value_name_span_start = evaled_span.start as i64;
|
||||
let arg_value_name_span_end = evaled_span.end as i64;
|
||||
|
||||
let rec = Value::Record {
|
||||
cols: vec![
|
||||
"arg_type".to_string(),
|
||||
"name".to_string(),
|
||||
"type".to_string(),
|
||||
"span_start".to_string(),
|
||||
"span_end".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(arg_type, span),
|
||||
Value::string(arg_value_name, inner_expr.span),
|
||||
Value::string(arg_value_type, span),
|
||||
Value::int(arg_value_name_span_start, span),
|
||||
Value::int(arg_value_name_span_end, span),
|
||||
],
|
||||
span: inner_expr.span,
|
||||
let record = record! {
|
||||
"arg_type" => Value::string(arg_type, span),
|
||||
"name" => Value::string(arg_value_name, inner_expr.span),
|
||||
"type" => Value::string(arg_value_type, span),
|
||||
"span_start" => Value::int(arg_value_name_span_start, span),
|
||||
"span_end" => Value::int(arg_value_name_span_end, span),
|
||||
};
|
||||
arg_value.push(rec);
|
||||
arg_value.push(Value::record(record, inner_expr.span));
|
||||
}
|
||||
Argument::Unknown(inner_expr) => {
|
||||
let arg_type = "unknown";
|
||||
@ -245,24 +191,14 @@ fn get_arguments(engine_state: &EngineState, stack: &mut Stack, call: Call) -> V
|
||||
let arg_value_name_span_start = evaled_span.start as i64;
|
||||
let arg_value_name_span_end = evaled_span.end as i64;
|
||||
|
||||
let rec = Value::Record {
|
||||
cols: vec![
|
||||
"arg_type".to_string(),
|
||||
"name".to_string(),
|
||||
"type".to_string(),
|
||||
"span_start".to_string(),
|
||||
"span_end".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(arg_type, span),
|
||||
Value::string(arg_value_name, inner_expr.span),
|
||||
Value::string(arg_value_type, span),
|
||||
Value::int(arg_value_name_span_start, span),
|
||||
Value::int(arg_value_name_span_end, span),
|
||||
],
|
||||
span: inner_expr.span,
|
||||
let record = record! {
|
||||
"arg_type" => Value::string(arg_type, span),
|
||||
"name" => Value::string(arg_value_name, inner_expr.span),
|
||||
"type" => Value::string(arg_value_type, span),
|
||||
"span_start" => Value::int(arg_value_name_span_start, span),
|
||||
"span_end" => Value::int(arg_value_name_span_end, span),
|
||||
};
|
||||
arg_value.push(rec);
|
||||
arg_value.push(Value::record(record, inner_expr.span));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -306,10 +242,9 @@ pub fn debug_string_without_formatting(value: &Value) -> String {
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
),
|
||||
Value::Record { cols, vals, .. } => format!(
|
||||
Value::Record { val, .. } => format!(
|
||||
"{{{}}}",
|
||||
cols.iter()
|
||||
.zip(vals.iter())
|
||||
val.iter()
|
||||
.map(|(x, y)| format!("{}: {}", x, debug_string_without_formatting(y)))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
|
@ -198,9 +198,10 @@ mod util {
|
||||
/// Try to build column names and a table grid.
|
||||
pub fn collect_input(value: Value) -> (Vec<String>, Vec<Vec<String>>) {
|
||||
match value {
|
||||
Value::Record { cols, vals, .. } => (
|
||||
cols,
|
||||
vec![vals
|
||||
Value::Record { val: record, .. } => (
|
||||
record.cols,
|
||||
vec![record
|
||||
.vals
|
||||
.into_iter()
|
||||
.map(|s| debug_string_without_formatting(&s))
|
||||
.collect()],
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::{Call, Expr, Expression};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, DataSource, Example, IntoPipelineData, PipelineData, PipelineMetadata, ShellError,
|
||||
Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, DataSource, Example, IntoPipelineData, PipelineData, PipelineMetadata,
|
||||
Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -73,37 +73,22 @@ impl Command for Metadata {
|
||||
Ok(build_metadata_record(&val, &input.metadata(), head).into_pipeline_data())
|
||||
}
|
||||
None => {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
if let Some(x) = input.metadata().as_deref() {
|
||||
match x {
|
||||
PipelineMetadata {
|
||||
data_source: DataSource::Ls,
|
||||
} => {
|
||||
cols.push("source".into());
|
||||
vals.push(Value::string("ls", head))
|
||||
}
|
||||
} => record.push("source", Value::string("ls", head)),
|
||||
PipelineMetadata {
|
||||
data_source: DataSource::HtmlThemes,
|
||||
} => {
|
||||
cols.push("source".into());
|
||||
vals.push(Value::string("into html --list", head))
|
||||
}
|
||||
} => record.push("source", Value::string("into html --list", head)),
|
||||
PipelineMetadata {
|
||||
data_source: DataSource::Profiling(values),
|
||||
} => {
|
||||
cols.push("profiling".into());
|
||||
vals.push(Value::list(values.clone(), head))
|
||||
}
|
||||
} => record.push("profiling", Value::list(values.clone(), head)),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
Ok(Value::record(record, head).into_pipeline_data())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,55 +114,36 @@ fn build_metadata_record(
|
||||
metadata: &Option<Box<PipelineMetadata>>,
|
||||
head: Span,
|
||||
) -> Value {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
|
||||
if let Ok(span) = arg.span() {
|
||||
cols.push("span".into());
|
||||
vals.push(Value::Record {
|
||||
cols: vec!["start".into(), "end".into()],
|
||||
vals: vec![
|
||||
Value::Int {
|
||||
val: span.start as i64,
|
||||
span,
|
||||
record.push(
|
||||
"span",
|
||||
Value::record(
|
||||
record! {
|
||||
"start" => Value::int(span.start as i64,span),
|
||||
"end" => Value::int(span.end as i64, span),
|
||||
},
|
||||
Value::Int {
|
||||
val: span.end as i64,
|
||||
span,
|
||||
},
|
||||
],
|
||||
span: head,
|
||||
});
|
||||
head,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(x) = metadata.as_deref() {
|
||||
match x {
|
||||
PipelineMetadata {
|
||||
data_source: DataSource::Ls,
|
||||
} => {
|
||||
cols.push("source".into());
|
||||
vals.push(Value::string("ls", head))
|
||||
}
|
||||
} => record.push("source", Value::string("ls", head)),
|
||||
PipelineMetadata {
|
||||
data_source: DataSource::HtmlThemes,
|
||||
} => {
|
||||
cols.push("source".into());
|
||||
vals.push(Value::string("into html --list", head))
|
||||
}
|
||||
} => record.push("source", Value::string("into html --list", head)),
|
||||
PipelineMetadata {
|
||||
data_source: DataSource::Profiling(values),
|
||||
} => {
|
||||
cols.push("profiling".into());
|
||||
vals.push(Value::list(values.clone(), head))
|
||||
}
|
||||
} => record.push("profiling", Value::list(values.clone(), head)),
|
||||
}
|
||||
}
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
}
|
||||
Value::record(record, head)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,7 +1,7 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -36,21 +36,15 @@ impl Command for ViewFiles {
|
||||
let mut records = vec![];
|
||||
|
||||
for (file, start, end) in engine_state.files() {
|
||||
records.push(Value::Record {
|
||||
cols: vec![
|
||||
"filename".to_string(),
|
||||
"start".to_string(),
|
||||
"end".to_string(),
|
||||
"size".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(file, call.head),
|
||||
Value::int(*start as i64, call.head),
|
||||
Value::int(*end as i64, call.head),
|
||||
Value::int(*end as i64 - *start as i64, call.head),
|
||||
],
|
||||
span: call.head,
|
||||
});
|
||||
records.push(Value::record(
|
||||
record! {
|
||||
"filename" => Value::string(file, call.head),
|
||||
"start" => Value::int(*start as i64, call.head),
|
||||
"end" => Value::int(*end as i64, call.head),
|
||||
"size" => Value::int(*end as i64 - *start as i64, call.head),
|
||||
},
|
||||
call.head,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Value::List {
|
||||
|
12
crates/nu-command/src/env/load_env.rs
vendored
12
crates/nu-command/src/env/load_env.rs
vendored
@ -2,7 +2,7 @@ use nu_engine::{current_dir, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -39,12 +39,12 @@ impl Command for LoadEnv {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let arg: Option<(Vec<String>, Vec<Value>)> = call.opt(engine_state, stack, 0)?;
|
||||
let arg: Option<Record> = call.opt(engine_state, stack, 0)?;
|
||||
let span = call.head;
|
||||
|
||||
match arg {
|
||||
Some((cols, vals)) => {
|
||||
for (env_var, rhs) in cols.into_iter().zip(vals) {
|
||||
Some(record) => {
|
||||
for (env_var, rhs) in record {
|
||||
let env_var_ = env_var.as_str();
|
||||
if ["FILE_PWD", "CURRENT_FILE", "PWD"].contains(&env_var_) {
|
||||
return Err(ShellError::AutomaticEnvVarSetManually {
|
||||
@ -57,8 +57,8 @@ impl Command for LoadEnv {
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
None => match input {
|
||||
PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
|
||||
for (env_var, rhs) in cols.into_iter().zip(vals) {
|
||||
PipelineData::Value(Value::Record { val, .. }, ..) => {
|
||||
for (env_var, rhs) in val {
|
||||
let env_var_ = env_var.as_str();
|
||||
if ["FILE_PWD", "CURRENT_FILE"].contains(&env_var_) {
|
||||
return Err(ShellError::AutomaticEnvVarSetManually {
|
||||
|
8
crates/nu-command/src/env/with_env.rs
vendored
8
crates/nu-command/src/env/with_env.rs
vendored
@ -94,8 +94,8 @@ fn with_env(
|
||||
if table.len() == 1 {
|
||||
// single row([[X W]; [Y Z]])
|
||||
match &table[0] {
|
||||
Value::Record { cols, vals, .. } => {
|
||||
for (k, v) in cols.iter().zip(vals.iter()) {
|
||||
Value::Record { val, .. } => {
|
||||
for (k, v) in val {
|
||||
env.insert(k.to_string(), v.clone());
|
||||
}
|
||||
}
|
||||
@ -122,8 +122,8 @@ fn with_env(
|
||||
}
|
||||
}
|
||||
// when get object by `open x.json` or `from json`
|
||||
Value::Record { cols, vals, .. } => {
|
||||
for (k, v) in cols.iter().zip(vals) {
|
||||
Value::Record { val, .. } => {
|
||||
for (k, v) in val {
|
||||
env.insert(k.clone(), v.clone());
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, DataSource, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
PipelineMetadata, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||
PipelineMetadata, Record, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
use pathdiff::diff_paths;
|
||||
|
||||
@ -431,219 +431,155 @@ pub(crate) fn dir_entry_dict(
|
||||
));
|
||||
}
|
||||
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
let mut file_type = "unknown".to_string();
|
||||
|
||||
cols.push("name".into());
|
||||
vals.push(Value::String {
|
||||
val: display_name.to_string(),
|
||||
span,
|
||||
});
|
||||
record.push("name", Value::string(display_name, span));
|
||||
|
||||
if let Some(md) = metadata {
|
||||
file_type = get_file_type(md, display_name, use_mime_type);
|
||||
cols.push("type".into());
|
||||
vals.push(Value::String {
|
||||
val: file_type.clone(),
|
||||
span,
|
||||
});
|
||||
record.push("type", Value::string(file_type.clone(), span));
|
||||
} else {
|
||||
cols.push("type".into());
|
||||
vals.push(Value::nothing(span));
|
||||
record.push("type", Value::nothing(span));
|
||||
}
|
||||
|
||||
if long {
|
||||
cols.push("target".into());
|
||||
if let Some(md) = metadata {
|
||||
if md.file_type().is_symlink() {
|
||||
if let Ok(path_to_link) = filename.read_link() {
|
||||
vals.push(Value::String {
|
||||
val: path_to_link.to_string_lossy().to_string(),
|
||||
span,
|
||||
});
|
||||
record.push(
|
||||
"target",
|
||||
if md.file_type().is_symlink() {
|
||||
if let Ok(path_to_link) = filename.read_link() {
|
||||
Value::string(path_to_link.to_string_lossy(), span)
|
||||
} else {
|
||||
Value::string("Could not obtain target file's path", span)
|
||||
}
|
||||
} else {
|
||||
vals.push(Value::String {
|
||||
val: "Could not obtain target file's path".to_string(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
vals.push(Value::nothing(span));
|
||||
}
|
||||
Value::nothing(span)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if long {
|
||||
if let Some(md) = metadata {
|
||||
cols.push("readonly".into());
|
||||
vals.push(Value::Bool {
|
||||
val: md.permissions().readonly(),
|
||||
span,
|
||||
});
|
||||
record.push("readonly", Value::bool(md.permissions().readonly(), span));
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use crate::filesystem::util::users;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
let mode = md.permissions().mode();
|
||||
cols.push("mode".into());
|
||||
vals.push(Value::String {
|
||||
val: umask::Mode::from(mode).to_string(),
|
||||
span,
|
||||
});
|
||||
record.push(
|
||||
"mode",
|
||||
Value::string(umask::Mode::from(mode).to_string(), span),
|
||||
);
|
||||
|
||||
let nlinks = md.nlink();
|
||||
cols.push("num_links".into());
|
||||
vals.push(Value::Int {
|
||||
val: nlinks as i64,
|
||||
span,
|
||||
});
|
||||
record.push("num_links", Value::int(nlinks as i64, span));
|
||||
|
||||
let inode = md.ino();
|
||||
cols.push("inode".into());
|
||||
vals.push(Value::Int {
|
||||
val: inode as i64,
|
||||
span,
|
||||
});
|
||||
record.push("inode", Value::int(inode as i64, span));
|
||||
|
||||
cols.push("user".into());
|
||||
if let Some(user) = users::get_user_by_uid(md.uid()) {
|
||||
vals.push(Value::String {
|
||||
val: user.name,
|
||||
span,
|
||||
});
|
||||
} else {
|
||||
vals.push(Value::Int {
|
||||
val: md.uid() as i64,
|
||||
span,
|
||||
})
|
||||
}
|
||||
record.push(
|
||||
"user",
|
||||
if let Some(user) = users::get_user_by_uid(md.uid()) {
|
||||
Value::string(user.name, span)
|
||||
} else {
|
||||
Value::int(md.uid() as i64, span)
|
||||
},
|
||||
);
|
||||
|
||||
cols.push("group".into());
|
||||
if let Some(group) = users::get_group_by_gid(md.gid()) {
|
||||
vals.push(Value::String {
|
||||
val: group.name,
|
||||
span,
|
||||
});
|
||||
} else {
|
||||
vals.push(Value::Int {
|
||||
val: md.gid() as i64,
|
||||
span,
|
||||
})
|
||||
}
|
||||
record.push(
|
||||
"group",
|
||||
if let Some(group) = users::get_group_by_gid(md.gid()) {
|
||||
Value::string(group.name, span)
|
||||
} else {
|
||||
Value::int(md.gid() as i64, span)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cols.push("size".to_string());
|
||||
if let Some(md) = metadata {
|
||||
let zero_sized = file_type == "pipe"
|
||||
|| file_type == "socket"
|
||||
|| file_type == "char device"
|
||||
|| file_type == "block device";
|
||||
record.push(
|
||||
"size",
|
||||
if let Some(md) = metadata {
|
||||
let zero_sized = file_type == "pipe"
|
||||
|| file_type == "socket"
|
||||
|| file_type == "char device"
|
||||
|| file_type == "block device";
|
||||
|
||||
if md.is_dir() {
|
||||
if du {
|
||||
let params = DirBuilder::new(Span::new(0, 2), None, false, None, false);
|
||||
let dir_size = DirInfo::new(filename, ¶ms, None, ctrl_c).get_size();
|
||||
if md.is_dir() {
|
||||
if du {
|
||||
let params = DirBuilder::new(Span::new(0, 2), None, false, None, false);
|
||||
let dir_size = DirInfo::new(filename, ¶ms, None, ctrl_c).get_size();
|
||||
|
||||
vals.push(Value::Filesize {
|
||||
val: dir_size as i64,
|
||||
span,
|
||||
});
|
||||
} else {
|
||||
let dir_size: u64 = md.len();
|
||||
Value::filesize(dir_size as i64, span)
|
||||
} else {
|
||||
let dir_size: u64 = md.len();
|
||||
|
||||
vals.push(Value::Filesize {
|
||||
val: dir_size as i64,
|
||||
span,
|
||||
});
|
||||
};
|
||||
} else if md.is_file() {
|
||||
vals.push(Value::Filesize {
|
||||
val: md.len() as i64,
|
||||
span,
|
||||
});
|
||||
} else if md.file_type().is_symlink() {
|
||||
if let Ok(symlink_md) = filename.symlink_metadata() {
|
||||
vals.push(Value::Filesize {
|
||||
val: symlink_md.len() as i64,
|
||||
span,
|
||||
});
|
||||
} else {
|
||||
vals.push(Value::nothing(span));
|
||||
}
|
||||
} else {
|
||||
let value = if zero_sized {
|
||||
Value::Filesize { val: 0, span }
|
||||
Value::filesize(dir_size as i64, span)
|
||||
}
|
||||
} else if md.is_file() {
|
||||
Value::filesize(md.len() as i64, span)
|
||||
} else if md.file_type().is_symlink() {
|
||||
if let Ok(symlink_md) = filename.symlink_metadata() {
|
||||
Value::filesize(symlink_md.len() as i64, span)
|
||||
} else {
|
||||
Value::nothing(span)
|
||||
}
|
||||
} else if zero_sized {
|
||||
Value::filesize(0, span)
|
||||
} else {
|
||||
Value::nothing(span)
|
||||
};
|
||||
vals.push(value);
|
||||
}
|
||||
} else {
|
||||
vals.push(Value::nothing(span));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Value::nothing(span)
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(md) = metadata {
|
||||
if long {
|
||||
cols.push("created".to_string());
|
||||
{
|
||||
record.push("created", {
|
||||
let mut val = Value::nothing(span);
|
||||
if let Ok(c) = md.created() {
|
||||
if let Some(local) = try_convert_to_local_date_time(c) {
|
||||
val = Value::Date {
|
||||
val: local.with_timezone(local.offset()),
|
||||
span,
|
||||
};
|
||||
val = Value::date(local.with_timezone(local.offset()), span);
|
||||
}
|
||||
}
|
||||
vals.push(val);
|
||||
}
|
||||
val
|
||||
});
|
||||
|
||||
cols.push("accessed".to_string());
|
||||
{
|
||||
record.push("accessed", {
|
||||
let mut val = Value::nothing(span);
|
||||
if let Ok(a) = md.accessed() {
|
||||
if let Some(local) = try_convert_to_local_date_time(a) {
|
||||
val = Value::Date {
|
||||
val: local.with_timezone(local.offset()),
|
||||
span,
|
||||
};
|
||||
val = Value::date(local.with_timezone(local.offset()), span)
|
||||
}
|
||||
}
|
||||
vals.push(val);
|
||||
}
|
||||
val
|
||||
});
|
||||
}
|
||||
|
||||
cols.push("modified".to_string());
|
||||
{
|
||||
record.push("modified", {
|
||||
let mut val = Value::nothing(span);
|
||||
if let Ok(m) = md.modified() {
|
||||
if let Some(local) = try_convert_to_local_date_time(m) {
|
||||
val = Value::Date {
|
||||
val: local.with_timezone(local.offset()),
|
||||
span,
|
||||
};
|
||||
val = Value::date(local.with_timezone(local.offset()), span);
|
||||
}
|
||||
}
|
||||
vals.push(val);
|
||||
}
|
||||
val
|
||||
})
|
||||
} else {
|
||||
if long {
|
||||
cols.push("created".to_string());
|
||||
vals.push(Value::nothing(span));
|
||||
|
||||
cols.push("accessed".to_string());
|
||||
vals.push(Value::nothing(span));
|
||||
record.push("created", Value::nothing(span));
|
||||
record.push("accessed", Value::nothing(span));
|
||||
}
|
||||
|
||||
cols.push("modified".to_string());
|
||||
vals.push(Value::nothing(span));
|
||||
record.push("modified", Value::nothing(span));
|
||||
}
|
||||
|
||||
Ok(Value::Record { cols, vals, span })
|
||||
Ok(Value::record(record, span))
|
||||
}
|
||||
|
||||
// TODO: can we get away from local times in `ls`? internals might be cleaner if we worked in UTC
|
||||
@ -702,14 +638,9 @@ mod windows_helper {
|
||||
span: Span,
|
||||
long: bool,
|
||||
) -> Value {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
|
||||
cols.push("name".into());
|
||||
vals.push(Value::String {
|
||||
val: display_name.to_string(),
|
||||
span,
|
||||
});
|
||||
record.push("name", Value::string(display_name, span));
|
||||
|
||||
let find_data = match find_first_file(filename, span) {
|
||||
Ok(fd) => fd,
|
||||
@ -722,90 +653,71 @@ mod windows_helper {
|
||||
filename.to_string_lossy()
|
||||
);
|
||||
log::error!("{e}");
|
||||
return Value::Record { cols, vals, span };
|
||||
return Value::record(record, span);
|
||||
}
|
||||
};
|
||||
|
||||
cols.push("type".into());
|
||||
vals.push(Value::String {
|
||||
val: get_file_type_windows_fallback(&find_data),
|
||||
span,
|
||||
});
|
||||
record.push(
|
||||
"type",
|
||||
Value::string(get_file_type_windows_fallback(&find_data), span),
|
||||
);
|
||||
|
||||
if long {
|
||||
cols.push("target".into());
|
||||
if is_symlink(&find_data) {
|
||||
if let Ok(path_to_link) = filename.read_link() {
|
||||
vals.push(Value::String {
|
||||
val: path_to_link.to_string_lossy().to_string(),
|
||||
span,
|
||||
});
|
||||
record.push(
|
||||
"target",
|
||||
if is_symlink(&find_data) {
|
||||
if let Ok(path_to_link) = filename.read_link() {
|
||||
Value::string(path_to_link.to_string_lossy(), span)
|
||||
} else {
|
||||
Value::string("Could not obtain target file's path", span)
|
||||
}
|
||||
} else {
|
||||
vals.push(Value::String {
|
||||
val: "Could not obtain target file's path".to_string(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
vals.push(Value::nothing(span));
|
||||
}
|
||||
Value::nothing(span)
|
||||
},
|
||||
);
|
||||
|
||||
cols.push("readonly".into());
|
||||
vals.push(Value::Bool {
|
||||
val: (find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY.0 != 0),
|
||||
span,
|
||||
});
|
||||
record.push(
|
||||
"readonly",
|
||||
Value::bool(
|
||||
find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY.0 != 0,
|
||||
span,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
cols.push("size".to_string());
|
||||
let file_size = (find_data.nFileSizeHigh as u64) << 32 | find_data.nFileSizeLow as u64;
|
||||
vals.push(Value::Filesize {
|
||||
val: file_size as i64,
|
||||
span,
|
||||
});
|
||||
record.push("size", Value::filesize(file_size as i64, span));
|
||||
|
||||
if long {
|
||||
cols.push("created".to_string());
|
||||
{
|
||||
record.push("created", {
|
||||
let mut val = Value::nothing(span);
|
||||
let seconds_since_unix_epoch = unix_time_from_filetime(&find_data.ftCreationTime);
|
||||
if let Some(local) = unix_time_to_local_date_time(seconds_since_unix_epoch) {
|
||||
val = Value::Date {
|
||||
val: local.with_timezone(local.offset()),
|
||||
span,
|
||||
};
|
||||
val = Value::date(local.with_timezone(local.offset()), span);
|
||||
}
|
||||
vals.push(val);
|
||||
}
|
||||
val
|
||||
});
|
||||
|
||||
cols.push("accessed".to_string());
|
||||
{
|
||||
record.push("accessed", {
|
||||
let mut val = Value::nothing(span);
|
||||
let seconds_since_unix_epoch = unix_time_from_filetime(&find_data.ftLastAccessTime);
|
||||
if let Some(local) = unix_time_to_local_date_time(seconds_since_unix_epoch) {
|
||||
val = Value::Date {
|
||||
val: local.with_timezone(local.offset()),
|
||||
span,
|
||||
};
|
||||
val = Value::date(local.with_timezone(local.offset()), span);
|
||||
}
|
||||
vals.push(val);
|
||||
}
|
||||
val
|
||||
});
|
||||
}
|
||||
|
||||
cols.push("modified".to_string());
|
||||
{
|
||||
record.push("modified", {
|
||||
let mut val = Value::nothing(span);
|
||||
let seconds_since_unix_epoch = unix_time_from_filetime(&find_data.ftLastWriteTime);
|
||||
if let Some(local) = unix_time_to_local_date_time(seconds_since_unix_epoch) {
|
||||
val = Value::Date {
|
||||
val: local.with_timezone(local.offset()),
|
||||
span,
|
||||
};
|
||||
val = Value::date(local.with_timezone(local.offset()), span);
|
||||
}
|
||||
vals.push(val);
|
||||
}
|
||||
val
|
||||
});
|
||||
|
||||
Value::Record { cols, vals, span }
|
||||
Value::record(record, span)
|
||||
}
|
||||
|
||||
fn unix_time_from_filetime(ft: &FILETIME) -> i64 {
|
||||
|
@ -131,7 +131,8 @@ fn getcol(
|
||||
.into_pipeline_data(ctrlc)
|
||||
.set_metadata(metadata)
|
||||
}),
|
||||
PipelineData::Value(Value::Record { cols, .. }, ..) => Ok(cols
|
||||
PipelineData::Value(Value::Record { val, .. }, ..) => Ok(val
|
||||
.cols
|
||||
.into_iter()
|
||||
.map(move |x| Value::String { val: x, span: head })
|
||||
.into_pipeline_data(ctrlc)
|
||||
|
@ -1,7 +1,7 @@
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call, engine::Command, engine::EngineState, engine::Stack, Category, Example,
|
||||
PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -63,11 +63,10 @@ impl Command for Compact {
|
||||
description: "Filter out all records where 'World' is null (Returns the table)",
|
||||
example: r#"[["Hello" "World"]; [null 3]] | compact World"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["Hello".into(), "World".into()],
|
||||
vals: vec![Value::nothing(Span::test_data()), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -86,29 +86,27 @@ fn default(
|
||||
input.map(
|
||||
move |item| match item {
|
||||
Value::Record {
|
||||
mut cols,
|
||||
mut vals,
|
||||
val: mut record,
|
||||
span,
|
||||
} => {
|
||||
let mut idx = 0;
|
||||
let mut found = false;
|
||||
|
||||
while idx < cols.len() {
|
||||
if cols[idx] == column.item {
|
||||
while idx < record.len() {
|
||||
if record.cols[idx] == column.item {
|
||||
found = true;
|
||||
if matches!(vals[idx], Value::Nothing { .. }) {
|
||||
vals[idx] = value.clone();
|
||||
if matches!(record.vals[idx], Value::Nothing { .. }) {
|
||||
record.vals[idx] = value.clone();
|
||||
}
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
if !found {
|
||||
cols.push(column.item.clone());
|
||||
vals.push(value.clone());
|
||||
record.push(column.item.clone(), value.clone());
|
||||
}
|
||||
|
||||
Value::Record { cols, vals, span }
|
||||
Value::record(record, span)
|
||||
}
|
||||
_ => item,
|
||||
},
|
||||
|
@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -66,16 +66,14 @@ impl Command for DropColumn {
|
||||
example: "[[lib, extension]; [nu-lib, rs] [nu-core, rb]] | drop column",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["lib".into()],
|
||||
vals: vec![Value::test_string("nu-lib")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["lib".into()],
|
||||
vals: vec![Value::test_string("nu-core")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -105,15 +103,13 @@ fn dropcol(
|
||||
keep_columns = get_cellpath_columns(kc, span);
|
||||
|
||||
for input_val in input_vals {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
|
||||
for path in &keep_columns {
|
||||
let fetcher = input_val.clone().follow_cell_path(&path.members, false)?;
|
||||
cols.push(path.into_string());
|
||||
vals.push(fetcher);
|
||||
record.push(path.into_string(), fetcher);
|
||||
}
|
||||
output.push(Value::Record { cols, vals, span })
|
||||
output.push(Value::record(record, span))
|
||||
}
|
||||
|
||||
Ok(output
|
||||
@ -129,15 +125,13 @@ fn dropcol(
|
||||
keep_columns = get_cellpath_columns(kc, span);
|
||||
|
||||
for input_val in v {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
|
||||
for path in &keep_columns {
|
||||
let fetcher = input_val.clone().follow_cell_path(&path.members, false)?;
|
||||
cols.push(path.into_string());
|
||||
vals.push(fetcher);
|
||||
record.push(path.into_string(), fetcher);
|
||||
}
|
||||
output.push(Value::Record { cols, vals, span })
|
||||
output.push(Value::record(record, span))
|
||||
}
|
||||
|
||||
Ok(output
|
||||
@ -145,17 +139,14 @@ fn dropcol(
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
PipelineData::Value(v, ..) => {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
|
||||
for cell_path in &keep_columns {
|
||||
let result = v.clone().follow_cell_path(&cell_path.members, false)?;
|
||||
|
||||
cols.push(cell_path.into_string());
|
||||
vals.push(result);
|
||||
record.push(cell_path.into_string(), result);
|
||||
}
|
||||
|
||||
Ok(Value::Record { cols, vals, span }.into_pipeline_data())
|
||||
Ok(Value::record(record, span).into_pipeline_data())
|
||||
}
|
||||
x => Ok(x),
|
||||
}
|
||||
@ -164,7 +155,7 @@ fn dropcol(
|
||||
fn get_input_cols(input: Vec<Value>) -> Vec<String> {
|
||||
let rec = input.first();
|
||||
match rec {
|
||||
Some(Value::Record { cols, vals: _, .. }) => cols.to_vec(),
|
||||
Some(Value::Record { val, .. }) => val.cols.to_vec(),
|
||||
_ => vec!["".to_string()],
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError, Signature,
|
||||
Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -71,11 +71,10 @@ impl Command for Drop {
|
||||
description: "Remove the last row in a table",
|
||||
example: "[[a, b]; [1, 2] [3, 4]] | drop 1",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
Type, Value,
|
||||
record, Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError,
|
||||
Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -33,21 +33,18 @@ impl Command for Enumerate {
|
||||
example: r#"[a, b, c] | enumerate "#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".into(), "item".into()],
|
||||
vals: vec![Value::test_int(0), Value::test_string("a")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".into(), "item".into()],
|
||||
vals: vec![Value::test_int(1), Value::test_string("b")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".into(), "item".into()],
|
||||
vals: vec![Value::test_int(2), Value::test_string("c")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -68,16 +65,14 @@ impl Command for Enumerate {
|
||||
Ok(input
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(move |(idx, x)| Value::Record {
|
||||
cols: vec!["index".into(), "item".into()],
|
||||
vals: vec![
|
||||
Value::Int {
|
||||
val: idx as i64,
|
||||
span,
|
||||
.map(move |(idx, x)| {
|
||||
Value::record(
|
||||
record! {
|
||||
"index" => Value::int(idx as i64, span),
|
||||
"item" => x,
|
||||
},
|
||||
x,
|
||||
],
|
||||
span,
|
||||
span,
|
||||
)
|
||||
})
|
||||
.into_pipeline_data_with_metadata(metadata, ctrlc))
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Record,
|
||||
ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -205,11 +205,10 @@ a variable. On the other hand, the "row condition" syntax is not supported."#
|
||||
description: "Filter rows of a table according to a condition",
|
||||
example: "[{a: 1} {a: 2}] | filter {|x| $x.a > 1}",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -217,11 +216,10 @@ a variable. On the other hand, the "row condition" syntax is not supported."#
|
||||
description: "Filter rows of a table according to a stored condition",
|
||||
example: "let cond = {|x| $x.a > 1}; [{a: 1} {a: 2}] | filter $cond",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -9,7 +9,7 @@ use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Config, Example, IntoInterruptiblePipelineData, IntoPipelineData, ListStream,
|
||||
PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -126,13 +126,13 @@ impl Command for Find {
|
||||
description: "Find value in records using regex",
|
||||
example: r#"[[version name]; ['0.1.0' nushell] ['0.1.1' fish] ['0.2.0' zsh]] | find -r "nu""#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_record(
|
||||
vec!["version", "name"],
|
||||
vec![
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["version".to_string(), "name".to_string()],
|
||||
vals: vec![
|
||||
Value::test_string("0.1.0"),
|
||||
Value::test_string("nushell".to_string()),
|
||||
],
|
||||
)],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -141,20 +141,20 @@ impl Command for Find {
|
||||
example: r#"[[version name]; ['0.1.0' nushell] ['0.1.1' fish] ['0.2.0' zsh]] | find -r "nu" --invert"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(
|
||||
vec!["version", "name"],
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec!["version".to_string(), "name".to_string()],
|
||||
vals: vec![
|
||||
Value::test_string("0.1.1"),
|
||||
Value::test_string("fish".to_string()),
|
||||
],
|
||||
),
|
||||
Value::test_record(
|
||||
vec!["version", "name"],
|
||||
vec![
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["version".to_string(), "name".to_string()],
|
||||
vals: vec![
|
||||
Value::test_string("0.2.0"),
|
||||
Value::test_string("zsh".to_string()),
|
||||
],
|
||||
),
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -191,9 +191,9 @@ impl Command for Find {
|
||||
example:
|
||||
"[[col1 col2 col3]; [moe larry curly] [larry curly moe]] | find moe -c [col1]",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_record(
|
||||
vec!["col1".to_string(), "col2".to_string(), "col3".to_string()],
|
||||
vec![
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["col1".to_string(), "col2".to_string(), "col3".to_string()],
|
||||
vals: vec![
|
||||
Value::test_string(
|
||||
"\u{1b}[37m\u{1b}[0m\u{1b}[41;37mmoe\u{1b}[0m\u{1b}[37m\u{1b}[0m"
|
||||
.to_string(),
|
||||
@ -201,7 +201,7 @@ impl Command for Find {
|
||||
Value::test_string("larry".to_string()),
|
||||
Value::test_string("curly".to_string()),
|
||||
],
|
||||
)],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -267,9 +267,11 @@ fn find_with_regex(
|
||||
input.filter(
|
||||
move |value| match value {
|
||||
Value::String { val, .. } => re.is_match(val.as_str()).unwrap_or(false) != invert,
|
||||
Value::Record { vals, .. } | Value::List { vals, .. } => {
|
||||
values_match_find(vals, &re, &config, invert)
|
||||
Value::Record {
|
||||
val: Record { vals, .. },
|
||||
..
|
||||
}
|
||||
| Value::List { vals, .. } => values_match_find(vals, &re, &config, invert),
|
||||
_ => false,
|
||||
},
|
||||
ctrlc,
|
||||
@ -293,8 +295,7 @@ fn record_matches_regex(values: &[Value], re: &Regex, config: &Config) -> bool {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn highlight_terms_in_record_with_search_columns(
|
||||
search_cols: &Vec<String>,
|
||||
cols: &[String],
|
||||
vals: &[Value],
|
||||
record: &Record,
|
||||
span: Span,
|
||||
config: &Config,
|
||||
terms: &[Value],
|
||||
@ -302,14 +303,14 @@ fn highlight_terms_in_record_with_search_columns(
|
||||
highlight_style: Style,
|
||||
) -> Value {
|
||||
let cols_to_search = if search_cols.is_empty() {
|
||||
cols.to_vec()
|
||||
&record.cols
|
||||
} else {
|
||||
search_cols.to_vec()
|
||||
search_cols
|
||||
};
|
||||
let term_strs: Vec<_> = terms.iter().map(|v| v.into_string("", config)).collect();
|
||||
|
||||
// iterator of Ok((val_str, term_str)) pairs if the value should be highlighted, otherwise Err(val)
|
||||
let try_val_highlight = vals.iter().zip(cols).map(|(val, col)| {
|
||||
let try_val_highlight = record.iter().map(|(col, val)| {
|
||||
let val_str = val.into_string("", config);
|
||||
let predicate = cols_to_search.contains(col);
|
||||
predicate
|
||||
@ -337,11 +338,13 @@ fn highlight_terms_in_record_with_search_columns(
|
||||
})
|
||||
.map(|v| v.unwrap_or_else(|v| v));
|
||||
|
||||
Value::Record {
|
||||
cols: cols.to_vec(),
|
||||
vals: new_vals.collect(),
|
||||
Value::record(
|
||||
Record {
|
||||
cols: record.cols.clone(),
|
||||
vals: new_vals.collect(),
|
||||
},
|
||||
span,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn contains_ignore_case(string: &str, substring: &str) -> bool {
|
||||
@ -392,18 +395,15 @@ fn find_with_rest_and_highlight(
|
||||
PipelineData::Value(_, _) => input
|
||||
.map(
|
||||
move |mut x| match &mut x {
|
||||
Value::Record { cols, vals, span } => {
|
||||
highlight_terms_in_record_with_search_columns(
|
||||
&cols_to_search_in_map,
|
||||
cols,
|
||||
vals,
|
||||
*span,
|
||||
&config,
|
||||
&terms,
|
||||
string_style,
|
||||
highlight_style,
|
||||
)
|
||||
}
|
||||
Value::Record { val, span } => highlight_terms_in_record_with_search_columns(
|
||||
&cols_to_search_in_map,
|
||||
val,
|
||||
*span,
|
||||
&config,
|
||||
&terms,
|
||||
string_style,
|
||||
highlight_style,
|
||||
),
|
||||
_ => x,
|
||||
},
|
||||
ctrlc.clone(),
|
||||
@ -424,18 +424,15 @@ fn find_with_rest_and_highlight(
|
||||
PipelineData::ListStream(stream, meta) => Ok(ListStream::from_stream(
|
||||
stream
|
||||
.map(move |mut x| match &mut x {
|
||||
Value::Record { cols, vals, span } => {
|
||||
highlight_terms_in_record_with_search_columns(
|
||||
&cols_to_search_in_map,
|
||||
cols,
|
||||
vals,
|
||||
*span,
|
||||
&config,
|
||||
&terms,
|
||||
string_style,
|
||||
highlight_style,
|
||||
)
|
||||
}
|
||||
Value::Record { val, span } => highlight_terms_in_record_with_search_columns(
|
||||
&cols_to_search_in_map,
|
||||
val,
|
||||
*span,
|
||||
&config,
|
||||
&terms,
|
||||
string_style,
|
||||
highlight_style,
|
||||
),
|
||||
_ => x,
|
||||
})
|
||||
.filter(move |value| {
|
||||
@ -535,13 +532,13 @@ fn value_should_be_printed(
|
||||
| Value::List { .. }
|
||||
| Value::CellPath { .. }
|
||||
| Value::CustomValue { .. } => term_contains_value(term, &lower_value, span),
|
||||
Value::Record { cols, vals, .. } => {
|
||||
record_matches_term(cols, vals, columns_to_search, filter_config, term, span)
|
||||
Value::Record { val, .. } => {
|
||||
record_matches_term(val, columns_to_search, filter_config, term, span)
|
||||
}
|
||||
Value::LazyRecord { val, .. } => match val.collect() {
|
||||
Ok(val) => match val {
|
||||
Value::Record { cols, vals, .. } => {
|
||||
record_matches_term(&cols, &vals, columns_to_search, filter_config, term, span)
|
||||
Value::Record { val, .. } => {
|
||||
record_matches_term(&val, columns_to_search, filter_config, term, span)
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
@ -567,19 +564,18 @@ fn term_equals_value(term: &Value, value: &Value, span: Span) -> bool {
|
||||
}
|
||||
|
||||
fn record_matches_term(
|
||||
cols: &[String],
|
||||
vals: &[Value],
|
||||
record: &Record,
|
||||
columns_to_search: &Vec<String>,
|
||||
filter_config: &Config,
|
||||
term: &Value,
|
||||
span: Span,
|
||||
) -> bool {
|
||||
let cols_to_search = if columns_to_search.is_empty() {
|
||||
cols.to_vec()
|
||||
&record.cols
|
||||
} else {
|
||||
columns_to_search.to_vec()
|
||||
columns_to_search
|
||||
};
|
||||
cols.iter().zip(vals).any(|(col, val)| {
|
||||
record.iter().any(|(col, val)| {
|
||||
if !cols_to_search.contains(col) {
|
||||
return false;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember};
|
||||
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -84,26 +84,50 @@ impl Command for Flatten {
|
||||
example: "{ a: b, d: [ 1 2 3 4 ], e: [ 4 3 ] } | flatten d --all",
|
||||
result: Some(Value::List{
|
||||
vals: vec![
|
||||
Value::Record{
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "d".to_string(), "e".to_string()],
|
||||
vals: vec![Value::test_string("b"), Value::test_int(1), Value::List{vals: vec![Value::test_int(4), Value::test_int(3)], span: Span::test_data()} ],
|
||||
span: Span::test_data()
|
||||
},
|
||||
Value::Record{
|
||||
vals: vec![
|
||||
Value::test_string("b"),
|
||||
Value::test_int(1),
|
||||
Value::List {
|
||||
vals: vec![Value::test_int(4), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "d".to_string(), "e".to_string()],
|
||||
vals: vec![Value::test_string("b"), Value::test_int(2), Value::List{vals: vec![Value::test_int(4), Value::test_int(3)], span: Span::test_data()} ],
|
||||
span: Span::test_data()
|
||||
},
|
||||
Value::Record{
|
||||
vals: vec![
|
||||
Value::test_string("b"),
|
||||
Value::test_int(2),
|
||||
Value::List {
|
||||
vals: vec![Value::test_int(4), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "d".to_string(), "e".to_string()],
|
||||
vals: vec![Value::test_string("b"), Value::test_int(3), Value::List{vals: vec![Value::test_int(4), Value::test_int(3)], span: Span::test_data()} ],
|
||||
span: Span::test_data()
|
||||
},
|
||||
Value::Record{
|
||||
vals: vec![
|
||||
Value::test_string("b"),
|
||||
Value::test_int(3),
|
||||
Value::List {
|
||||
vals: vec![Value::test_int(4), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "d".to_string(), "e".to_string()],
|
||||
vals: vec![Value::test_string("b"), Value::test_int(4), Value::List{vals: vec![Value::test_int(4), Value::test_int(3)], span: Span::test_data()} ],
|
||||
span: Span::test_data()
|
||||
}
|
||||
vals: vec![
|
||||
Value::test_string("b"),
|
||||
Value::test_int(4),
|
||||
Value::List {
|
||||
vals: vec![Value::test_int(4), Value::test_int(3)],
|
||||
span: Span::test_data()
|
||||
}
|
||||
],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -157,259 +181,208 @@ fn flat_value(columns: &[CellPath], item: &Value, _name_tag: Span, all: bool) ->
|
||||
Err(e) => return vec![Value::Error { error: Box::new(e) }],
|
||||
};
|
||||
|
||||
let res = {
|
||||
if item.as_record().is_ok() {
|
||||
let mut out = IndexMap::<String, Value>::new();
|
||||
let mut inner_table = None;
|
||||
if item.as_record().is_ok() {
|
||||
let mut out = IndexMap::<String, Value>::new();
|
||||
let mut inner_table = None;
|
||||
|
||||
let records = match item {
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: _,
|
||||
} => (cols, vals),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => return vec![item.clone()],
|
||||
other => {
|
||||
return vec![Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "record".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: _name_tag,
|
||||
src_span: other.expect_span(),
|
||||
}),
|
||||
}];
|
||||
}
|
||||
};
|
||||
let record = match item {
|
||||
Value::Record { val, .. } => val,
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => return vec![item.clone()],
|
||||
other => {
|
||||
return vec![Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "record".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: _name_tag,
|
||||
src_span: other.expect_span(),
|
||||
}),
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
let s = match item.span() {
|
||||
Ok(x) => x,
|
||||
Err(e) => return vec![Value::Error { error: Box::new(e) }],
|
||||
};
|
||||
let s = match item.span() {
|
||||
Ok(x) => x,
|
||||
Err(e) => return vec![Value::Error { error: Box::new(e) }],
|
||||
};
|
||||
|
||||
let records_iterator = {
|
||||
let cols = records.0;
|
||||
let vals = records.1;
|
||||
for (column_index, (column, value)) in record.iter().enumerate() {
|
||||
let column_requested = columns.iter().find(|c| c.into_string() == *column);
|
||||
let need_flatten = { columns.is_empty() || column_requested.is_some() };
|
||||
|
||||
let mut pairs = vec![];
|
||||
for i in 0..cols.len() {
|
||||
pairs.push((cols[i].as_str(), &vals[i]));
|
||||
}
|
||||
pairs
|
||||
};
|
||||
|
||||
for (column_index, (column, value)) in records_iterator.into_iter().enumerate() {
|
||||
let column_requested = columns.iter().find(|c| c.into_string() == *column);
|
||||
let need_flatten = { columns.is_empty() || column_requested.is_some() };
|
||||
|
||||
match value {
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: _,
|
||||
} => {
|
||||
if need_flatten {
|
||||
cols.iter().enumerate().for_each(|(idx, inner_record_col)| {
|
||||
if out.contains_key(inner_record_col) {
|
||||
out.insert(
|
||||
format!("{column}_{inner_record_col}"),
|
||||
vals[idx].clone(),
|
||||
);
|
||||
} else {
|
||||
out.insert(inner_record_col.to_string(), vals[idx].clone());
|
||||
}
|
||||
})
|
||||
} else if out.contains_key(column) {
|
||||
out.insert(format!("{column}_{column}"), value.clone());
|
||||
} else {
|
||||
out.insert(column.to_string(), value.clone());
|
||||
}
|
||||
}
|
||||
Value::List { vals, span }
|
||||
if all && vals.iter().all(|f| f.as_record().is_ok()) =>
|
||||
{
|
||||
if need_flatten && inner_table.is_some() {
|
||||
return vec![Value::Error{ error: Box::new(ShellError::UnsupportedInput(
|
||||
"can only flatten one inner list at a time. tried flattening more than one column with inner lists... but is flattened already".to_string(),
|
||||
"value originates from here".into(),
|
||||
s,
|
||||
*span
|
||||
))}
|
||||
];
|
||||
}
|
||||
// it's a table (a list of record, we can flatten inner record)
|
||||
let mut cs = vec![];
|
||||
let mut vs = vec![];
|
||||
|
||||
for v in vals {
|
||||
if let Ok(r) = v.as_record() {
|
||||
cs.push(r.0);
|
||||
vs.push(r.1)
|
||||
}
|
||||
}
|
||||
|
||||
if need_flatten {
|
||||
let cols = cs.into_iter().map(|f| f.to_vec());
|
||||
let vals = vs.into_iter().map(|f| f.to_vec());
|
||||
|
||||
inner_table = Some(TableInside::FlattenedRows {
|
||||
columns: cols.collect(),
|
||||
_span: &s,
|
||||
values: vals.collect(),
|
||||
parent_column_name: column,
|
||||
parent_column_index: column_index,
|
||||
});
|
||||
} else if out.contains_key(column) {
|
||||
out.insert(format!("{column}_{column}"), value.clone());
|
||||
} else {
|
||||
out.insert(column.to_string(), value.clone());
|
||||
}
|
||||
}
|
||||
Value::List { vals: values, span } => {
|
||||
if need_flatten && inner_table.is_some() {
|
||||
return vec![Value::Error{ error: Box::new(ShellError::UnsupportedInput(
|
||||
"can only flatten one inner list at a time. tried flattening more than one column with inner lists... but is flattened already".to_string(),
|
||||
"value originates from here".into(),
|
||||
s,
|
||||
*span
|
||||
))}
|
||||
];
|
||||
}
|
||||
|
||||
if !columns.is_empty() {
|
||||
let cell_path =
|
||||
column_requested.and_then(|x| match x.members.first() {
|
||||
Some(PathMember::String { val, span: _, .. }) => Some(val),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
if let Some(r) = cell_path {
|
||||
inner_table = Some(TableInside::Entries(
|
||||
r,
|
||||
&s,
|
||||
values.iter().collect::<Vec<_>>(),
|
||||
column_index,
|
||||
));
|
||||
match value {
|
||||
Value::Record { val, .. } => {
|
||||
if need_flatten {
|
||||
val.iter().for_each(|(col, val)| {
|
||||
if out.contains_key(col) {
|
||||
out.insert(format!("{column}_{col}"), val.clone());
|
||||
} else {
|
||||
out.insert(column.to_string(), value.clone());
|
||||
out.insert(col.to_string(), val.clone());
|
||||
}
|
||||
} else {
|
||||
})
|
||||
} else if out.contains_key(column) {
|
||||
out.insert(format!("{column}_{column}"), value.clone());
|
||||
} else {
|
||||
out.insert(column.to_string(), value.clone());
|
||||
}
|
||||
}
|
||||
Value::List { vals, span } if all && vals.iter().all(|f| f.as_record().is_ok()) => {
|
||||
if need_flatten && inner_table.is_some() {
|
||||
return vec![Value::Error{ error: Box::new(ShellError::UnsupportedInput(
|
||||
"can only flatten one inner list at a time. tried flattening more than one column with inner lists... but is flattened already".to_string(),
|
||||
"value originates from here".into(),
|
||||
s,
|
||||
*span
|
||||
))}
|
||||
];
|
||||
}
|
||||
// it's a table (a list of record, we can flatten inner record)
|
||||
let mut records = vec![];
|
||||
|
||||
for v in vals {
|
||||
if let Ok(r) = v.as_record() {
|
||||
records.push(r)
|
||||
}
|
||||
}
|
||||
|
||||
if need_flatten {
|
||||
let cols = records.iter().map(|r| r.cols.clone());
|
||||
let vals = records.iter().map(|r| r.vals.clone());
|
||||
|
||||
inner_table = Some(TableInside::FlattenedRows {
|
||||
columns: cols.collect(),
|
||||
_span: &s,
|
||||
values: vals.collect(),
|
||||
parent_column_name: column,
|
||||
parent_column_index: column_index,
|
||||
});
|
||||
} else if out.contains_key(column) {
|
||||
out.insert(format!("{column}_{column}"), value.clone());
|
||||
} else {
|
||||
out.insert(column.to_string(), value.clone());
|
||||
}
|
||||
}
|
||||
Value::List { vals: values, span } => {
|
||||
if need_flatten && inner_table.is_some() {
|
||||
return vec![Value::Error{ error: Box::new(ShellError::UnsupportedInput(
|
||||
"can only flatten one inner list at a time. tried flattening more than one column with inner lists... but is flattened already".to_string(),
|
||||
"value originates from here".into(),
|
||||
s,
|
||||
*span
|
||||
))}
|
||||
];
|
||||
}
|
||||
|
||||
if !columns.is_empty() {
|
||||
let cell_path = column_requested.and_then(|x| match x.members.first() {
|
||||
Some(PathMember::String { val, span: _, .. }) => Some(val),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
if let Some(r) = cell_path {
|
||||
inner_table = Some(TableInside::Entries(
|
||||
column,
|
||||
r,
|
||||
&s,
|
||||
values.iter().collect::<Vec<_>>(),
|
||||
column_index,
|
||||
));
|
||||
} else {
|
||||
out.insert(column.to_string(), value.clone());
|
||||
}
|
||||
} else {
|
||||
inner_table = Some(TableInside::Entries(
|
||||
column,
|
||||
&s,
|
||||
values.iter().collect::<Vec<_>>(),
|
||||
column_index,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
out.insert(column.to_string(), value.clone());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
out.insert(column.to_string(), value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut expanded = vec![];
|
||||
match inner_table {
|
||||
Some(TableInside::Entries(column, _, entries, parent_column_index)) => {
|
||||
for entry in entries {
|
||||
let base = out.clone();
|
||||
let (mut record_cols, mut record_vals) = (vec![], vec![]);
|
||||
let mut index = 0;
|
||||
for (col, val) in base.into_iter() {
|
||||
// meet the flattened column, push them to result record first
|
||||
// this can avoid output column order changed.
|
||||
if index == parent_column_index {
|
||||
record_cols.push(column.to_string());
|
||||
record_vals.push(entry.clone());
|
||||
}
|
||||
record_cols.push(col);
|
||||
record_vals.push(val);
|
||||
index += 1;
|
||||
}
|
||||
// the flattened column may be the last column in the original table.
|
||||
let mut expanded = vec![];
|
||||
match inner_table {
|
||||
Some(TableInside::Entries(column, _, entries, parent_column_index)) => {
|
||||
for entry in entries {
|
||||
let base = out.clone();
|
||||
let mut record = Record::new();
|
||||
let mut index = 0;
|
||||
for (col, val) in base.into_iter() {
|
||||
// meet the flattened column, push them to result record first
|
||||
// this can avoid output column order changed.
|
||||
if index == parent_column_index {
|
||||
record_cols.push(column.to_string());
|
||||
record_vals.push(entry.clone());
|
||||
record.push(column, entry.clone());
|
||||
}
|
||||
let record = Value::Record {
|
||||
cols: record_cols,
|
||||
vals: record_vals,
|
||||
span: tag,
|
||||
};
|
||||
expanded.push(record);
|
||||
record.push(col, val);
|
||||
index += 1;
|
||||
}
|
||||
// the flattened column may be the last column in the original table.
|
||||
if index == parent_column_index {
|
||||
record.push(column, entry.clone());
|
||||
}
|
||||
expanded.push(Value::record(record, tag));
|
||||
}
|
||||
Some(TableInside::FlattenedRows {
|
||||
columns,
|
||||
_span,
|
||||
values,
|
||||
parent_column_name,
|
||||
parent_column_index,
|
||||
}) => {
|
||||
for (inner_cols, inner_vals) in columns.into_iter().zip(values) {
|
||||
let base = out.clone();
|
||||
let (mut record_cols, mut record_vals) = (vec![], vec![]);
|
||||
let mut index = 0;
|
||||
}
|
||||
Some(TableInside::FlattenedRows {
|
||||
columns,
|
||||
_span,
|
||||
values,
|
||||
parent_column_name,
|
||||
parent_column_index,
|
||||
}) => {
|
||||
for (inner_cols, inner_vals) in columns.into_iter().zip(values) {
|
||||
let base = out.clone();
|
||||
let mut record = Record::new();
|
||||
let mut index = 0;
|
||||
|
||||
for (base_col, base_val) in base.into_iter() {
|
||||
// meet the flattened column, push them to result record first
|
||||
// this can avoid output column order changed.
|
||||
if index == parent_column_index {
|
||||
for (col, val) in inner_cols.iter().zip(inner_vals.iter()) {
|
||||
if record_cols.contains(col) {
|
||||
record_cols.push(format!("{parent_column_name}_{col}"));
|
||||
} else {
|
||||
record_cols.push(col.to_string());
|
||||
}
|
||||
record_vals.push(val.clone());
|
||||
}
|
||||
}
|
||||
|
||||
record_cols.push(base_col);
|
||||
record_vals.push(base_val);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
// the flattened column may be the last column in the original table.
|
||||
for (base_col, base_val) in base.into_iter() {
|
||||
// meet the flattened column, push them to result record first
|
||||
// this can avoid output column order changed.
|
||||
if index == parent_column_index {
|
||||
for (col, val) in inner_cols.iter().zip(inner_vals.iter()) {
|
||||
if record_cols.contains(col) {
|
||||
record_cols.push(format!("{parent_column_name}_{col}"));
|
||||
if record.cols.contains(col) {
|
||||
record.push(format!("{parent_column_name}_{col}"), val.clone());
|
||||
} else {
|
||||
record_cols.push(col.to_string());
|
||||
}
|
||||
record_vals.push(val.clone());
|
||||
record.push(col, val.clone());
|
||||
};
|
||||
}
|
||||
}
|
||||
let record = Value::Record {
|
||||
cols: record_cols,
|
||||
vals: record_vals,
|
||||
span: tag,
|
||||
};
|
||||
expanded.push(record);
|
||||
|
||||
record.push(base_col, base_val);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let record = Value::Record {
|
||||
cols: out.keys().map(|f| f.to_string()).collect::<Vec<_>>(),
|
||||
vals: out.values().cloned().collect(),
|
||||
span: tag,
|
||||
};
|
||||
expanded.push(record);
|
||||
|
||||
// the flattened column may be the last column in the original table.
|
||||
if index == parent_column_index {
|
||||
for (col, val) in inner_cols.iter().zip(inner_vals.iter()) {
|
||||
if record.cols.contains(col) {
|
||||
record.push(format!("{parent_column_name}_{col}"), val.clone());
|
||||
} else {
|
||||
record.push(col, val.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
expanded.push(Value::record(record, tag));
|
||||
}
|
||||
}
|
||||
expanded
|
||||
} else if item.as_list().is_ok() {
|
||||
if let Value::List { vals, span: _ } = item {
|
||||
vals.to_vec()
|
||||
} else {
|
||||
vec![]
|
||||
None => {
|
||||
expanded.push(Value::record(out.into_iter().collect(), tag));
|
||||
}
|
||||
} else {
|
||||
vec![item.clone()]
|
||||
}
|
||||
};
|
||||
res
|
||||
expanded
|
||||
} else if item.as_list().is_ok() {
|
||||
if let Value::List { vals, span: _ } = item {
|
||||
vals.to_vec()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
} else {
|
||||
vec![item.clone()]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::ast::{Call, CellPath};
|
||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
@ -68,7 +68,7 @@ impl Command for GroupBy {
|
||||
Example {
|
||||
description: "Group using a block which is evaluated against each input value",
|
||||
example: "[foo.txt bar.csv baz.txt] | group-by { path parse | get extension }",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["txt".to_string(), "csv".to_string()],
|
||||
vals: vec![
|
||||
Value::List {
|
||||
@ -83,14 +83,13 @@ impl Command for GroupBy {
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
|
||||
Example {
|
||||
description: "You can also group by raw values by leaving out the argument",
|
||||
example: "['1' '3' '1' '3' '2' '1' '1'] | group-by",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["1".to_string(), "3".to_string(), "2".to_string()],
|
||||
vals: vec![
|
||||
Value::List {
|
||||
@ -111,8 +110,7 @@ impl Command for GroupBy {
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -177,15 +175,13 @@ pub fn group_cell_path(
|
||||
group.push(value);
|
||||
}
|
||||
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
for (k, v) in groups {
|
||||
cols.push(k.to_string());
|
||||
vals.push(Value::List { vals: v, span });
|
||||
}
|
||||
|
||||
Ok(Value::Record { cols, vals, span })
|
||||
Ok(Value::record(
|
||||
groups
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, Value::list(v, span)))
|
||||
.collect(),
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn group_no_grouper(values: Vec<Value>, span: Span) -> Result<Value, ShellError> {
|
||||
@ -197,15 +193,13 @@ pub fn group_no_grouper(values: Vec<Value>, span: Span) -> Result<Value, ShellEr
|
||||
group.push(value);
|
||||
}
|
||||
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
for (k, v) in groups {
|
||||
cols.push(k.to_string());
|
||||
vals.push(Value::List { vals: v, span });
|
||||
}
|
||||
|
||||
Ok(Value::Record { cols, vals, span })
|
||||
Ok(Value::record(
|
||||
groups
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, Value::list(v, span)))
|
||||
.collect(),
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
// TODO: refactor this, it's a bit of a mess
|
||||
@ -285,15 +279,13 @@ fn group_closure(
|
||||
group.push(value);
|
||||
}
|
||||
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
for (k, v) in groups {
|
||||
cols.push(k.to_string());
|
||||
vals.push(Value::List { vals: v, span });
|
||||
}
|
||||
|
||||
Ok(Value::Record { cols, vals, span })
|
||||
Ok(Value::record(
|
||||
groups
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, Value::list(v, span)))
|
||||
.collect(),
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type,
|
||||
Value,
|
||||
Category, Config, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -37,15 +37,14 @@ impl Command for Headers {
|
||||
description: "Sets the column names for a table created by `split column`",
|
||||
example: r#""a b c|1 2 3" | split row "|" | split column " " | headers"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![
|
||||
Value::test_string("1"),
|
||||
Value::test_string("2"),
|
||||
Value::test_string("3"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -54,24 +53,22 @@ impl Command for Headers {
|
||||
example: r#""a b c|1 2 3|1 2 3 4" | split row "|" | split column " " | headers"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![
|
||||
Value::test_string("1"),
|
||||
Value::test_string("2"),
|
||||
Value::test_string("3"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns,
|
||||
vals: vec![
|
||||
Value::test_string("1"),
|
||||
Value::test_string("2"),
|
||||
Value::test_string("3"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -102,20 +99,17 @@ fn replace_headers(
|
||||
new_headers: &[String],
|
||||
) -> Result<Value, ShellError> {
|
||||
match value {
|
||||
Value::Record { cols, vals, span } => {
|
||||
let (cols, vals) = cols
|
||||
.into_iter()
|
||||
.zip(vals)
|
||||
Value::Record { val, span } => Ok(Value::record(
|
||||
val.into_iter()
|
||||
.filter_map(|(col, val)| {
|
||||
old_headers
|
||||
.iter()
|
||||
.position(|c| c == &col)
|
||||
.map(|i| (new_headers[i].clone(), val))
|
||||
})
|
||||
.unzip();
|
||||
|
||||
Ok(Value::Record { cols, vals, span })
|
||||
}
|
||||
.collect(),
|
||||
span,
|
||||
)),
|
||||
Value::List { vals, span } => {
|
||||
let vals = vals
|
||||
.into_iter()
|
||||
@ -148,8 +142,8 @@ fn extract_headers(
|
||||
config: &Config,
|
||||
) -> Result<(Vec<String>, Vec<String>), ShellError> {
|
||||
match value {
|
||||
Value::Record { cols, vals, .. } => {
|
||||
for v in vals {
|
||||
Value::Record { val: record, .. } => {
|
||||
for v in &record.vals {
|
||||
if !is_valid_header(v) {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: "needs compatible type: Null, String, Bool, Float, Int"
|
||||
@ -159,8 +153,9 @@ fn extract_headers(
|
||||
}
|
||||
}
|
||||
|
||||
let old_headers = cols.to_vec();
|
||||
let new_headers = vals
|
||||
let old_headers = record.cols.clone();
|
||||
let new_headers = record
|
||||
.vals
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, value)| {
|
||||
|
@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember};
|
||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -60,51 +60,56 @@ impl Command for Insert {
|
||||
vec![Example {
|
||||
description: "Insert a new entry into a single record",
|
||||
example: "{'name': 'nu', 'stars': 5} | insert alias 'Nushell'",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["name".into(), "stars".into(), "alias".into()],
|
||||
vals: vec![
|
||||
Value::test_string("nu"),
|
||||
Value::test_int(5),
|
||||
Value::test_string("Nushell"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Insert a new column into a table, populating all rows",
|
||||
example: "[[project, lang]; ['Nushell', 'Rust']] | insert type 'shell'",
|
||||
result: Some(Value::List { vals: vec![Value::Record { cols: vec!["project".into(), "lang".into(), "type".into()],
|
||||
vals: vec![Value::test_string("Nushell"), Value::test_string("Rust"), Value::test_string("shell")], span: Span::test_data()}], span: Span::test_data()}),
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["project".into(), "lang".into(), "type".into()],
|
||||
vals: vec![Value::test_string("Nushell"), Value::test_string("Rust"), Value::test_string("shell")],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Insert a column with values equal to their row index, plus the value of 'foo' in each row",
|
||||
example: "[[foo]; [7] [8] [9]] | enumerate | insert bar {|e| $e.item.foo + $e.index } | flatten",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols: vec!["index".into(), "foo".into(), "bar".into()],
|
||||
vals: vec![
|
||||
Value::test_int(0),
|
||||
Value::test_int(7),
|
||||
Value::test_int(7),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}, Value::Record {
|
||||
cols: vec!["index".into(),"foo".into(), "bar".into()],
|
||||
vals: vec![
|
||||
Value::test_int(1),
|
||||
Value::test_int(8),
|
||||
Value::test_int(9),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}, Value::Record {
|
||||
cols: vec!["index".into(), "foo".into(), "bar".into()],
|
||||
vals: vec![
|
||||
Value::test_int(2),
|
||||
Value::test_int(9),
|
||||
Value::test_int(11),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
vals: vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".into(), "foo".into(), "bar".into()],
|
||||
vals: vec![
|
||||
Value::test_int(0),
|
||||
Value::test_int(7),
|
||||
Value::test_int(7),
|
||||
],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".into(),"foo".into(), "bar".into()],
|
||||
vals: vec![
|
||||
Value::test_int(1),
|
||||
Value::test_int(8),
|
||||
Value::test_int(9),
|
||||
],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".into(), "foo".into(), "bar".into()],
|
||||
vals: vec![
|
||||
Value::test_int(2),
|
||||
Value::test_int(9),
|
||||
Value::test_int(11),
|
||||
],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
}]
|
||||
|
@ -94,9 +94,8 @@ impl Command for Items {
|
||||
};
|
||||
match input {
|
||||
PipelineData::Empty => Ok(PipelineData::Empty),
|
||||
PipelineData::Value(Value::Record { cols, vals, .. }, ..) => Ok(cols
|
||||
PipelineData::Value(Value::Record { val, .. }, ..) => Ok(val
|
||||
.into_iter()
|
||||
.zip(vals)
|
||||
.map_while(run_for_each_item)
|
||||
.into_pipeline_data(ctrlc)),
|
||||
// Errors
|
||||
|
@ -2,7 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Config, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Config, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
use std::cmp::max;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@ -22,8 +23,6 @@ enum IncludeInner {
|
||||
Yes,
|
||||
}
|
||||
|
||||
type RowEntries<'a> = Vec<(&'a Vec<String>, &'a Vec<Value>)>;
|
||||
|
||||
const EMPTY_COL_NAMES: &Vec<String> = &vec![];
|
||||
|
||||
impl Command for Join {
|
||||
@ -112,7 +111,7 @@ impl Command for Join {
|
||||
description: "Join two tables",
|
||||
example: "[{a: 1 b: 2}] | join [{a: 1 c: 3}] a",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["a".into(), "b".into(), "c".into()],
|
||||
vals: vec![
|
||||
Value::Int {
|
||||
@ -128,8 +127,7 @@ impl Command for Join {
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
}]
|
||||
@ -265,8 +263,8 @@ fn join_rows(
|
||||
result: &mut Vec<Value>,
|
||||
this: &Vec<Value>,
|
||||
this_join_key: &str,
|
||||
other: HashMap<String, RowEntries>,
|
||||
other_keys: &Vec<String>,
|
||||
other: HashMap<String, Vec<&Record>>,
|
||||
other_keys: &[String],
|
||||
shared_join_key: Option<&str>,
|
||||
join_type: &JoinType,
|
||||
include_inner: IncludeInner,
|
||||
@ -276,71 +274,60 @@ fn join_rows(
|
||||
) {
|
||||
for this_row in this {
|
||||
if let Value::Record {
|
||||
cols: this_cols,
|
||||
vals: this_vals,
|
||||
..
|
||||
val: this_record, ..
|
||||
} = this_row
|
||||
{
|
||||
if let Some(this_valkey) = this_row.get_data_by_key(this_join_key) {
|
||||
if let Some(other_rows) = other.get(&this_valkey.into_string(sep, config)) {
|
||||
if matches!(include_inner, IncludeInner::Yes) {
|
||||
for (other_cols, other_vals) in other_rows {
|
||||
for other_record in other_rows {
|
||||
// `other` table contains rows matching `this` row on the join column
|
||||
let (res_cols, res_vals) = match join_type {
|
||||
let record = match join_type {
|
||||
JoinType::Inner | JoinType::Right => merge_records(
|
||||
(other_cols, other_vals), // `other` (lookup) is the left input table
|
||||
(this_cols, this_vals),
|
||||
other_record, // `other` (lookup) is the left input table
|
||||
this_record,
|
||||
shared_join_key,
|
||||
),
|
||||
JoinType::Left => merge_records(
|
||||
(this_cols, this_vals), // `this` is the left input table
|
||||
(other_cols, other_vals),
|
||||
this_record, // `this` is the left input table
|
||||
other_record,
|
||||
shared_join_key,
|
||||
),
|
||||
_ => panic!("not implemented"),
|
||||
};
|
||||
result.push(Value::Record {
|
||||
cols: res_cols,
|
||||
vals: res_vals,
|
||||
span,
|
||||
})
|
||||
result.push(Value::record(record, span))
|
||||
}
|
||||
}
|
||||
} else if !matches!(join_type, JoinType::Inner) {
|
||||
// `other` table did not contain any rows matching
|
||||
// `this` row on the join column; emit a single joined
|
||||
// row with null values for columns not present,
|
||||
let other_vals = other_keys
|
||||
let other_record = other_keys
|
||||
.iter()
|
||||
.map(|key| {
|
||||
if Some(key.as_ref()) == shared_join_key {
|
||||
let val = if Some(key.as_ref()) == shared_join_key {
|
||||
this_row
|
||||
.get_data_by_key(key)
|
||||
.unwrap_or_else(|| Value::nothing(span))
|
||||
} else {
|
||||
Value::nothing(span)
|
||||
}
|
||||
};
|
||||
|
||||
(key.clone(), val)
|
||||
})
|
||||
.collect();
|
||||
let (res_cols, res_vals) = match join_type {
|
||||
JoinType::Inner | JoinType::Right => merge_records(
|
||||
(other_keys, &other_vals),
|
||||
(this_cols, this_vals),
|
||||
shared_join_key,
|
||||
),
|
||||
JoinType::Left => merge_records(
|
||||
(this_cols, this_vals),
|
||||
(other_keys, &other_vals),
|
||||
shared_join_key,
|
||||
),
|
||||
|
||||
let record = match join_type {
|
||||
JoinType::Inner | JoinType::Right => {
|
||||
merge_records(&other_record, this_record, shared_join_key)
|
||||
}
|
||||
JoinType::Left => {
|
||||
merge_records(this_record, &other_record, shared_join_key)
|
||||
}
|
||||
_ => panic!("not implemented"),
|
||||
};
|
||||
|
||||
result.push(Value::Record {
|
||||
cols: res_cols,
|
||||
vals: res_vals,
|
||||
span,
|
||||
})
|
||||
result.push(Value::record(record, span))
|
||||
}
|
||||
} // else { a row is missing a value for the join column }
|
||||
};
|
||||
@ -353,7 +340,7 @@ fn column_names(table: &[Value]) -> &Vec<String> {
|
||||
table
|
||||
.iter()
|
||||
.find_map(|val| match val {
|
||||
Value::Record { cols, .. } => Some(cols),
|
||||
Value::Record { val, .. } => Some(&val.cols),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(EMPTY_COL_NAMES)
|
||||
@ -367,13 +354,13 @@ fn lookup_table<'a>(
|
||||
sep: &str,
|
||||
cap: usize,
|
||||
config: &Config,
|
||||
) -> HashMap<String, RowEntries<'a>> {
|
||||
let mut map = HashMap::<String, RowEntries>::with_capacity(cap);
|
||||
) -> HashMap<String, Vec<&'a Record>> {
|
||||
let mut map = HashMap::<String, Vec<&'a Record>>::with_capacity(cap);
|
||||
for row in rows {
|
||||
if let Value::Record { cols, vals, .. } = row {
|
||||
if let Value::Record { val: record, .. } = row {
|
||||
if let Some(val) = &row.get_data_by_key(on) {
|
||||
let valkey = val.into_string(sep, config);
|
||||
map.entry(valkey).or_default().push((cols, vals));
|
||||
map.entry(valkey).or_default().push(record);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -383,31 +370,27 @@ fn lookup_table<'a>(
|
||||
// Merge `left` and `right` records, renaming keys in `right` where they clash
|
||||
// with keys in `left`. If `shared_key` is supplied then it is the name of a key
|
||||
// that should not be renamed (its values are guaranteed to be equal).
|
||||
fn merge_records(
|
||||
left: (&Vec<String>, &Vec<Value>),
|
||||
right: (&Vec<String>, &Vec<Value>),
|
||||
shared_key: Option<&str>,
|
||||
) -> (Vec<String>, Vec<Value>) {
|
||||
let ((l_keys, l_vals), (r_keys, r_vals)) = (left, right);
|
||||
let cap = max(l_keys.len(), r_keys.len());
|
||||
fn merge_records(left: &Record, right: &Record, shared_key: Option<&str>) -> Record {
|
||||
let cap = max(left.len(), right.len());
|
||||
let mut seen = HashSet::with_capacity(cap);
|
||||
let (mut res_keys, mut res_vals) = (Vec::with_capacity(cap), Vec::with_capacity(cap));
|
||||
for (k, v) in l_keys.iter().zip(l_vals) {
|
||||
res_keys.push(k.clone());
|
||||
res_vals.push(v.clone());
|
||||
let mut record = Record::with_capacity(cap);
|
||||
for (k, v) in left {
|
||||
record.push(k.clone(), v.clone());
|
||||
seen.insert(k);
|
||||
}
|
||||
|
||||
for (k, v) in r_keys.iter().zip(r_vals) {
|
||||
for (k, v) in right {
|
||||
let k_seen = seen.contains(k);
|
||||
let k_shared = shared_key == Some(k);
|
||||
// Do not output shared join key twice
|
||||
if !(k_seen && k_shared) {
|
||||
res_keys.push(if k_seen { format!("{}_", k) } else { k.clone() });
|
||||
res_vals.push(v.clone());
|
||||
record.push(
|
||||
if k_seen { format!("{}_", k) } else { k.clone() },
|
||||
v.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
(res_keys, res_vals)
|
||||
record
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Record,
|
||||
ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -48,18 +48,18 @@ repeating this process with row 1, and so on."#
|
||||
description: "Add an 'index' column to 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)],
|
||||
),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["name".to_string(), "index".to_string()],
|
||||
vals: vec![Value::test_string("a"), Value::test_int(1)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["name".to_string(), "index".to_string()],
|
||||
vals: vec![Value::test_string("b"), Value::test_int(2)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["name".to_string(), "index".to_string()],
|
||||
vals: vec![Value::test_string("c"), Value::test_int(3)],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -67,20 +67,19 @@ repeating this process with row 1, and so on."#
|
||||
Example {
|
||||
example: "{a: 1, b: 2} | merge {c: 3}",
|
||||
description: "Merge two records",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string(), "c".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
example: "[{columnA: A0 columnB: B0}] | merge [{columnA: 'A0*'}]",
|
||||
description: "Merge two tables, overwriting overlapping columns",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_record(
|
||||
vec!["columnA", "columnB"],
|
||||
vec![Value::test_string("A0*"), Value::test_string("B0")],
|
||||
)],
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["columnA".to_string(), "columnB".to_string()],
|
||||
vals: vec![Value::test_string("A0*"), Value::test_string("B0")],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -112,24 +111,12 @@ repeating this process with row 1, and so on."#
|
||||
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, vals) = do_merge(
|
||||
(inp_cols.to_vec(), inp_vals.to_vec()),
|
||||
(to_merge_cols.to_vec(), to_merge_vals.to_vec()),
|
||||
);
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: call.head,
|
||||
}
|
||||
}
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
},
|
||||
}
|
||||
}
|
||||
(Ok(inp), Some(to_merge)) => match to_merge.as_record() {
|
||||
Ok(to_merge) => Value::record(do_merge(inp, to_merge), call.head),
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
},
|
||||
},
|
||||
(_, None) => inp,
|
||||
(Err(error), _) => Value::Error {
|
||||
error: Box::new(error),
|
||||
@ -144,31 +131,9 @@ repeating this process with row 1, and so on."#
|
||||
}
|
||||
// record
|
||||
(
|
||||
PipelineData::Value(
|
||||
Value::Record {
|
||||
cols: inp_cols,
|
||||
vals: inp_vals,
|
||||
..
|
||||
},
|
||||
..,
|
||||
),
|
||||
Value::Record {
|
||||
cols: to_merge_cols,
|
||||
vals: to_merge_vals,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let (cols, vals) = do_merge(
|
||||
(inp_cols.to_vec(), inp_vals.to_vec()),
|
||||
(to_merge_cols.to_vec(), to_merge_vals.to_vec()),
|
||||
);
|
||||
Ok(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
PipelineData::Value(Value::Record { val: inp, .. }, ..),
|
||||
Value::Record { val: to_merge, .. },
|
||||
) => Ok(Value::record(do_merge(inp, &to_merge), call.head).into_pipeline_data()),
|
||||
(PipelineData::Value(val, ..), ..) => {
|
||||
// Only point the "value originates here" arrow at the merge value
|
||||
// if it was generated from a block. Otherwise, point at the pipeline value. -Leon 2022-10-27
|
||||
@ -194,27 +159,22 @@ repeating this process with row 1, and so on."#
|
||||
}
|
||||
}
|
||||
|
||||
fn do_merge(
|
||||
input_record: (Vec<String>, Vec<Value>),
|
||||
to_merge_record: (Vec<String>, Vec<Value>),
|
||||
) -> (Vec<String>, Vec<Value>) {
|
||||
let (mut result_cols, mut result_vals) = input_record;
|
||||
let (to_merge_cols, to_merge_vals) = to_merge_record;
|
||||
fn do_merge(input_record: &Record, to_merge_record: &Record) -> Record {
|
||||
let mut result = input_record.clone();
|
||||
|
||||
for (col, val) in to_merge_cols.into_iter().zip(to_merge_vals) {
|
||||
let pos = result_cols.iter().position(|c| c == &col);
|
||||
for (col, val) in to_merge_record {
|
||||
let pos = result.cols.iter().position(|c| c == col);
|
||||
// if find, replace existing data, else, push new data.
|
||||
match pos {
|
||||
Some(index) => {
|
||||
result_vals[index] = val;
|
||||
result.vals[index] = val.clone();
|
||||
}
|
||||
None => {
|
||||
result_cols.push(col);
|
||||
result_vals.push(val);
|
||||
result.push(col, val.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
(result_cols, result_vals)
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Record,
|
||||
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -54,18 +54,18 @@ impl Command for Move {
|
||||
result:
|
||||
Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(
|
||||
vec!["index", "name", "value"],
|
||||
vec![Value::test_int(1), Value::test_string("foo"), Value::test_string("a")],
|
||||
),
|
||||
Value::test_record(
|
||||
vec!["index", "name", "value"],
|
||||
vec![Value::test_int(2), Value::test_string("bar"), Value::test_string("b")],
|
||||
),
|
||||
Value::test_record(
|
||||
vec!["index", "name", "value"],
|
||||
vec![Value::test_int(3), Value::test_string("baz"), Value::test_string("c")],
|
||||
),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".to_string(), "name".to_string(), "value".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_string("foo"), Value::test_string("a")],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".to_string(), "name".to_string(), "value".to_string()],
|
||||
vals: vec![Value::test_int(2), Value::test_string("bar"), Value::test_string("b")],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".to_string(), "name".to_string(), "value".to_string()],
|
||||
vals: vec![Value::test_int(3), Value::test_string("baz"), Value::test_string("c")],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
})
|
||||
@ -76,18 +76,18 @@ impl Command for Move {
|
||||
result:
|
||||
Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(
|
||||
vec!["index", "value", "name"],
|
||||
vec![Value::test_int(1), Value::test_string("a"), Value::test_string("foo")],
|
||||
),
|
||||
Value::test_record(
|
||||
vec!["index", "value", "name"],
|
||||
vec![Value::test_int(2), Value::test_string("b"), Value::test_string("bar")],
|
||||
),
|
||||
Value::test_record(
|
||||
vec!["index", "value", "name"],
|
||||
vec![Value::test_int(3), Value::test_string("c"), Value::test_string("baz")],
|
||||
),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".to_string(), "value".to_string(), "name".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_string("a"), Value::test_string("foo")],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".to_string(), "value".to_string(), "name".to_string()],
|
||||
vals: vec![Value::test_int(2), Value::test_string("b"), Value::test_string("bar")],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["index".to_string(), "value".to_string(), "name".to_string()],
|
||||
vals: vec![Value::test_int(3), Value::test_string("c"), Value::test_string("baz")],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
})
|
||||
@ -95,10 +95,10 @@ impl Command for Move {
|
||||
Example {
|
||||
example: "{ name: foo, value: a, index: 1 } | move name --before index",
|
||||
description: "Move columns of a record",
|
||||
result: Some(Value::test_record(
|
||||
vec!["value", "name", "index"],
|
||||
vec![Value::test_string("a"), Value::test_string("foo"), Value::test_int(1)],
|
||||
))
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["value".to_string(), "name".to_string(), "index".to_string()],
|
||||
vals: vec![Value::test_string("a"), Value::test_string("foo"), Value::test_int(1)],
|
||||
}))
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -150,18 +150,14 @@ impl Command for Move {
|
||||
match input {
|
||||
PipelineData::Value(Value::List { .. }, ..) | PipelineData::ListStream { .. } => {
|
||||
let res = input.into_iter().map(move |x| match x.as_record() {
|
||||
Ok((inp_cols, inp_vals)) => match move_record_columns(
|
||||
inp_cols,
|
||||
inp_vals,
|
||||
&columns,
|
||||
&before_or_after,
|
||||
call.head,
|
||||
) {
|
||||
Ok(val) => val,
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
},
|
||||
},
|
||||
Ok(record) => {
|
||||
match move_record_columns(record, &columns, &before_or_after, call.head) {
|
||||
Ok(val) => val,
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
},
|
||||
}
|
||||
}
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
},
|
||||
@ -173,21 +169,12 @@ impl Command for Move {
|
||||
Ok(res.into_pipeline_data(ctrlc))
|
||||
}
|
||||
}
|
||||
PipelineData::Value(
|
||||
Value::Record {
|
||||
cols: inp_cols,
|
||||
vals: inp_vals,
|
||||
..
|
||||
},
|
||||
..,
|
||||
) => Ok(move_record_columns(
|
||||
&inp_cols,
|
||||
&inp_vals,
|
||||
&columns,
|
||||
&before_or_after,
|
||||
call.head,
|
||||
)?
|
||||
.into_pipeline_data()),
|
||||
PipelineData::Value(Value::Record { val, .. }, ..) => {
|
||||
Ok(
|
||||
move_record_columns(&val, &columns, &before_or_after, call.head)?
|
||||
.into_pipeline_data(),
|
||||
)
|
||||
}
|
||||
_ => Err(ShellError::PipelineMismatch {
|
||||
exp_input_type: "record or table".to_string(),
|
||||
dst_span: call.head,
|
||||
@ -199,8 +186,7 @@ impl Command for Move {
|
||||
|
||||
// Move columns within a record
|
||||
fn move_record_columns(
|
||||
inp_cols: &[String],
|
||||
inp_vals: &[Value],
|
||||
record: &Record,
|
||||
columns: &[Value],
|
||||
before_or_after: &Spanned<BeforeOrAfter>,
|
||||
span: Span,
|
||||
@ -210,7 +196,7 @@ fn move_record_columns(
|
||||
// Check if before/after column exist
|
||||
match &before_or_after.item {
|
||||
BeforeOrAfter::After(after) => {
|
||||
if !inp_cols.contains(after) {
|
||||
if !record.cols.contains(after) {
|
||||
return Err(ShellError::GenericError(
|
||||
"Cannot move columns".to_string(),
|
||||
"column does not exist".to_string(),
|
||||
@ -221,7 +207,7 @@ fn move_record_columns(
|
||||
}
|
||||
}
|
||||
BeforeOrAfter::Before(before) => {
|
||||
if !inp_cols.contains(before) {
|
||||
if !record.cols.contains(before) {
|
||||
return Err(ShellError::GenericError(
|
||||
"Cannot move columns".to_string(),
|
||||
"column does not exist".to_string(),
|
||||
@ -237,7 +223,11 @@ fn move_record_columns(
|
||||
for column in columns.iter() {
|
||||
let column_str = column.as_string()?;
|
||||
|
||||
if let Some(idx) = inp_cols.iter().position(|inp_col| &column_str == inp_col) {
|
||||
if let Some(idx) = record
|
||||
.cols
|
||||
.iter()
|
||||
.position(|inp_col| &column_str == inp_col)
|
||||
{
|
||||
column_idx.push(idx);
|
||||
} else {
|
||||
return Err(ShellError::GenericError(
|
||||
@ -250,19 +240,16 @@ fn move_record_columns(
|
||||
}
|
||||
}
|
||||
|
||||
let mut out_cols: Vec<String> = Vec::with_capacity(inp_cols.len());
|
||||
let mut out_vals: Vec<Value> = Vec::with_capacity(inp_vals.len());
|
||||
let mut out = Record::with_capacity(record.len());
|
||||
|
||||
for (i, (inp_col, inp_val)) in inp_cols.iter().zip(inp_vals).enumerate() {
|
||||
for (i, (inp_col, inp_val)) in record.iter().enumerate() {
|
||||
match &before_or_after.item {
|
||||
BeforeOrAfter::After(after) if after == inp_col => {
|
||||
out_cols.push(inp_col.into());
|
||||
out_vals.push(inp_val.clone());
|
||||
out.push(inp_col.clone(), inp_val.clone());
|
||||
|
||||
for idx in column_idx.iter() {
|
||||
if let (Some(col), Some(val)) = (inp_cols.get(*idx), inp_vals.get(*idx)) {
|
||||
out_cols.push(col.into());
|
||||
out_vals.push(val.clone());
|
||||
if let (Some(col), Some(val)) = (record.cols.get(*idx), record.vals.get(*idx)) {
|
||||
out.push(col.clone(), val.clone());
|
||||
} else {
|
||||
return Err(ShellError::NushellFailedSpanned {
|
||||
msg: "Error indexing input columns".to_string(),
|
||||
@ -274,9 +261,8 @@ fn move_record_columns(
|
||||
}
|
||||
BeforeOrAfter::Before(before) if before == inp_col => {
|
||||
for idx in column_idx.iter() {
|
||||
if let (Some(col), Some(val)) = (inp_cols.get(*idx), inp_vals.get(*idx)) {
|
||||
out_cols.push(col.into());
|
||||
out_vals.push(val.clone());
|
||||
if let (Some(col), Some(val)) = (record.cols.get(*idx), record.vals.get(*idx)) {
|
||||
out.push(col.clone(), val.clone());
|
||||
} else {
|
||||
return Err(ShellError::NushellFailedSpanned {
|
||||
msg: "Error indexing input columns".to_string(),
|
||||
@ -286,23 +272,17 @@ fn move_record_columns(
|
||||
}
|
||||
}
|
||||
|
||||
out_cols.push(inp_col.into());
|
||||
out_vals.push(inp_val.clone());
|
||||
out.push(inp_col.clone(), inp_val.clone());
|
||||
}
|
||||
_ => {
|
||||
if !column_idx.contains(&i) {
|
||||
out_cols.push(inp_col.into());
|
||||
out_vals.push(inp_val.clone());
|
||||
out.push(inp_col.clone(), inp_val.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Record {
|
||||
cols: out_cols,
|
||||
vals: out_vals,
|
||||
span,
|
||||
})
|
||||
Ok(Value::record(out, span))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::{Call, CellPath};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -63,35 +63,31 @@ impl Command for Reject {
|
||||
description: "Reject a column in a table",
|
||||
example: "[[a, b]; [1, 2]] | reject a",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["b".to_string()],
|
||||
vals: vec![Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Reject the specified field in a record",
|
||||
example: "{a: 1, b: 2} | reject a",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["b".into()],
|
||||
vals: vec![Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Reject a nested field in a record",
|
||||
example: "{a: {b: 3, c: 5}} | reject a.b",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".into()],
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["c".into()],
|
||||
vals: vec![Value::test_int(5)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})],
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block_with_early_return, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -56,11 +56,10 @@ impl Command for Rename {
|
||||
description: "Rename a column",
|
||||
example: "[[a, b]; [1, 2]] | rename my_column",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["my_column".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -68,11 +67,10 @@ impl Command for Rename {
|
||||
description: "Rename many columns",
|
||||
example: "[[a, b, c]; [1, 2, 3]] | rename eggs ham bacon",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["eggs".to_string(), "ham".to_string(), "bacon".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -80,31 +78,28 @@ impl Command for Rename {
|
||||
description: "Rename a specific column",
|
||||
example: "[[a, b, c]; [1, 2, 3]] | rename -c [a ham]",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ham".to_string(), "b".to_string(), "c".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Rename the fields of a record",
|
||||
example: "{a: 1 b: 2} | rename x y",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["x".to_string(), "y".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Rename fields based on a given closure",
|
||||
example: "{abc: 1, bbc: 2} | rename -b {str replace -a 'b' 'z'}",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["azc".to_string(), "zzc".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -162,14 +157,13 @@ fn rename(
|
||||
.map(
|
||||
move |item| match item {
|
||||
Value::Record {
|
||||
mut cols,
|
||||
vals,
|
||||
val: mut record,
|
||||
span,
|
||||
} => {
|
||||
if let Some((engine_state, block, mut stack, env_vars, env_hidden)) =
|
||||
block_info.clone()
|
||||
{
|
||||
for c in &mut cols {
|
||||
for c in &mut record.cols {
|
||||
stack.with_env(&env_vars, &env_hidden);
|
||||
|
||||
if let Some(var) = block.signature.get_positional(0) {
|
||||
@ -197,7 +191,7 @@ fn rename(
|
||||
match &specified_column {
|
||||
Some(c) => {
|
||||
// check if the specified column to be renamed exists
|
||||
if !cols.contains(&c[0]) {
|
||||
if !record.cols.contains(&c[0]) {
|
||||
return Value::Error {
|
||||
error: Box::new(ShellError::UnsupportedInput(
|
||||
format!(
|
||||
@ -212,26 +206,26 @@ fn rename(
|
||||
)),
|
||||
};
|
||||
}
|
||||
for (idx, val) in cols.iter_mut().enumerate() {
|
||||
for (idx, val) in record.cols.iter_mut().enumerate() {
|
||||
if *val == c[0] {
|
||||
cols[idx] = c[1].to_string();
|
||||
record.cols[idx] = c[1].to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
for (idx, val) in columns.iter().enumerate() {
|
||||
if idx >= cols.len() {
|
||||
if idx >= record.len() {
|
||||
// skip extra new columns names if we already reached the final column
|
||||
break;
|
||||
}
|
||||
cols[idx] = val.clone();
|
||||
record.cols[idx] = val.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value::Record { cols, vals, span }
|
||||
Value::record(record, span)
|
||||
}
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => item.clone(),
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError, Signature,
|
||||
Span, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -53,8 +53,14 @@ impl Command for Reverse {
|
||||
description: "Reverse a table",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(vec!["a"], vec![Value::test_int(2)]),
|
||||
Value::test_record(vec!["a"], vec![Value::test_int(1)]),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(2)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(1)],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
|
@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
PipelineIterator, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
PipelineIterator, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
|
||||
@ -152,14 +152,20 @@ produce a table, a list will produce a list, and a record will produce a record.
|
||||
description: "Select a column in a table",
|
||||
example: "[{a: a b: b}] | select a",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_record(vec!["a"], vec![Value::test_string("a")])],
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_string("a")]
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Select a field in a record",
|
||||
example: "{a: a b: b} | select a",
|
||||
result: Some(Value::test_record(vec!["a"], vec![Value::test_string("a")])),
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_string("a")]
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Select just the `name` column",
|
||||
@ -256,14 +262,12 @@ fn select(
|
||||
let mut columns_with_value = Vec::new();
|
||||
for input_val in input_vals {
|
||||
if !columns.is_empty() {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
for path in &columns {
|
||||
//FIXME: improve implementation to not clone
|
||||
match input_val.clone().follow_cell_path(&path.members, false) {
|
||||
Ok(fetcher) => {
|
||||
cols.push(path.into_string().replace('.', "_"));
|
||||
vals.push(fetcher);
|
||||
record.push(path.into_string().replace('.', "_"), fetcher);
|
||||
if !columns_with_value.contains(&path) {
|
||||
columns_with_value.push(path);
|
||||
}
|
||||
@ -274,7 +278,7 @@ fn select(
|
||||
}
|
||||
}
|
||||
|
||||
output.push(Value::Record { cols, vals, span })
|
||||
output.push(Value::record(record, span))
|
||||
} else {
|
||||
output.push(input_val)
|
||||
}
|
||||
@ -290,23 +294,17 @@ fn select(
|
||||
|
||||
for x in stream {
|
||||
if !columns.is_empty() {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
for path in &columns {
|
||||
//FIXME: improve implementation to not clone
|
||||
match x.clone().follow_cell_path(&path.members, false) {
|
||||
Ok(value) => {
|
||||
cols.push(path.into_string().replace('.', "_"));
|
||||
vals.push(value);
|
||||
record.push(path.into_string().replace('.', "_"), value);
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
values.push(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: call_span,
|
||||
});
|
||||
values.push(Value::record(record, call_span));
|
||||
} else {
|
||||
values.push(x);
|
||||
}
|
||||
@ -318,27 +316,21 @@ fn select(
|
||||
}
|
||||
PipelineData::Value(v, metadata, ..) => {
|
||||
if !columns.is_empty() {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
|
||||
for cell_path in columns {
|
||||
// FIXME: remove clone
|
||||
match v.clone().follow_cell_path(&cell_path.members, false) {
|
||||
Ok(result) => {
|
||||
cols.push(cell_path.into_string().replace('.', "_"));
|
||||
vals.push(result);
|
||||
record.push(cell_path.into_string().replace('.', "_"), result);
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: call_span,
|
||||
}
|
||||
.into_pipeline_data()
|
||||
.set_metadata(metadata))
|
||||
Ok(Value::record(record, call_span)
|
||||
.into_pipeline_data()
|
||||
.set_metadata(metadata))
|
||||
} else {
|
||||
Ok(v.into_pipeline_data().set_metadata(metadata))
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Record,
|
||||
ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -55,11 +55,10 @@ impl Command for Skip {
|
||||
description: "Skip two rows of a table",
|
||||
example: "[[editions]; [2015] [2018] [2021]] | skip 2",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["editions".to_owned()],
|
||||
vals: vec![Value::test_int(2021)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Closure, Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError, Signature,
|
||||
Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -62,8 +62,14 @@ impl Command for SkipUntil {
|
||||
example: "[{a: -2} {a: 0} {a: 2} {a: -1}] | skip until {|x| $x.a > 0 }",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(vec!["a"], vec![Value::test_int(2)]),
|
||||
Value::test_record(vec!["a"], vec![Value::test_int(-1)]),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(2)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(-1)],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Closure, Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError, Signature,
|
||||
Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -62,9 +62,18 @@ impl Command for SkipWhile {
|
||||
example: "[{a: -2} {a: 0} {a: 2} {a: -1}] | skip while {|x| $x.a < 0 }",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(vec!["a"], vec![Value::test_int(0)]),
|
||||
Value::test_record(vec!["a"], vec![Value::test_int(2)]),
|
||||
Value::test_record(vec!["a"], vec![Value::test_int(-1)]),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(0)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(2)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(-1)],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
|
@ -2,8 +2,8 @@ use alphanumeric_sort::compare_str;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Record,
|
||||
ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
@ -113,20 +113,18 @@ impl Command for Sort {
|
||||
Example {
|
||||
description: "Sort record by key (case-insensitive)",
|
||||
example: "{b: 3, a: 4} | sort",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_int(4), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Sort record by value",
|
||||
example: "{b: 4, a: 3, c:1} | sort -v",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["c".to_string(), "a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(3), Value::test_int(4)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -145,17 +143,9 @@ impl Command for Sort {
|
||||
|
||||
match input {
|
||||
// Records have two sorting methods, toggled by presence or absence of -v
|
||||
PipelineData::Value(Value::Record { cols, vals, span }, ..) => {
|
||||
PipelineData::Value(Value::Record { val, span }, ..) => {
|
||||
let sort_by_value = call.has_flag("values");
|
||||
let record = sort_record(
|
||||
cols,
|
||||
vals,
|
||||
span,
|
||||
sort_by_value,
|
||||
reverse,
|
||||
insensitive,
|
||||
natural,
|
||||
);
|
||||
let record = sort_record(val, span, sort_by_value, reverse, insensitive, natural);
|
||||
Ok(record.into_pipeline_data())
|
||||
}
|
||||
// Other values are sorted here
|
||||
@ -185,15 +175,14 @@ impl Command for Sort {
|
||||
}
|
||||
|
||||
fn sort_record(
|
||||
cols: Vec<String>,
|
||||
vals: Vec<Value>,
|
||||
record: Record,
|
||||
rec_span: Span,
|
||||
sort_by_value: bool,
|
||||
reverse: bool,
|
||||
insensitive: bool,
|
||||
natural: bool,
|
||||
) -> Value {
|
||||
let mut input_pairs: Vec<(String, Value)> = cols.into_iter().zip(vals).collect();
|
||||
let mut input_pairs: Vec<(String, Value)> = record.into_iter().collect();
|
||||
input_pairs.sort_by(|a, b| {
|
||||
// Extract the data (if sort_by_value) or the column names for comparison
|
||||
let left_res = if sort_by_value {
|
||||
@ -248,21 +237,11 @@ fn sort_record(
|
||||
}
|
||||
});
|
||||
|
||||
let mut new_cols = Vec::with_capacity(input_pairs.len());
|
||||
let mut new_vals = Vec::with_capacity(input_pairs.len());
|
||||
for (col, val) in input_pairs {
|
||||
new_cols.push(col);
|
||||
new_vals.push(val)
|
||||
}
|
||||
if reverse {
|
||||
new_cols.reverse();
|
||||
new_vals.reverse();
|
||||
}
|
||||
Value::Record {
|
||||
cols: new_cols,
|
||||
vals: new_vals,
|
||||
span: rec_span,
|
||||
input_pairs.reverse();
|
||||
}
|
||||
|
||||
Value::record(input_pairs.into_iter().collect(), rec_span)
|
||||
}
|
||||
|
||||
pub fn sort(
|
||||
@ -272,12 +251,8 @@ pub fn sort(
|
||||
natural: bool,
|
||||
) -> Result<(), ShellError> {
|
||||
match vec.first() {
|
||||
Some(Value::Record {
|
||||
cols,
|
||||
vals: _input_vals,
|
||||
..
|
||||
}) => {
|
||||
let columns = cols.clone();
|
||||
Some(Value::Record { val, .. }) => {
|
||||
let columns = val.cols.clone();
|
||||
vec.sort_by(|a, b| process(a, b, &columns, span, insensitive, natural));
|
||||
}
|
||||
_ => {
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError, Signature,
|
||||
Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -60,18 +60,18 @@ impl Command for SortBy {
|
||||
example: "[[fruit count]; [apple 9] [pear 3] [orange 7]] | sort-by fruit -r",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(
|
||||
vec!["fruit", "count"],
|
||||
vec![Value::test_string("pear"), Value::test_int(3)],
|
||||
),
|
||||
Value::test_record(
|
||||
vec!["fruit", "count"],
|
||||
vec![Value::test_string("orange"), Value::test_int(7)],
|
||||
),
|
||||
Value::test_record(
|
||||
vec!["fruit", "count"],
|
||||
vec![Value::test_string("apple"), Value::test_int(9)],
|
||||
),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["fruit".to_string(), "count".to_string()],
|
||||
vals: vec![Value::test_string("pear"), Value::test_int(3)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["fruit".to_string(), "count".to_string()],
|
||||
vals: vec![Value::test_string("orange"), Value::test_int(7)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["fruit".to_string(), "count".to_string()],
|
||||
vals: vec![Value::test_string("apple"), Value::test_int(9)],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
|
@ -3,8 +3,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -48,13 +48,13 @@ impl Command for SplitBy {
|
||||
{ name: 'storm', lang: 'rs', 'year': '2021' }
|
||||
]
|
||||
} | split-by lang"#,
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["rb".to_string(), "rs".to_string()],
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["2019".to_string()],
|
||||
vals: vec![Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"lang".to_string(),
|
||||
@ -65,17 +65,15 @@ impl Command for SplitBy {
|
||||
Value::test_string("rb"),
|
||||
Value::test_string("2019"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["2019".to_string(), "2021".to_string()],
|
||||
vals: vec![
|
||||
Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"lang".to_string(),
|
||||
@ -86,12 +84,11 @@ impl Command for SplitBy {
|
||||
Value::test_string("rs"),
|
||||
Value::test_string("2019"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"lang".to_string(),
|
||||
@ -102,16 +99,13 @@ impl Command for SplitBy {
|
||||
Value::test_string("rs"),
|
||||
Value::test_string("2021"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -203,15 +197,13 @@ fn data_group(
|
||||
group.push(value);
|
||||
}
|
||||
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
for (k, v) in groups {
|
||||
cols.push(k.to_string());
|
||||
vals.push(Value::List { vals: v, span });
|
||||
}
|
||||
|
||||
Ok(Value::Record { cols, vals, span })
|
||||
Ok(Value::record(
|
||||
groups
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, Value::list(v, span)))
|
||||
.collect(),
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
@ -222,32 +214,17 @@ pub fn data_split(
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let mut splits = indexmap::IndexMap::new();
|
||||
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
match value {
|
||||
PipelineData::Value(
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: grouped_rows,
|
||||
span,
|
||||
},
|
||||
_,
|
||||
) => {
|
||||
for (idx, list) in grouped_rows.iter().enumerate() {
|
||||
PipelineData::Value(Value::Record { val: grouped, span }, _) => {
|
||||
for (idx, list) in grouped.vals.iter().enumerate() {
|
||||
match data_group(list, splitter, span) {
|
||||
Ok(grouped) => {
|
||||
if let Value::Record {
|
||||
vals: li,
|
||||
cols: sub_cols,
|
||||
..
|
||||
} = grouped
|
||||
{
|
||||
for (inner_idx, subset) in li.iter().enumerate() {
|
||||
Ok(grouped_vals) => {
|
||||
if let Value::Record { val: sub, .. } = grouped_vals {
|
||||
for (inner_idx, subset) in sub.vals.iter().enumerate() {
|
||||
let s: &mut IndexMap<String, Value> =
|
||||
splits.entry(sub_cols[inner_idx].clone()).or_default();
|
||||
splits.entry(sub.cols[inner_idx].clone()).or_default();
|
||||
|
||||
s.insert(cols[idx].clone(), subset.clone());
|
||||
s.insert(grouped.cols[idx].clone(), subset.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -266,28 +243,12 @@ pub fn data_split(
|
||||
}
|
||||
}
|
||||
|
||||
for (k, rows) in splits {
|
||||
cols.push(k.to_string());
|
||||
let record = splits
|
||||
.into_iter()
|
||||
.map(|(k, rows)| (k, Value::record(rows.into_iter().collect(), span)))
|
||||
.collect();
|
||||
|
||||
let mut sub_cols = vec![];
|
||||
let mut sub_vals = vec![];
|
||||
|
||||
for (k, v) in rows {
|
||||
sub_cols.push(k);
|
||||
sub_vals.push(v);
|
||||
}
|
||||
|
||||
vals.push(Value::Record {
|
||||
cols: sub_cols,
|
||||
vals: sub_vals,
|
||||
span,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(PipelineData::Value(
|
||||
Value::Record { cols, vals, span },
|
||||
None,
|
||||
))
|
||||
Ok(PipelineData::Value(Value::record(record, span), None))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError, Signature,
|
||||
Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -125,8 +125,14 @@ impl Command for Take {
|
||||
example: "[[editions]; [2015] [2018] [2021]] | take 2",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(vec!["editions"], vec![Value::test_int(2015)]),
|
||||
Value::test_record(vec!["editions"], vec![Value::test_int(2018)]),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["editions".to_string()],
|
||||
vals: vec![Value::test_int(2015)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["editions".to_string()],
|
||||
vals: vec![Value::test_int(2018)],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Closure, Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError, Signature,
|
||||
Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -58,8 +58,14 @@ impl Command for TakeUntil {
|
||||
example: "[{a: -1} {a: -2} {a: 9} {a: 1}] | take until {|x| $x.a > 0 }",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(vec!["a"], vec![Value::test_int(-1)]),
|
||||
Value::test_record(vec!["a"], vec![Value::test_int(-2)]),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(-1)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(-2)],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Closure, Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError, Signature,
|
||||
Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -58,8 +58,14 @@ impl Command for TakeWhile {
|
||||
example: "[{a: -1} {a: -2} {a: 9} {a: 1}] | take while {|x| $x.a < 0 }",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(vec!["a"], vec![Value::test_int(-1)]),
|
||||
Value::test_record(vec!["a"], vec![Value::test_int(-2)]),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(-1)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(-2)],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
|
@ -3,8 +3,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||
Spanned, SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError, Signature,
|
||||
Span, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -90,16 +90,14 @@ impl Command for Transpose {
|
||||
example: "[[c1 c2]; [1 2]] | transpose",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column0".to_string(), "column1".to_string()],
|
||||
vals: vec![Value::test_string("c1"), Value::test_int(1)],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column0".to_string(), "column1".to_string()],
|
||||
vals: vec![Value::test_string("c2"), Value::test_int(2)],
|
||||
span,
|
||||
},
|
||||
}),
|
||||
],
|
||||
span,
|
||||
}),
|
||||
@ -109,16 +107,14 @@ impl Command for Transpose {
|
||||
example: "[[c1 c2]; [1 2]] | transpose key val",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["key".to_string(), "val".to_string()],
|
||||
vals: vec![Value::test_string("c1"), Value::test_int(1)],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["key".to_string(), "val".to_string()],
|
||||
vals: vec![Value::test_string("c2"), Value::test_int(2)],
|
||||
span,
|
||||
},
|
||||
}),
|
||||
],
|
||||
span,
|
||||
}),
|
||||
@ -129,16 +125,14 @@ impl Command for Transpose {
|
||||
example: "[[c1 c2]; [1 2]] | transpose -i val",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["val".to_string()],
|
||||
vals: vec![Value::test_int(1)],
|
||||
span,
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["val".to_string()],
|
||||
vals: vec![Value::test_int(2)],
|
||||
span,
|
||||
},
|
||||
}),
|
||||
],
|
||||
span,
|
||||
}),
|
||||
@ -146,11 +140,10 @@ impl Command for Transpose {
|
||||
Example {
|
||||
description: "Transfer back to record with -d flag",
|
||||
example: "{c1: 1, c2: 2} | transpose | transpose -i -r -d",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["c1".to_string(), "c2".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(2)],
|
||||
span,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -248,24 +241,26 @@ pub fn transpose(
|
||||
.into_iter()
|
||||
.map(move |desc| {
|
||||
let mut column_num: usize = 0;
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
|
||||
if !args.ignore_titles && !args.header_row {
|
||||
cols.push(headers[column_num].clone());
|
||||
vals.push(Value::string(desc.clone(), name));
|
||||
record.push(
|
||||
headers[column_num].clone(),
|
||||
Value::string(desc.clone(), name),
|
||||
);
|
||||
column_num += 1
|
||||
}
|
||||
|
||||
for i in input.clone() {
|
||||
match &i.get_data_by_key(&desc) {
|
||||
Some(x) => {
|
||||
if args.keep_all && cols.contains(&headers[column_num]) {
|
||||
let index = cols
|
||||
if args.keep_all && record.cols.contains(&headers[column_num]) {
|
||||
let index = record
|
||||
.cols
|
||||
.iter()
|
||||
.position(|y| y == &headers[column_num])
|
||||
.expect("value is contained.");
|
||||
let new_val = match &vals[index] {
|
||||
let new_val = match &record.vals[index] {
|
||||
Value::List { vals, span } => {
|
||||
let mut vals = vals.clone();
|
||||
vals.push(x.clone());
|
||||
@ -279,32 +274,31 @@ pub fn transpose(
|
||||
span: v.expect_span(),
|
||||
},
|
||||
};
|
||||
cols.remove(index);
|
||||
vals.remove(index);
|
||||
record.cols.remove(index);
|
||||
record.vals.remove(index);
|
||||
|
||||
cols.push(headers[column_num].clone());
|
||||
vals.push(new_val);
|
||||
} else if args.keep_last && cols.contains(&headers[column_num]) {
|
||||
let index = cols
|
||||
record.push(headers[column_num].clone(), new_val);
|
||||
} else if args.keep_last && record.cols.contains(&headers[column_num]) {
|
||||
let index = record
|
||||
.cols
|
||||
.iter()
|
||||
.position(|y| y == &headers[column_num])
|
||||
.expect("value is contained.");
|
||||
cols.remove(index);
|
||||
vals.remove(index);
|
||||
cols.push(headers[column_num].clone());
|
||||
vals.push(x.clone());
|
||||
} else if !cols.contains(&headers[column_num]) {
|
||||
cols.push(headers[column_num].clone());
|
||||
vals.push(x.clone());
|
||||
record.cols.remove(index);
|
||||
record.vals.remove(index);
|
||||
record.push(headers[column_num].clone(), x.clone());
|
||||
} else if !record.cols.contains(&headers[column_num]) {
|
||||
record.push(headers[column_num].clone(), x.clone());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if args.keep_all && cols.contains(&headers[column_num]) {
|
||||
let index = cols
|
||||
if args.keep_all && record.cols.contains(&headers[column_num]) {
|
||||
let index = record
|
||||
.cols
|
||||
.iter()
|
||||
.position(|y| y == &headers[column_num])
|
||||
.expect("value is contained.");
|
||||
let new_val = match &vals[index] {
|
||||
let new_val = match &record.vals[index] {
|
||||
Value::List { vals, span } => {
|
||||
let mut vals = vals.clone();
|
||||
vals.push(Value::nothing(name));
|
||||
@ -318,34 +312,28 @@ pub fn transpose(
|
||||
span: v.expect_span(),
|
||||
},
|
||||
};
|
||||
cols.remove(index);
|
||||
vals.remove(index);
|
||||
record.cols.remove(index);
|
||||
record.vals.remove(index);
|
||||
|
||||
cols.push(headers[column_num].clone());
|
||||
vals.push(new_val);
|
||||
} else if args.keep_last && cols.contains(&headers[column_num]) {
|
||||
let index = cols
|
||||
record.push(headers[column_num].clone(), new_val);
|
||||
} else if args.keep_last && record.cols.contains(&headers[column_num]) {
|
||||
let index = record
|
||||
.cols
|
||||
.iter()
|
||||
.position(|y| y == &headers[column_num])
|
||||
.expect("value is contained.");
|
||||
cols.remove(index);
|
||||
vals.remove(index);
|
||||
cols.push(headers[column_num].clone());
|
||||
vals.push(Value::nothing(name));
|
||||
} else if !cols.contains(&headers[column_num]) {
|
||||
cols.push(headers[column_num].clone());
|
||||
vals.push(Value::nothing(name));
|
||||
record.cols.remove(index);
|
||||
record.vals.remove(index);
|
||||
record.push(headers[column_num].clone(), Value::nothing(name));
|
||||
} else if !record.cols.contains(&headers[column_num]) {
|
||||
record.push(headers[column_num].clone(), Value::nothing(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
column_num += 1;
|
||||
}
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: name,
|
||||
}
|
||||
Value::record(record, name)
|
||||
})
|
||||
.collect::<Vec<Value>>();
|
||||
if result_data.len() == 1 && args.as_record {
|
||||
|
@ -3,8 +3,8 @@ use itertools::Itertools;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, PipelineMetadata, ShellError, Signature,
|
||||
Span, Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, PipelineMetadata, Record,
|
||||
ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
use std::collections::hash_map::IntoIter;
|
||||
use std::collections::HashMap;
|
||||
@ -122,16 +122,14 @@ impl Command for Uniq {
|
||||
example: "[1 2 2] | uniq -c",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string(), "count".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string(), "count".to_string()],
|
||||
vals: vec![Value::test_int(2), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -193,32 +191,25 @@ fn clone_to_lowercase(value: &Value) -> Value {
|
||||
span: *span,
|
||||
},
|
||||
Value::List { vals: vec, span } => Value::List {
|
||||
vals: vec
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|v| clone_to_lowercase(&v))
|
||||
.collect(),
|
||||
span: *span,
|
||||
},
|
||||
Value::Record { cols, vals, span } => Value::Record {
|
||||
cols: cols.clone(),
|
||||
vals: vals
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|v| clone_to_lowercase(&v))
|
||||
.collect(),
|
||||
vals: vec.iter().map(clone_to_lowercase).collect(),
|
||||
span: *span,
|
||||
},
|
||||
Value::Record { val: record, span } => Value::record(
|
||||
Record {
|
||||
cols: record.cols.clone(),
|
||||
vals: record.vals.iter().map(clone_to_lowercase).collect(),
|
||||
},
|
||||
*span,
|
||||
),
|
||||
other => other.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_attributes(val: Value) -> Value {
|
||||
match val {
|
||||
Value::Record { cols, vals, span } => {
|
||||
let sorted = cols
|
||||
Value::Record { val, span } => {
|
||||
let sorted = val
|
||||
.into_iter()
|
||||
.zip(vals)
|
||||
.sorted_by(|a, b| a.0.cmp(&b.0))
|
||||
.collect_vec();
|
||||
|
||||
@ -228,11 +219,13 @@ fn sort_attributes(val: Value) -> Value {
|
||||
.map(|a| sort_attributes(a.1))
|
||||
.collect_vec();
|
||||
|
||||
Value::Record {
|
||||
cols: sorted_cols,
|
||||
vals: sorted_vals,
|
||||
Value::record(
|
||||
Record {
|
||||
cols: sorted_cols,
|
||||
vals: sorted_vals,
|
||||
},
|
||||
span,
|
||||
}
|
||||
)
|
||||
}
|
||||
Value::List { vals, span } => Value::List {
|
||||
vals: vals.into_iter().map(sort_attributes).collect_vec(),
|
||||
@ -250,10 +243,14 @@ fn generate_key(item: &ValueCounter) -> Result<String, ShellError> {
|
||||
fn generate_results_with_count(head: Span, uniq_values: Vec<ValueCounter>) -> Vec<Value> {
|
||||
uniq_values
|
||||
.into_iter()
|
||||
.map(|item| Value::Record {
|
||||
cols: vec!["value".to_string(), "count".to_string()],
|
||||
vals: vec![item.val, Value::int(item.count, head)],
|
||||
span: head,
|
||||
.map(|item| {
|
||||
Value::record(
|
||||
record! {
|
||||
"value" => item.val,
|
||||
"count" => Value::int(item.count, head),
|
||||
},
|
||||
head,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -94,18 +94,18 @@ impl Command for UniqBy {
|
||||
example: "[[fruit count]; [apple 9] [apple 2] [pear 3] [orange 7]] | uniq-by fruit",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(
|
||||
vec!["fruit", "count"],
|
||||
vec![Value::test_string("apple"), Value::test_int(9)],
|
||||
),
|
||||
Value::test_record(
|
||||
vec!["fruit", "count"],
|
||||
vec![Value::test_string("pear"), Value::test_int(3)],
|
||||
),
|
||||
Value::test_record(
|
||||
vec!["fruit", "count"],
|
||||
vec![Value::test_string("orange"), Value::test_int(7)],
|
||||
),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["fruit".to_string(), "count".to_string()],
|
||||
vals: vec![Value::test_string("apple"), Value::test_int(9)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["fruit".to_string(), "count".to_string()],
|
||||
vals: vec![Value::test_string("pear"), Value::test_int(3)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["fruit".to_string(), "count".to_string()],
|
||||
vals: vec![Value::test_string("orange"), Value::test_int(7)],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -115,8 +115,7 @@ impl Command for UniqBy {
|
||||
|
||||
fn validate(vec: Vec<Value>, columns: &Vec<String>, span: Span) -> Result<(), ShellError> {
|
||||
if let Some(Value::Record {
|
||||
cols,
|
||||
vals: _input_vals,
|
||||
val: record,
|
||||
span: val_span,
|
||||
}) = vec.first()
|
||||
{
|
||||
@ -131,7 +130,7 @@ fn validate(vec: Vec<Value>, columns: &Vec<String>, span: Span) -> Result<(), Sh
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(nonexistent) = nonexistent_column(columns.clone(), cols.to_vec()) {
|
||||
if let Some(nonexistent) = nonexistent_column(columns.clone(), record.cols.clone()) {
|
||||
return Err(ShellError::CantFindColumn {
|
||||
col_name: nonexistent,
|
||||
span,
|
||||
|
@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember};
|
||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -57,33 +57,43 @@ impl Command for Update {
|
||||
Example {
|
||||
description: "Update a column value",
|
||||
example: "{'name': 'nu', 'stars': 5} | update name 'Nushell'",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["name".into(), "stars".into()],
|
||||
vals: vec![Value::test_string("Nushell"), Value::test_int(5)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Use in closure form for more involved updating logic",
|
||||
example: "[[count fruit]; [1 'apple']] | enumerate | update item.count {|e| ($e.item.fruit | str length) + $e.index } | get item",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["count".into(), "fruit".into()],
|
||||
vals: vec![Value::test_int(5), Value::test_string("apple")],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Alter each value in the 'authors' column to use a single string instead of a list",
|
||||
example: "[[project, authors]; ['nu', ['Andrés', 'JT', 'Yehuda']]] | update authors {|row| $row.authors | str join ','}",
|
||||
result: Some(Value::List { vals: vec![Value::Record { cols: vec!["project".into(), "authors".into()], vals: vec![Value::test_string("nu"), Value::test_string("Andrés,JT,Yehuda")], span: Span::test_data()}], span: Span::test_data()}),
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["project".into(), "authors".into()],
|
||||
vals: vec![Value::test_string("nu"), Value::test_string("Andrés,JT,Yehuda")],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "You can also use a simple command to update 'authors' to a single string",
|
||||
example: "[[project, authors]; ['nu', ['Andrés', 'JT', 'Yehuda']]] | update authors {|| str join ','}",
|
||||
result: Some(Value::List { vals: vec![Value::Record { cols: vec!["project".into(), "authors".into()], vals: vec![Value::test_string("nu"), Value::test_string("Andrés,JT,Yehuda")], span: Span::test_data()}], span: Span::test_data()}),
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["project".into(), "authors".into()],
|
||||
vals: vec![Value::test_string("nu"), Value::test_string("Andrés,JT,Yehuda")],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember};
|
||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -60,26 +60,45 @@ impl Command for Upsert {
|
||||
vec![Example {
|
||||
description: "Update a record's value",
|
||||
example: "{'name': 'nu', 'stars': 5} | upsert name 'Nushell'",
|
||||
result: Some(Value::Record { cols: vec!["name".into(), "stars".into()], vals: vec![Value::test_string("Nushell"), Value::test_int(5)], span: Span::test_data()}),
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["name".into(), "stars".into()],
|
||||
vals: vec![Value::test_string("Nushell"), Value::test_int(5)],
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Update each row of a table",
|
||||
example: "[[name lang]; [Nushell ''] [Reedline '']] | upsert lang 'Rust'",
|
||||
result: Some(Value::List { vals: vec![
|
||||
Value::Record { cols: vec!["name".into(), "lang".into()], vals: vec![Value::test_string("Nushell"), Value::test_string("Rust")], span: Span::test_data()},
|
||||
Value::Record { cols: vec!["name".into(), "lang".into()], vals: vec![Value::test_string("Reedline"), Value::test_string("Rust")], span: Span::test_data()}
|
||||
], span: Span::test_data()}),
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec!["name".into(), "lang".into()],
|
||||
vals: vec![Value::test_string("Nushell"), Value::test_string("Rust")],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["name".into(), "lang".into()],
|
||||
vals: vec![Value::test_string("Reedline"), Value::test_string("Rust")],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Insert a new entry into a single record",
|
||||
example: "{'name': 'nu', 'stars': 5} | upsert language 'Rust'",
|
||||
result: Some(Value::Record { cols: vec!["name".into(), "stars".into(), "language".into()], vals: vec![Value::test_string("nu"), Value::test_int(5), Value::test_string("Rust")], span: Span::test_data()}),
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["name".into(), "stars".into(), "language".into()],
|
||||
vals: vec![Value::test_string("nu"), Value::test_int(5), Value::test_string("Rust")],
|
||||
})),
|
||||
}, Example {
|
||||
description: "Use in closure form for more involved updating logic",
|
||||
example: "[[count fruit]; [1 'apple']] | enumerate | upsert item.count {|e| ($e.item.fruit | str length) + $e.index } | get item",
|
||||
result: Some(Value::List { vals: vec![
|
||||
Value::Record { cols: vec!["count".into(), "fruit".into()], vals: vec![Value::test_int(5), Value::test_string("apple")], span: Span::test_data()}],
|
||||
span: Span::test_data()}),
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["count".into(), "fruit".into()],
|
||||
vals: vec![Value::test_int(5), Value::test_string("apple")],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Upsert an int into a list, updating an existing value based on the index",
|
||||
|
@ -110,8 +110,8 @@ pub fn get_values<'a>(
|
||||
|
||||
for item in input {
|
||||
match item {
|
||||
Value::Record { cols, vals, .. } => {
|
||||
for (k, v) in cols.iter().zip(vals.iter()) {
|
||||
Value::Record { val, .. } => {
|
||||
for (k, v) in val {
|
||||
if let Some(vec) = output.get_mut(k) {
|
||||
vec.push(v.clone());
|
||||
} else {
|
||||
@ -172,8 +172,8 @@ fn values(
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
PipelineData::Value(Value::Record { vals, .. }, ..) => {
|
||||
Ok(vals.into_pipeline_data(ctrlc).set_metadata(metadata))
|
||||
PipelineData::Value(Value::Record { val, .. }, ..) => {
|
||||
Ok(val.vals.into_pipeline_data(ctrlc).set_metadata(metadata))
|
||||
}
|
||||
// Propagate errors
|
||||
PipelineData::Value(Value::Error { error }, ..) => Err(*error),
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Record,
|
||||
ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -114,11 +114,10 @@ not supported."#
|
||||
description: "Filter rows of a table according to a condition",
|
||||
example: "[{a: 1} {a: 2}] | where a > 1",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -47,27 +47,18 @@ impl Command for Wrap {
|
||||
| PipelineData::Value(Value::List { .. }, ..)
|
||||
| PipelineData::ListStream { .. } => Ok(input
|
||||
.into_iter()
|
||||
.map(move |x| Value::Record {
|
||||
cols: vec![name.clone()],
|
||||
vals: vec![x],
|
||||
span,
|
||||
})
|
||||
.map(move |x| Value::record(record! { name.clone() => x }, span))
|
||||
.into_pipeline_data(engine_state.ctrlc.clone())
|
||||
.set_metadata(metadata)),
|
||||
PipelineData::ExternalStream { .. } => Ok(Value::Record {
|
||||
cols: vec![name],
|
||||
vals: vec![input.into_value(call.head)],
|
||||
PipelineData::ExternalStream { .. } => Ok(Value::record(
|
||||
record! { name => input.into_value(call.head) },
|
||||
span,
|
||||
}
|
||||
.into_pipeline_data()
|
||||
.set_metadata(metadata)),
|
||||
PipelineData::Value(input, ..) => Ok(Value::Record {
|
||||
cols: vec![name],
|
||||
vals: vec![input],
|
||||
span,
|
||||
}
|
||||
)
|
||||
.into_pipeline_data()
|
||||
.set_metadata(metadata)),
|
||||
PipelineData::Value(input, ..) => Ok(Value::record(record! { name => input }, span)
|
||||
.into_pipeline_data()
|
||||
.set_metadata(metadata)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,21 +69,18 @@ impl Command for Wrap {
|
||||
example: "[1 2 3] | wrap num",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["num".into()],
|
||||
vals: vec![Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["num".into()],
|
||||
vals: vec![Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["num".into()],
|
||||
vals: vec![Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -102,21 +90,18 @@ impl Command for Wrap {
|
||||
example: "1..3 | wrap num",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["num".into()],
|
||||
vals: vec![Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["num".into()],
|
||||
vals: vec![Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["num".into()],
|
||||
vals: vec![Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
|
@ -4,7 +4,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -82,14 +82,13 @@ impl Command for FromCsv {
|
||||
description: "Convert comma-separated data to a table",
|
||||
example: "\"ColA,ColB\n1,2\" | from csv",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string()],
|
||||
vals: vec![
|
||||
Value::test_int(1),
|
||||
Value::test_int(2),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
})
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
use csv::{ReaderBuilder, Trim};
|
||||
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Value};
|
||||
use nu_protocol::{IntoPipelineData, PipelineData, Record, ShellError, Span, Value};
|
||||
|
||||
fn from_delimited_string_to_value(
|
||||
DelimitedReaderConfig {
|
||||
@ -56,11 +56,13 @@ fn from_delimited_string_to_value(
|
||||
});
|
||||
}
|
||||
}
|
||||
rows.push(Value::Record {
|
||||
cols: headers.clone(),
|
||||
vals: output_row,
|
||||
rows.push(Value::record(
|
||||
Record {
|
||||
cols: headers.clone(),
|
||||
vals: output_row,
|
||||
},
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Value::List { vals: rows, span })
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Record,
|
||||
ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -29,16 +29,15 @@ impl Command for FromJson {
|
||||
Example {
|
||||
example: r#"'{ "a": 1 }' | from json"#,
|
||||
description: "Converts json formatted string to table",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
example: r#"'{ "a": 1, "b": [1, 2] }' | from json"#,
|
||||
description: "Converts json formatted string to table",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![
|
||||
Value::test_int(1),
|
||||
@ -47,8 +46,7 @@ impl Command for FromJson {
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -107,17 +105,12 @@ fn convert_nujson_to_value(value: &nu_json::Value, span: Span) -> Value {
|
||||
nu_json::Value::F64(f) => Value::Float { val: *f, span },
|
||||
nu_json::Value::I64(i) => Value::Int { val: *i, span },
|
||||
nu_json::Value::Null => Value::Nothing { span },
|
||||
nu_json::Value::Object(k) => {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
for item in k {
|
||||
cols.push(item.0.clone());
|
||||
vals.push(convert_nujson_to_value(item.1, span));
|
||||
}
|
||||
|
||||
Value::Record { cols, vals, span }
|
||||
}
|
||||
nu_json::Value::Object(k) => Value::record(
|
||||
k.iter()
|
||||
.map(|(k, v)| (k.clone(), convert_nujson_to_value(v, span)))
|
||||
.collect(),
|
||||
span,
|
||||
),
|
||||
nu_json::Value::U64(u) => {
|
||||
if *u > i64::MAX as u64 {
|
||||
Value::Error {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nu_protocol::ast::{Call, Expr, Expression, PipelineElement};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, Range, ShellError, Signature, Span, Type,
|
||||
Unit, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Range, Record, ShellError, Signature, Span,
|
||||
Type, Unit, Value,
|
||||
};
|
||||
#[derive(Clone)]
|
||||
pub struct FromNuon;
|
||||
@ -27,16 +27,15 @@ impl Command for FromNuon {
|
||||
Example {
|
||||
example: "'{ a:1 }' | from nuon",
|
||||
description: "Converts nuon formatted string to table",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
example: "'{ a:1, b: [1, 2] }' | from nuon",
|
||||
description: "Converts nuon formatted string to table",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![
|
||||
Value::test_int(1),
|
||||
@ -45,8 +44,7 @@ impl Command for FromNuon {
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -323,8 +321,7 @@ fn convert_to_value(
|
||||
})
|
||||
}
|
||||
Expr::Record(key_vals) => {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
|
||||
for (key, val) in key_vals {
|
||||
let key_str = match key.expr {
|
||||
@ -341,11 +338,10 @@ fn convert_to_value(
|
||||
|
||||
let value = convert_to_value(val, span, original_text)?;
|
||||
|
||||
cols.push(key_str);
|
||||
vals.push(value);
|
||||
record.push(key_str, value);
|
||||
}
|
||||
|
||||
Ok(Value::Record { cols, vals, span })
|
||||
Ok(Value::record(record, span))
|
||||
}
|
||||
Expr::RowCondition(..) => Err(ShellError::OutsideSpannedLabeledError(
|
||||
original_text.to_string(),
|
||||
@ -409,11 +405,13 @@ fn convert_to_value(
|
||||
));
|
||||
}
|
||||
|
||||
output.push(Value::Record {
|
||||
cols: cols.clone(),
|
||||
vals,
|
||||
output.push(Value::record(
|
||||
Record {
|
||||
cols: cols.clone(),
|
||||
vals,
|
||||
},
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Value::List { vals: output, span })
|
||||
|
@ -133,41 +133,29 @@ fn from_ods(
|
||||
sheet_names.retain(|e| sel_sheets.contains(e));
|
||||
}
|
||||
|
||||
for sheet_name in &sheet_names {
|
||||
for sheet_name in sheet_names {
|
||||
let mut sheet_output = vec![];
|
||||
|
||||
if let Some(Ok(current_sheet)) = ods.worksheet_range(sheet_name) {
|
||||
if let Some(Ok(current_sheet)) = ods.worksheet_range(&sheet_name) {
|
||||
for row in current_sheet.rows() {
|
||||
let mut row_output = IndexMap::new();
|
||||
for (i, cell) in row.iter().enumerate() {
|
||||
let value = match cell {
|
||||
DataType::Empty => Value::nothing(head),
|
||||
DataType::String(s) => Value::string(s, head),
|
||||
DataType::Float(f) => Value::float(*f, head),
|
||||
DataType::Int(i) => Value::int(*i, head),
|
||||
DataType::Bool(b) => Value::bool(*b, head),
|
||||
_ => Value::nothing(head),
|
||||
};
|
||||
let record = row
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, cell)| {
|
||||
let value = match cell {
|
||||
DataType::Empty => Value::nothing(head),
|
||||
DataType::String(s) => Value::string(s, head),
|
||||
DataType::Float(f) => Value::float(*f, head),
|
||||
DataType::Int(i) => Value::int(*i, head),
|
||||
DataType::Bool(b) => Value::bool(*b, head),
|
||||
_ => Value::nothing(head),
|
||||
};
|
||||
|
||||
row_output.insert(format!("column{i}"), value);
|
||||
}
|
||||
(format!("column{i}"), value)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (cols, vals) =
|
||||
row_output
|
||||
.into_iter()
|
||||
.fold((vec![], vec![]), |mut acc, (k, v)| {
|
||||
acc.0.push(k);
|
||||
acc.1.push(v);
|
||||
acc
|
||||
});
|
||||
|
||||
let record = Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
};
|
||||
|
||||
sheet_output.push(record);
|
||||
sheet_output.push(Value::record(record, head));
|
||||
}
|
||||
|
||||
dict.insert(
|
||||
@ -187,19 +175,10 @@ fn from_ods(
|
||||
}
|
||||
}
|
||||
|
||||
let (cols, vals) = dict.into_iter().fold((vec![], vec![]), |mut acc, (k, v)| {
|
||||
acc.0.push(k.clone());
|
||||
acc.1.push(v);
|
||||
acc
|
||||
});
|
||||
|
||||
let record = Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
};
|
||||
|
||||
Ok(PipelineData::Value(record, None))
|
||||
Ok(PipelineData::Value(
|
||||
Value::record(dict.into_iter().collect(), head),
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -3,8 +3,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -44,13 +44,32 @@ impl Command for FromSsv {
|
||||
example: r#"'FOO BAR
|
||||
1 2' | from ssv"#,
|
||||
description: "Converts ssv formatted string to table",
|
||||
result: Some(Value::List { vals: vec![Value::Record { cols: vec!["FOO".to_string(), "BAR".to_string()], vals: vec![Value::test_string("1"), Value::test_string("2")], span: Span::test_data() }], span: Span::test_data() }),
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["FOO".to_string(), "BAR".to_string()],
|
||||
vals: vec![Value::test_string("1"), Value::test_string("2")],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
}, Example {
|
||||
example: r#"'FOO BAR
|
||||
1 2' | from ssv -n"#,
|
||||
description: "Converts ssv formatted string to table but not treating the first row as column names",
|
||||
result: Some(
|
||||
Value::List { vals: vec![Value::Record { cols: vec!["column1".to_string(), "column2".to_string()], vals: vec![Value::test_string("FOO"), Value::test_string("BAR")], span: Span::test_data() }, Value::Record { cols: vec!["column1".to_string(), "column2".to_string()], vals: vec![Value::test_string("1"), Value::test_string("2")], span: Span::test_data() }], span: Span::test_data() }),
|
||||
Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column1".to_string(), "column2".to_string()],
|
||||
vals: vec![Value::test_string("FOO"), Value::test_string("BAR")],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column1".to_string(), "column2".to_string()],
|
||||
vals: vec![Value::test_string("1"), Value::test_string("2")],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}
|
||||
),
|
||||
}]
|
||||
}
|
||||
|
||||
@ -251,19 +270,13 @@ fn from_ssv_string_to_value(
|
||||
span: Span,
|
||||
) -> Value {
|
||||
let rows = string_to_table(s, noheaders, aligned_columns, split_at)
|
||||
.iter()
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
let mut dict = IndexMap::new();
|
||||
for (col, entry) in row {
|
||||
dict.insert(
|
||||
col.to_string(),
|
||||
Value::String {
|
||||
val: entry.to_string(),
|
||||
span,
|
||||
},
|
||||
);
|
||||
dict.insert(col, Value::string(entry, span));
|
||||
}
|
||||
Value::from(Spanned { item: dict, span })
|
||||
Value::record(dict.into_iter().collect(), span)
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span, Type,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -27,17 +28,16 @@ impl Command for FromToml {
|
||||
Example {
|
||||
example: "'a = 1' | from toml",
|
||||
description: "Converts toml formatted string to record",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
example: "'a = 1
|
||||
b = [1, 2]' | from toml",
|
||||
description: "Converts toml formatted string to record",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![
|
||||
Value::test_int(1),
|
||||
@ -46,8 +46,7 @@ b = [1, 2]' | from toml",
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -79,17 +78,12 @@ fn convert_toml_to_value(value: &toml::Value, span: Span) -> Value {
|
||||
toml::Value::Boolean(b) => Value::Bool { val: *b, span },
|
||||
toml::Value::Float(f) => Value::Float { val: *f, span },
|
||||
toml::Value::Integer(i) => Value::Int { val: *i, span },
|
||||
toml::Value::Table(k) => {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
for item in k {
|
||||
cols.push(item.0.clone());
|
||||
vals.push(convert_toml_to_value(item.1, span));
|
||||
}
|
||||
|
||||
Value::Record { cols, vals, span }
|
||||
}
|
||||
toml::Value::Table(k) => Value::record(
|
||||
k.iter()
|
||||
.map(|(k, v)| (k.clone(), convert_toml_to_value(v, span)))
|
||||
.collect(),
|
||||
span,
|
||||
),
|
||||
toml::Value::String(s) => Value::String {
|
||||
val: s.clone(),
|
||||
span,
|
||||
|
@ -4,7 +4,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -76,14 +76,13 @@ impl Command for FromTsv {
|
||||
description: "Convert tab-separated data to a table",
|
||||
example: "\"ColA\tColB\n1\t2\" | from tsv",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string()],
|
||||
vals: vec![
|
||||
Value::test_int(1),
|
||||
Value::test_int(2),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
})
|
||||
},
|
||||
|
@ -132,41 +132,29 @@ fn from_xlsx(
|
||||
sheet_names.retain(|e| sel_sheets.contains(e));
|
||||
}
|
||||
|
||||
for sheet_name in &sheet_names {
|
||||
for sheet_name in sheet_names {
|
||||
let mut sheet_output = vec![];
|
||||
|
||||
if let Some(Ok(current_sheet)) = xlsx.worksheet_range(sheet_name) {
|
||||
if let Some(Ok(current_sheet)) = xlsx.worksheet_range(&sheet_name) {
|
||||
for row in current_sheet.rows() {
|
||||
let mut row_output = IndexMap::new();
|
||||
for (i, cell) in row.iter().enumerate() {
|
||||
let value = match cell {
|
||||
DataType::Empty => Value::nothing(head),
|
||||
DataType::String(s) => Value::string(s, head),
|
||||
DataType::Float(f) => Value::float(*f, head),
|
||||
DataType::Int(i) => Value::int(*i, head),
|
||||
DataType::Bool(b) => Value::bool(*b, head),
|
||||
_ => Value::nothing(head),
|
||||
};
|
||||
let record = row
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, cell)| {
|
||||
let value = match cell {
|
||||
DataType::Empty => Value::nothing(head),
|
||||
DataType::String(s) => Value::string(s, head),
|
||||
DataType::Float(f) => Value::float(*f, head),
|
||||
DataType::Int(i) => Value::int(*i, head),
|
||||
DataType::Bool(b) => Value::bool(*b, head),
|
||||
_ => Value::nothing(head),
|
||||
};
|
||||
|
||||
row_output.insert(format!("column{i}"), value);
|
||||
}
|
||||
(format!("column{i}"), value)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (cols, vals) =
|
||||
row_output
|
||||
.into_iter()
|
||||
.fold((vec![], vec![]), |mut acc, (k, v)| {
|
||||
acc.0.push(k);
|
||||
acc.1.push(v);
|
||||
acc
|
||||
});
|
||||
|
||||
let record = Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
};
|
||||
|
||||
sheet_output.push(record);
|
||||
sheet_output.push(Value::record(record, head));
|
||||
}
|
||||
|
||||
dict.insert(
|
||||
@ -186,19 +174,10 @@ fn from_xlsx(
|
||||
}
|
||||
}
|
||||
|
||||
let (cols, vals) = dict.into_iter().fold((vec![], vec![]), |mut acc, (k, v)| {
|
||||
acc.0.push(k.clone());
|
||||
acc.1.push(v);
|
||||
acc
|
||||
});
|
||||
|
||||
let record = Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
};
|
||||
|
||||
Ok(PipelineData::Value(record, None))
|
||||
Ok(PipelineData::Value(
|
||||
Value::record(dict.into_iter().collect(), head),
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -3,7 +3,7 @@ use indexmap::map::IndexMap;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, Type,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span, Type,
|
||||
Value,
|
||||
};
|
||||
use roxmltree::NodeType;
|
||||
@ -69,38 +69,46 @@ string. This way content of every tag is always a table and is easier to parse"#
|
||||
<remember>Event</remember>
|
||||
</note>' | from xml"#,
|
||||
description: "Converts xml formatted string to record",
|
||||
result: Some(Value::test_record(
|
||||
vec![COLUMN_TAG_NAME, COLUMN_ATTRS_NAME, COLUMN_CONTENT_NAME],
|
||||
vec![
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
COLUMN_TAG_NAME.to_string(),
|
||||
COLUMN_ATTRS_NAME.to_string(),
|
||||
COLUMN_CONTENT_NAME.to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_string("note"),
|
||||
Value::test_record(Vec::<&str>::new(), vec![]),
|
||||
Value::test_record(Record::new()),
|
||||
Value::list(
|
||||
vec![Value::test_record(
|
||||
vec![COLUMN_TAG_NAME, COLUMN_ATTRS_NAME, COLUMN_CONTENT_NAME],
|
||||
vec![
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
COLUMN_TAG_NAME.to_string(),
|
||||
COLUMN_ATTRS_NAME.to_string(),
|
||||
COLUMN_CONTENT_NAME.to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_string("remember"),
|
||||
Value::test_record(Vec::<&str>::new(), vec![]),
|
||||
Value::test_record(Record::new()),
|
||||
Value::list(
|
||||
vec![Value::test_record(
|
||||
vec![
|
||||
COLUMN_TAG_NAME,
|
||||
COLUMN_ATTRS_NAME,
|
||||
COLUMN_CONTENT_NAME,
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
COLUMN_TAG_NAME.to_string(),
|
||||
COLUMN_ATTRS_NAME.to_string(),
|
||||
COLUMN_CONTENT_NAME.to_string(),
|
||||
],
|
||||
vec![
|
||||
vals: vec![
|
||||
Value::test_nothing(),
|
||||
Value::test_nothing(),
|
||||
Value::test_string("Event"),
|
||||
],
|
||||
)],
|
||||
})],
|
||||
Span::test_data(),
|
||||
),
|
||||
],
|
||||
)],
|
||||
})],
|
||||
Span::test_data(),
|
||||
),
|
||||
],
|
||||
)),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -116,20 +124,7 @@ fn from_attributes_to_value(attributes: &[roxmltree::Attribute], info: &ParsingI
|
||||
for a in attributes {
|
||||
collected.insert(String::from(a.name()), Value::string(a.value(), info.span));
|
||||
}
|
||||
|
||||
let (cols, vals) = collected
|
||||
.into_iter()
|
||||
.fold((vec![], vec![]), |mut acc, (k, v)| {
|
||||
acc.0.push(k);
|
||||
acc.1.push(v);
|
||||
acc
|
||||
});
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: info.span,
|
||||
}
|
||||
Value::record(collected.into_iter().collect(), info.span)
|
||||
}
|
||||
|
||||
fn element_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> Value {
|
||||
@ -151,7 +146,7 @@ fn element_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> Value {
|
||||
node.insert(String::from(COLUMN_ATTRS_NAME), attributes);
|
||||
node.insert(String::from(COLUMN_CONTENT_NAME), content);
|
||||
|
||||
Value::from(Spanned { item: node, span })
|
||||
Value::record(node.into_iter().collect(), span)
|
||||
}
|
||||
|
||||
fn text_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> Option<Value> {
|
||||
@ -168,9 +163,7 @@ fn text_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> Option<Value> {
|
||||
node.insert(String::from(COLUMN_ATTRS_NAME), Value::nothing(span));
|
||||
node.insert(String::from(COLUMN_CONTENT_NAME), content);
|
||||
|
||||
let result = Value::from(Spanned { item: node, span });
|
||||
|
||||
Some(result)
|
||||
Some(Value::record(node.into_iter().collect(), span))
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,9 +181,7 @@ fn comment_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> Option<Value> {
|
||||
node.insert(String::from(COLUMN_ATTRS_NAME), Value::nothing(span));
|
||||
node.insert(String::from(COLUMN_CONTENT_NAME), content);
|
||||
|
||||
let result = Value::from(Spanned { item: node, span });
|
||||
|
||||
Some(result)
|
||||
Some(Value::record(node.into_iter().collect(), span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -213,9 +204,7 @@ fn processing_instruction_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> O
|
||||
node.insert(String::from(COLUMN_ATTRS_NAME), Value::nothing(span));
|
||||
node.insert(String::from(COLUMN_CONTENT_NAME), content);
|
||||
|
||||
let result = Value::from(Spanned { item: node, span });
|
||||
|
||||
Some(result)
|
||||
Some(Value::record(node.into_iter().collect(), span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -332,20 +321,18 @@ mod tests {
|
||||
|
||||
use indexmap::indexmap;
|
||||
use indexmap::IndexMap;
|
||||
use nu_protocol::{Spanned, Value};
|
||||
|
||||
fn string(input: impl Into<String>) -> Value {
|
||||
Value::test_string(input)
|
||||
}
|
||||
|
||||
fn attributes(entries: IndexMap<&str, &str>) -> Value {
|
||||
Value::from(Spanned {
|
||||
item: entries
|
||||
Value::test_record(
|
||||
entries
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.into(), string(v)))
|
||||
.collect::<IndexMap<String, Value>>(),
|
||||
span: Span::test_data(),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn table(list: &[Value]) -> Value {
|
||||
@ -360,24 +347,28 @@ mod tests {
|
||||
attrs: IndexMap<&str, &str>,
|
||||
content: &[Value],
|
||||
) -> Value {
|
||||
Value::from(Spanned {
|
||||
item: indexmap! {
|
||||
COLUMN_TAG_NAME.into() => string(tag),
|
||||
COLUMN_ATTRS_NAME.into() => attributes(attrs),
|
||||
COLUMN_CONTENT_NAME.into() => table(content),
|
||||
},
|
||||
span: Span::test_data(),
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
COLUMN_TAG_NAME.into(),
|
||||
COLUMN_ATTRS_NAME.into(),
|
||||
COLUMN_CONTENT_NAME.into(),
|
||||
],
|
||||
vals: vec![string(tag), attributes(attrs), table(content)],
|
||||
})
|
||||
}
|
||||
|
||||
fn content_string(value: impl Into<String>) -> Value {
|
||||
Value::from(Spanned {
|
||||
item: indexmap! {
|
||||
COLUMN_TAG_NAME.into() => Value::nothing(Span::test_data()),
|
||||
COLUMN_ATTRS_NAME.into() => Value::nothing(Span::test_data()),
|
||||
COLUMN_CONTENT_NAME.into() => string(value),
|
||||
},
|
||||
span: Span::test_data(),
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
COLUMN_TAG_NAME.into(),
|
||||
COLUMN_ATTRS_NAME.into(),
|
||||
COLUMN_CONTENT_NAME.into(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::nothing(Span::test_data()),
|
||||
Value::nothing(Span::test_data()),
|
||||
string(value),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use itertools::Itertools;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, Type,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span, Type,
|
||||
Value,
|
||||
};
|
||||
use serde::de::Deserialize;
|
||||
@ -112,11 +112,8 @@ fn convert_yaml_value_to_nu_value(
|
||||
}
|
||||
}
|
||||
serde_yaml::Value::Mapping(t) => {
|
||||
let mut collected = Spanned {
|
||||
// Using an IndexMap ensures consistent ordering
|
||||
item: IndexMap::new(),
|
||||
span,
|
||||
};
|
||||
// Using an IndexMap ensures consistent ordering
|
||||
let mut collected = IndexMap::new();
|
||||
|
||||
for (k, v) in t {
|
||||
// A ShellError that we re-use multiple times in the Mapping scenario
|
||||
@ -128,19 +125,19 @@ fn convert_yaml_value_to_nu_value(
|
||||
);
|
||||
match (k, v) {
|
||||
(serde_yaml::Value::Number(k), _) => {
|
||||
collected.item.insert(
|
||||
collected.insert(
|
||||
k.to_string(),
|
||||
convert_yaml_value_to_nu_value(v, span, val_span)?,
|
||||
);
|
||||
}
|
||||
(serde_yaml::Value::Bool(k), _) => {
|
||||
collected.item.insert(
|
||||
collected.insert(
|
||||
k.to_string(),
|
||||
convert_yaml_value_to_nu_value(v, span, val_span)?,
|
||||
);
|
||||
}
|
||||
(serde_yaml::Value::String(k), _) => {
|
||||
collected.item.insert(
|
||||
collected.insert(
|
||||
k.clone(),
|
||||
convert_yaml_value_to_nu_value(v, span, val_span)?,
|
||||
);
|
||||
@ -173,7 +170,7 @@ fn convert_yaml_value_to_nu_value(
|
||||
}
|
||||
}
|
||||
|
||||
Value::from(collected)
|
||||
Value::record(collected.into_iter().collect(), span)
|
||||
}
|
||||
serde_yaml::Value::Tagged(t) => {
|
||||
let tag = &t.tag;
|
||||
@ -238,30 +235,27 @@ pub fn get_examples() -> Vec<Example<'static>> {
|
||||
Example {
|
||||
example: "'a: 1' | from yaml",
|
||||
description: "Converts yaml formatted string to table",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
example: "'[ a: 1, b: [1, 2] ]' | from yaml",
|
||||
description: "Converts yaml formatted string to table",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["b".to_string()],
|
||||
vals: vec![Value::List {
|
||||
vals: vec![Value::test_int(1), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -294,20 +288,18 @@ mod test {
|
||||
TestCase {
|
||||
description: "Double Curly Braces With Quotes",
|
||||
input: r#"value: "{{ something }}""#,
|
||||
expected: Ok(Value::Record {
|
||||
expected: Ok(Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::test_string("{{ something }}")],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
TestCase {
|
||||
description: "Double Curly Braces Without Quotes",
|
||||
input: r#"value: {{ something }}"#,
|
||||
expected: Ok(Value::Record {
|
||||
expected: Ok(Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::test_string("{{ something }}")],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
];
|
||||
let config = Config::default();
|
||||
@ -359,16 +351,14 @@ mod test {
|
||||
|
||||
let expected: Result<Value, ShellError> = Ok(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_string("b"), Value::test_string("c")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_string("g"), Value::test_string("h")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
});
|
||||
@ -388,15 +378,15 @@ mod test {
|
||||
let actual_record = actual_vals[jj].as_record().unwrap();
|
||||
let expected_record = expected_vals[jj].as_record().unwrap();
|
||||
|
||||
let actual_columns = actual_record.0;
|
||||
let expected_columns = expected_record.0;
|
||||
let actual_columns = &actual_record.cols;
|
||||
let expected_columns = &expected_record.cols;
|
||||
assert_eq!(
|
||||
expected_columns, actual_columns,
|
||||
"record {jj}, iteration {ii}"
|
||||
);
|
||||
|
||||
let actual_vals = actual_record.1;
|
||||
let expected_vals = expected_record.1;
|
||||
let actual_vals = &actual_record.vals;
|
||||
let expected_vals = &expected_record.vals;
|
||||
assert_eq!(expected_vals, actual_vals, "record {jj}, iteration {ii}")
|
||||
}
|
||||
}
|
||||
@ -412,43 +402,38 @@ mod test {
|
||||
let test_cases: Vec<TestCase> = vec![
|
||||
TestCase {
|
||||
input: "Key: !Value ${TEST}-Test-role",
|
||||
expected: Ok(Value::Record {
|
||||
expected: Ok(Value::test_record(Record {
|
||||
cols: vec!["Key".to_string()],
|
||||
vals: vec![Value::test_string("!Value ${TEST}-Test-role")],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
TestCase {
|
||||
input: "Key: !Value test-${TEST}",
|
||||
expected: Ok(Value::Record {
|
||||
expected: Ok(Value::test_record(Record {
|
||||
cols: vec!["Key".to_string()],
|
||||
vals: vec![Value::test_string("!Value test-${TEST}")],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
TestCase {
|
||||
input: "Key: !Value",
|
||||
expected: Ok(Value::Record {
|
||||
expected: Ok(Value::test_record(Record {
|
||||
cols: vec!["Key".to_string()],
|
||||
vals: vec![Value::test_string("!Value")],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
TestCase {
|
||||
input: "Key: !True",
|
||||
expected: Ok(Value::Record {
|
||||
expected: Ok(Value::test_record(Record {
|
||||
cols: vec!["Key".to_string()],
|
||||
vals: vec![Value::test_string("!True")],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
TestCase {
|
||||
input: "Key: !123",
|
||||
expected: Ok(Value::Record {
|
||||
expected: Ok(Value::test_record(Record {
|
||||
cols: vec!["Key".to_string()],
|
||||
vals: vec![Value::test_string("!123")],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use csv::{Writer, WriterBuilder};
|
||||
use nu_cmd_base::formats::to::delimited::merge_descriptors;
|
||||
use nu_protocol::{Config, IntoPipelineData, PipelineData, ShellError, Span, Value};
|
||||
use nu_protocol::{Config, IntoPipelineData, PipelineData, Record, ShellError, Span, Value};
|
||||
use std::collections::VecDeque;
|
||||
use std::error::Error;
|
||||
|
||||
@ -11,9 +11,7 @@ fn from_value_to_delimited_string(
|
||||
head: Span,
|
||||
) -> Result<String, ShellError> {
|
||||
match value {
|
||||
Value::Record { cols, vals, span } => {
|
||||
record_to_delimited(cols, vals, *span, separator, config, head)
|
||||
}
|
||||
Value::Record { val, span } => record_to_delimited(val, *span, separator, config, head),
|
||||
Value::List { vals, span } => table_to_delimited(vals, *span, separator, config, head),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { error } => Err(*error.clone()),
|
||||
@ -22,8 +20,7 @@ fn from_value_to_delimited_string(
|
||||
}
|
||||
|
||||
fn record_to_delimited(
|
||||
cols: &[String],
|
||||
vals: &[Value],
|
||||
record: &Record,
|
||||
span: Span,
|
||||
separator: char,
|
||||
config: &Config,
|
||||
@ -35,7 +32,7 @@ fn record_to_delimited(
|
||||
let mut fields: VecDeque<String> = VecDeque::new();
|
||||
let mut values: VecDeque<String> = VecDeque::new();
|
||||
|
||||
for (k, v) in cols.iter().zip(vals.iter()) {
|
||||
for (k, v) in record {
|
||||
fields.push_back(k.clone());
|
||||
|
||||
values.push_back(to_string_tagged_value(v, config, head, span)?);
|
||||
|
@ -134,9 +134,9 @@ pub fn value_to_json_value(v: &Value) -> Result<nu_json::Value, ShellError> {
|
||||
Value::Binary { val, .. } => {
|
||||
nu_json::Value::Array(val.iter().map(|x| nu_json::Value::U64(*x as u64)).collect())
|
||||
}
|
||||
Value::Record { cols, vals, .. } => {
|
||||
Value::Record { val, .. } => {
|
||||
let mut m = nu_json::Map::new();
|
||||
for (k, v) in cols.iter().zip(vals) {
|
||||
for (k, v) in val {
|
||||
m.insert(k.clone(), value_to_json_value(v)?);
|
||||
}
|
||||
nu_json::Value::Object(m)
|
||||
|
@ -105,10 +105,7 @@ fn to_md(
|
||||
}
|
||||
|
||||
fn fragment(input: Value, pretty: bool, config: &Config) -> String {
|
||||
let headers = match input {
|
||||
Value::Record { ref cols, .. } => cols.to_owned(),
|
||||
_ => vec![],
|
||||
};
|
||||
let headers = input.columns();
|
||||
let mut out = String::new();
|
||||
|
||||
if headers.len() == 1 {
|
||||
@ -207,9 +204,12 @@ pub fn group_by(values: PipelineData, head: Span, config: &Config) -> (PipelineD
|
||||
let mut lists = IndexMap::new();
|
||||
let mut single_list = false;
|
||||
for val in values {
|
||||
if let Value::Record { ref cols, .. } = val {
|
||||
if let Value::Record {
|
||||
val: ref record, ..
|
||||
} = val
|
||||
{
|
||||
lists
|
||||
.entry(cols.concat())
|
||||
.entry(record.cols.concat())
|
||||
.and_modify(|v: &mut Vec<Value>| v.push(val.clone()))
|
||||
.or_insert_with(|| vec![val.clone()]);
|
||||
} else {
|
||||
@ -329,7 +329,7 @@ fn get_padded_string(text: String, desired_length: usize, padding_character: cha
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nu_protocol::{Config, IntoPipelineData, Span, Value};
|
||||
use nu_protocol::{Config, IntoPipelineData, Record, Span, Value};
|
||||
|
||||
fn one(string: &str) -> String {
|
||||
string
|
||||
@ -351,44 +351,40 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn render_h1() {
|
||||
let value = Value::Record {
|
||||
let value = Value::test_record(Record {
|
||||
cols: vec!["H1".to_string()],
|
||||
vals: vec![Value::test_string("Ecuador")],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
});
|
||||
|
||||
assert_eq!(fragment(value, false, &Config::default()), "# Ecuador\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_h2() {
|
||||
let value = Value::Record {
|
||||
let value = Value::test_record(Record {
|
||||
cols: vec!["H2".to_string()],
|
||||
vals: vec![Value::test_string("Ecuador")],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
});
|
||||
|
||||
assert_eq!(fragment(value, false, &Config::default()), "## Ecuador\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_h3() {
|
||||
let value = Value::Record {
|
||||
let value = Value::test_record(Record {
|
||||
cols: vec!["H3".to_string()],
|
||||
vals: vec![Value::test_string("Ecuador")],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
});
|
||||
|
||||
assert_eq!(fragment(value, false, &Config::default()), "### Ecuador\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_blockquote() {
|
||||
let value = Value::Record {
|
||||
let value = Value::test_record(Record {
|
||||
cols: vec!["BLOCKQUOTE".to_string()],
|
||||
vals: vec![Value::test_string("Ecuador")],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
});
|
||||
|
||||
assert_eq!(fragment(value, false, &Config::default()), "> Ecuador\n");
|
||||
}
|
||||
@ -397,21 +393,18 @@ mod tests {
|
||||
fn render_table() {
|
||||
let value = Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["country".to_string()],
|
||||
vals: vec![Value::test_string("Ecuador")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["country".to_string()],
|
||||
vals: vec![Value::test_string("New Zealand")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["country".to_string()],
|
||||
vals: vec![Value::test_string("USA")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
|
@ -209,8 +209,8 @@ pub fn value_to_string(
|
||||
for val in vals {
|
||||
let mut row = vec![];
|
||||
|
||||
if let Value::Record { vals, .. } = val {
|
||||
for val in vals {
|
||||
if let Value::Record { val, .. } = val {
|
||||
for val in &val.vals {
|
||||
row.push(value_to_string_without_quotes(
|
||||
val,
|
||||
span,
|
||||
@ -259,9 +259,9 @@ pub fn value_to_string(
|
||||
},
|
||||
value_to_string(&val.to, span, depth + 1, indent)?
|
||||
)),
|
||||
Value::Record { cols, vals, .. } => {
|
||||
Value::Record { val, .. } => {
|
||||
let mut collection = vec![];
|
||||
for (col, val) in cols.iter().zip(vals) {
|
||||
for (col, val) in val {
|
||||
collection.push(if needs_quotes(col) {
|
||||
format!(
|
||||
"{idt_po}\"{}\": {}",
|
||||
|
@ -132,14 +132,13 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
||||
}
|
||||
Value::String { val, .. } => val,
|
||||
Value::List { vals: val, .. } => val
|
||||
.iter()
|
||||
.map(|x| local_into_string(x.clone(), ", ", config))
|
||||
.into_iter()
|
||||
.map(|x| local_into_string(x, ", ", config))
|
||||
.collect::<Vec<_>>()
|
||||
.join(separator),
|
||||
Value::Record { cols, vals, .. } => cols
|
||||
.iter()
|
||||
.zip(vals.iter())
|
||||
.map(|(x, y)| format!("{}: {}", x, local_into_string(y.clone(), ", ", config)))
|
||||
Value::Record { val, .. } => val
|
||||
.into_iter()
|
||||
.map(|(x, y)| format!("{}: {}", x, local_into_string(y, ", ", config)))
|
||||
.collect::<Vec<_>>()
|
||||
.join(separator),
|
||||
Value::LazyRecord { val, .. } => match val.collect() {
|
||||
|
@ -54,9 +54,9 @@ fn helper(engine_state: &EngineState, v: &Value) -> Result<toml::Value, ShellErr
|
||||
Value::Range { .. } => toml::Value::String("<Range>".to_string()),
|
||||
Value::Float { val, .. } => toml::Value::Float(*val),
|
||||
Value::String { val, .. } => toml::Value::String(val.clone()),
|
||||
Value::Record { cols, vals, .. } => {
|
||||
Value::Record { val, .. } => {
|
||||
let mut m = toml::map::Map::new();
|
||||
for (k, v) in cols.iter().zip(vals.iter()) {
|
||||
for (k, v) in val {
|
||||
m.insert(k.clone(), helper(engine_state, v)?);
|
||||
}
|
||||
toml::Value::Table(m)
|
||||
@ -172,7 +172,6 @@ fn to_toml(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nu_protocol::Spanned;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
@ -201,10 +200,7 @@ mod tests {
|
||||
);
|
||||
let tv = value_to_toml_value(
|
||||
&engine_state,
|
||||
&Value::from(Spanned {
|
||||
item: m,
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
&Value::record(m.into_iter().collect(), Span::test_data()),
|
||||
Span::test_data(),
|
||||
)
|
||||
.expect("Expected Ok from valid TOML dictionary");
|
||||
|
@ -4,8 +4,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
|
||||
use std::io::Cursor;
|
||||
@ -208,9 +208,9 @@ fn to_tag_like<W: Write>(
|
||||
// Allow tag to have no attributes or content for short hand input
|
||||
// alternatives like {tag: a attributes: {} content: []}, {tag: a attribbutes: null
|
||||
// content: null}, {tag: a}. See to_xml_entry for more
|
||||
let (attr_cols, attr_values) = match attrs {
|
||||
Value::Record { cols, vals, .. } => (cols, vals),
|
||||
Value::Nothing { .. } => (Vec::new(), Vec::new()),
|
||||
let attrs = match attrs {
|
||||
Value::Record { val, .. } => val,
|
||||
Value::Nothing { .. } => Record::new(),
|
||||
_ => {
|
||||
return Err(ShellError::CantConvert {
|
||||
to_type: "XML".into(),
|
||||
@ -234,15 +234,7 @@ fn to_tag_like<W: Write>(
|
||||
}
|
||||
};
|
||||
|
||||
to_tag(
|
||||
entry_span,
|
||||
tag,
|
||||
tag_span,
|
||||
attr_cols,
|
||||
attr_values,
|
||||
content,
|
||||
writer,
|
||||
)
|
||||
to_tag(entry_span, tag, tag_span, attrs, content, writer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,8 +297,7 @@ fn to_tag<W: Write>(
|
||||
entry_span: Span,
|
||||
tag: String,
|
||||
tag_span: Span,
|
||||
attr_cols: Vec<String>,
|
||||
attr_vals: Vec<Value>,
|
||||
attrs: Record,
|
||||
children: Vec<Value>,
|
||||
writer: &mut quick_xml::Writer<W>,
|
||||
) -> Result<(), ShellError> {
|
||||
@ -322,7 +313,7 @@ fn to_tag<W: Write>(
|
||||
});
|
||||
}
|
||||
|
||||
let attributes = parse_attributes(attr_cols, attr_vals)?;
|
||||
let attributes = parse_attributes(attrs)?;
|
||||
let mut open_tag_event = BytesStart::new(tag.clone());
|
||||
add_attributes(&mut open_tag_event, &attributes);
|
||||
|
||||
@ -350,12 +341,9 @@ fn to_tag<W: Write>(
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_attributes(
|
||||
cols: Vec<String>,
|
||||
vals: Vec<Value>,
|
||||
) -> Result<IndexMap<String, String>, ShellError> {
|
||||
fn parse_attributes(attrs: Record) -> Result<IndexMap<String, String>, ShellError> {
|
||||
let mut h = IndexMap::new();
|
||||
for (k, v) in cols.into_iter().zip(vals) {
|
||||
for (k, v) in attrs {
|
||||
if let Value::String { val, .. } = v {
|
||||
h.insert(k, val);
|
||||
} else {
|
||||
|
@ -53,9 +53,9 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
|
||||
Value::Range { .. } => serde_yaml::Value::Null,
|
||||
Value::Float { val, .. } => serde_yaml::Value::Number(serde_yaml::Number::from(*val)),
|
||||
Value::String { val, .. } => serde_yaml::Value::String(val.clone()),
|
||||
Value::Record { cols, vals, .. } => {
|
||||
Value::Record { val, .. } => {
|
||||
let mut m = serde_yaml::Mapping::new();
|
||||
for (k, v) in cols.iter().zip(vals.iter()) {
|
||||
for (k, v) in val {
|
||||
m.insert(
|
||||
serde_yaml::Value::String(k.clone()),
|
||||
value_to_yaml_value(v)?,
|
||||
|
@ -359,18 +359,7 @@ fn add_month_to_table(
|
||||
day_number += 1;
|
||||
}
|
||||
|
||||
let cols: Vec<String> = indexmap.keys().map(|f| f.to_string()).collect();
|
||||
let mut vals: Vec<Value> = Vec::new();
|
||||
for c in &cols {
|
||||
if let Some(x) = indexmap.get(c) {
|
||||
vals.push(x.to_owned())
|
||||
}
|
||||
}
|
||||
calendar_vec_deque.push_back(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: tag,
|
||||
})
|
||||
calendar_vec_deque.push_back(Value::record(indexmap.into_iter().collect(), tag))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -148,8 +148,8 @@ pub fn highlight_search_in_table(
|
||||
let mut matches = vec![];
|
||||
|
||||
for record in table {
|
||||
let (cols, mut vals, record_span) = if let Value::Record { cols, vals, span } = record {
|
||||
(cols, vals, span)
|
||||
let (mut record, record_span) = if let Value::Record { val, span } = record {
|
||||
(val, span)
|
||||
} else {
|
||||
return Err(ShellError::NushellFailedSpanned {
|
||||
msg: "Expected record".to_string(),
|
||||
@ -158,7 +158,7 @@ pub fn highlight_search_in_table(
|
||||
});
|
||||
};
|
||||
|
||||
let has_match = cols.iter().zip(vals.iter_mut()).try_fold(
|
||||
let has_match = record.iter_mut().try_fold(
|
||||
false,
|
||||
|acc: bool, (col, val)| -> Result<bool, ShellError> {
|
||||
if !searched_cols.contains(&col.as_str()) {
|
||||
@ -186,11 +186,7 @@ pub fn highlight_search_in_table(
|
||||
)?;
|
||||
|
||||
if has_match {
|
||||
matches.push(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: record_span,
|
||||
});
|
||||
matches.push(Value::record(record, record_span));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,8 @@ use nu_engine::{get_full_help, CallExt};
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
span, Category, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||
record, span, Category, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
use std::borrow::Borrow;
|
||||
|
||||
@ -128,9 +128,6 @@ fn build_help_commands(engine_state: &EngineState, span: Span) -> Vec<Value> {
|
||||
let mut found_cmds_vec = Vec::new();
|
||||
|
||||
for (_, decl_id) in commands {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
let decl = engine_state.get_decl(decl_id);
|
||||
let sig = decl.signature().update_from_command(decl.borrow());
|
||||
|
||||
@ -138,79 +135,46 @@ fn build_help_commands(engine_state: &EngineState, span: Span) -> Vec<Value> {
|
||||
let usage = sig.usage;
|
||||
let search_terms = sig.search_terms;
|
||||
|
||||
cols.push("name".into());
|
||||
vals.push(Value::String { val: key, span });
|
||||
|
||||
cols.push("category".into());
|
||||
vals.push(Value::string(sig.category.to_string(), span));
|
||||
|
||||
cols.push("command_type".into());
|
||||
vals.push(Value::String {
|
||||
val: format!("{:?}", decl.command_type()).to_lowercase(),
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("usage".into());
|
||||
vals.push(Value::String { val: usage, span });
|
||||
|
||||
cols.push("params".into());
|
||||
let command_type = format!("{:?}", decl.command_type()).to_lowercase();
|
||||
|
||||
// Build table of parameters
|
||||
let param_table = {
|
||||
let mut vals = vec![];
|
||||
|
||||
for required_param in &sig.required_positional {
|
||||
vals.push(Value::Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"type".to_string(),
|
||||
"required".to_string(),
|
||||
"description".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(&required_param.name, span),
|
||||
Value::string(required_param.shape.to_string(), span),
|
||||
Value::bool(true, span),
|
||||
Value::string(&required_param.desc, span),
|
||||
],
|
||||
vals.push(Value::record(
|
||||
record! {
|
||||
"name" => Value::string(&required_param.name, span),
|
||||
"type" => Value::string(required_param.shape.to_string(), span),
|
||||
"required" => Value::bool(true, span),
|
||||
"description" => Value::string(&required_param.desc, span),
|
||||
},
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
for optional_param in &sig.optional_positional {
|
||||
vals.push(Value::Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"type".to_string(),
|
||||
"required".to_string(),
|
||||
"description".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(&optional_param.name, span),
|
||||
Value::string(optional_param.shape.to_string(), span),
|
||||
Value::bool(false, span),
|
||||
Value::string(&optional_param.desc, span),
|
||||
],
|
||||
vals.push(Value::record(
|
||||
record! {
|
||||
"name" => Value::string(&optional_param.name, span),
|
||||
"type" => Value::string(optional_param.shape.to_string(), span),
|
||||
"required" => Value::bool(false, span),
|
||||
"description" => Value::string(&optional_param.desc, span),
|
||||
},
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(rest_positional) = &sig.rest_positional {
|
||||
vals.push(Value::Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"type".to_string(),
|
||||
"required".to_string(),
|
||||
"description".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(format!("...{}", rest_positional.name), span),
|
||||
Value::string(rest_positional.shape.to_string(), span),
|
||||
Value::bool(false, span),
|
||||
Value::string(&rest_positional.desc, span),
|
||||
],
|
||||
vals.push(Value::record(
|
||||
record! {
|
||||
"name" => Value::string(format!("...{}", rest_positional.name), span),
|
||||
"type" => Value::string(rest_positional.shape.to_string(), span),
|
||||
"required" => Value::bool(false, span),
|
||||
"description" => Value::string(&rest_positional.desc, span),
|
||||
},
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
for named_param in &sig.named {
|
||||
@ -224,68 +188,54 @@ fn build_help_commands(engine_state: &EngineState, span: Span) -> Vec<Value> {
|
||||
format!("--{}", named_param.long)
|
||||
};
|
||||
|
||||
vals.push(Value::Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"type".to_string(),
|
||||
"required".to_string(),
|
||||
"description".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(name, span),
|
||||
Value::string(
|
||||
if let Some(arg) = &named_param.arg {
|
||||
arg.to_string()
|
||||
} else {
|
||||
"switch".to_string()
|
||||
},
|
||||
span,
|
||||
),
|
||||
Value::bool(named_param.required, span),
|
||||
Value::string(&named_param.desc, span),
|
||||
],
|
||||
let typ = if let Some(arg) = &named_param.arg {
|
||||
arg.to_string()
|
||||
} else {
|
||||
"switch".to_string()
|
||||
};
|
||||
|
||||
vals.push(Value::record(
|
||||
record! {
|
||||
"name" => Value::string(name, span),
|
||||
"type" => Value::string(typ, span),
|
||||
"required" => Value::bool(named_param.required, span),
|
||||
"description" => Value::string(&named_param.desc, span),
|
||||
},
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
Value::List { vals, span }
|
||||
};
|
||||
vals.push(param_table);
|
||||
|
||||
cols.push("input_output".into());
|
||||
|
||||
// Build the signature input/output table
|
||||
let input_output_table = {
|
||||
let mut vals = vec![];
|
||||
|
||||
for (input_type, output_type) in sig.input_output_types {
|
||||
vals.push(Value::Record {
|
||||
cols: vec!["input".to_string(), "output".to_string()],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
val: input_type.to_string(),
|
||||
span,
|
||||
},
|
||||
Value::String {
|
||||
val: output_type.to_string(),
|
||||
span,
|
||||
},
|
||||
],
|
||||
vals.push(Value::record(
|
||||
record! {
|
||||
"input" => Value::string(input_type.to_string(), span),
|
||||
"output" => Value::string(output_type.to_string(), span),
|
||||
},
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
Value::List { vals, span }
|
||||
};
|
||||
vals.push(input_output_table);
|
||||
|
||||
cols.push("search_terms".into());
|
||||
vals.push(Value::String {
|
||||
val: search_terms.join(", "),
|
||||
span,
|
||||
});
|
||||
let record = record! {
|
||||
"name" => Value::string(key, span),
|
||||
"category" => Value::string(sig.category.to_string(), span),
|
||||
"command_type" => Value::string(command_type, span),
|
||||
"usage" => Value::string(usage, span),
|
||||
"params" => param_table,
|
||||
"input_output" => input_output_table,
|
||||
"search_terms" => Value::string(search_terms.join(", "), span),
|
||||
};
|
||||
|
||||
found_cmds_vec.push(Value::Record { cols, vals, span });
|
||||
found_cmds_vec.push(Value::record(record, span));
|
||||
}
|
||||
|
||||
found_cmds_vec
|
||||
|
@ -1,7 +1,8 @@
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type, Value,
|
||||
record, Category, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -35,23 +36,16 @@ impl Command for HelpOperators {
|
||||
let mut recs = vec![];
|
||||
|
||||
for op in op_info {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
cols.push("type".into());
|
||||
vals.push(Value::string(op.op_type, head));
|
||||
cols.push("operator".into());
|
||||
vals.push(Value::string(op.operator, head));
|
||||
cols.push("name".into());
|
||||
vals.push(Value::string(op.name, head));
|
||||
cols.push("description".into());
|
||||
vals.push(Value::string(op.description, head));
|
||||
cols.push("precedence".into());
|
||||
vals.push(Value::int(op.precedence, head));
|
||||
recs.push(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
})
|
||||
recs.push(Value::record(
|
||||
record! {
|
||||
"type" => Value::string(op.op_type, head),
|
||||
"operator" => Value::string(op.operator, head),
|
||||
"name" => Value::string(op.name, head),
|
||||
"description" => Value::string(op.description, head),
|
||||
"precedence" => Value::int(op.precedence, head),
|
||||
},
|
||||
head,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(recs
|
||||
|
@ -14,12 +14,8 @@ pub fn eval_env_change_hook(
|
||||
) -> Result<(), ShellError> {
|
||||
if let Some(hook) = env_change_hook {
|
||||
match hook {
|
||||
Value::Record {
|
||||
cols: env_names,
|
||||
vals: hook_values,
|
||||
..
|
||||
} => {
|
||||
for (env_name, hook_value) in env_names.iter().zip(hook_values.iter()) {
|
||||
Value::Record { val, .. } => {
|
||||
for (env_name, hook_value) in &val {
|
||||
let before = engine_state
|
||||
.previous_env_vars
|
||||
.get(env_name)
|
||||
|
@ -2,7 +2,9 @@ use crate::math::reducers::{reducer_for, Reduce};
|
||||
use crate::math::utils::run_with_function;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -50,11 +52,10 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Find the maxima of the columns of a table",
|
||||
example: "[{a: 1 b: 3} {a: 2 b: -1}] | math max",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_int(2), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ use crate::math::avg::average;
|
||||
use crate::math::utils::run_with_function;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -54,11 +56,10 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Compute the medians of the columns of a table",
|
||||
example: "[{a: 1 b: 3} {a: 2 b: -1} {a: -3 b: 5}] | math median",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ use crate::math::reducers::{reducer_for, Reduce};
|
||||
use crate::math::utils::run_with_function;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -50,11 +52,10 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Compute the minima of the columns of a table",
|
||||
example: "[{a: 1 b: 3} {a: 2 b: -1}] | math min",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(-1)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Find the minimum of a list of arbitrary values (Warning: Weird)",
|
||||
|
@ -1,7 +1,9 @@
|
||||
use crate::math::utils::run_with_function;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -88,7 +90,7 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Compute the mode(s) of the columns of a table",
|
||||
example: "[{a: 1 b: 3} {a: 2 b: -1} {a: 1 b: 5}] | math mode",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![
|
||||
Value::List {
|
||||
@ -100,8 +102,7 @@ impl Command for SubCommand {
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Spanned, Value};
|
||||
use nu_protocol::{IntoPipelineData, PipelineData, Record, ShellError, Span, Value};
|
||||
|
||||
pub fn run_with_function(
|
||||
call: &Call,
|
||||
@ -26,8 +26,8 @@ fn helper_for_tables(
|
||||
let mut column_values = IndexMap::new();
|
||||
for val in values {
|
||||
match val {
|
||||
Value::Record { cols, vals, .. } => {
|
||||
for (key, value) in cols.iter().zip(vals.iter()) {
|
||||
Value::Record { val, .. } => {
|
||||
for (key, value) in val {
|
||||
column_values
|
||||
.entry(key.clone())
|
||||
.and_modify(|v: &mut Vec<Value>| v.push(value.clone()))
|
||||
@ -57,10 +57,7 @@ fn helper_for_tables(
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Value::from(Spanned {
|
||||
item: column_totals,
|
||||
span: name,
|
||||
}))
|
||||
Ok(Value::record(column_totals.into_iter().collect(), name))
|
||||
}
|
||||
|
||||
pub fn calculate(
|
||||
@ -83,15 +80,20 @@ pub fn calculate(
|
||||
),
|
||||
_ => mf(vals, span, name),
|
||||
},
|
||||
PipelineData::Value(Value::Record { vals, cols, span }, ..) => {
|
||||
let new_vals: Result<Vec<Value>, ShellError> =
|
||||
vals.into_iter().map(|val| mf(&[val], span, name)).collect();
|
||||
PipelineData::Value(Value::Record { val: record, span }, ..) => {
|
||||
let new_vals: Result<Vec<Value>, ShellError> = record
|
||||
.vals
|
||||
.into_iter()
|
||||
.map(|val| mf(&[val], span, name))
|
||||
.collect();
|
||||
match new_vals {
|
||||
Ok(vec) => Ok(Value::Record {
|
||||
cols,
|
||||
vals: vec,
|
||||
Ok(vec) => Ok(Value::record(
|
||||
Record {
|
||||
cols: record.cols,
|
||||
vals: vec,
|
||||
},
|
||||
span,
|
||||
}),
|
||||
)),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use base64::{alphabet, Engine};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
BufferedReader, IntoPipelineData, PipelineData, RawStream, ShellError, Span, Value,
|
||||
record, BufferedReader, IntoPipelineData, PipelineData, RawStream, ShellError, Span, Value,
|
||||
};
|
||||
use ureq::{Error, ErrorKind, Request, Response};
|
||||
|
||||
@ -183,12 +183,12 @@ pub fn send_request(
|
||||
let data = value_to_json_value(&body)?;
|
||||
send_cancellable_request(&request_url, Box::new(|| request.send_json(data)), ctrl_c)
|
||||
}
|
||||
Value::Record { cols, vals, .. } if body_type == BodyType::Form => {
|
||||
let mut data: Vec<(String, String)> = Vec::with_capacity(cols.len());
|
||||
Value::Record { val, .. } if body_type == BodyType::Form => {
|
||||
let mut data: Vec<(String, String)> = Vec::with_capacity(val.len());
|
||||
|
||||
for (col, val) in cols.iter().zip(vals.iter()) {
|
||||
for (col, val) in val {
|
||||
let val_string = val.as_string()?;
|
||||
data.push((col.clone(), val_string))
|
||||
data.push((col, val_string))
|
||||
}
|
||||
|
||||
let request_fn = move || {
|
||||
@ -296,8 +296,8 @@ pub fn request_add_custom_headers(
|
||||
let mut custom_headers: HashMap<String, Value> = HashMap::new();
|
||||
|
||||
match &headers {
|
||||
Value::Record { cols, vals, .. } => {
|
||||
for (k, v) in cols.iter().zip(vals.iter()) {
|
||||
Value::Record { val, .. } => {
|
||||
for (k, v) in val {
|
||||
custom_headers.insert(k.to_string(), v.clone());
|
||||
}
|
||||
}
|
||||
@ -306,8 +306,8 @@ pub fn request_add_custom_headers(
|
||||
if table.len() == 1 {
|
||||
// single row([key1 key2]; [val1 val2])
|
||||
match &table[0] {
|
||||
Value::Record { cols, vals, .. } => {
|
||||
for (k, v) in cols.iter().zip(vals.iter()) {
|
||||
Value::Record { val, .. } => {
|
||||
for (k, v) in val {
|
||||
custom_headers.insert(k.to_string(), v.clone());
|
||||
}
|
||||
}
|
||||
@ -498,25 +498,19 @@ fn request_handle_response_content(
|
||||
Err(_) => Value::nothing(span),
|
||||
};
|
||||
|
||||
let headers = Value::Record {
|
||||
cols: vec!["request".to_string(), "response".to_string()],
|
||||
vals: vec![request_headers_value, response_headers_value],
|
||||
span,
|
||||
let headers = record! {
|
||||
"request" => request_headers_value,
|
||||
"response" => response_headers_value,
|
||||
};
|
||||
|
||||
let full_response = Value::Record {
|
||||
cols: vec![
|
||||
"headers".to_string(),
|
||||
"body".to_string(),
|
||||
"status".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
headers,
|
||||
consume_response_body(resp)?.into_value(span),
|
||||
Value::int(response_status as i64, span),
|
||||
],
|
||||
let full_response = Value::record(
|
||||
record! {
|
||||
"headers" => Value::record(headers, span),
|
||||
"body" => consume_response_body(resp)?.into_value(span),
|
||||
"status" => Value::int(response_status as i64, span),
|
||||
},
|
||||
span,
|
||||
};
|
||||
);
|
||||
|
||||
Ok(full_response.into_pipeline_data())
|
||||
} else {
|
||||
@ -597,15 +591,14 @@ fn extract_response_headers(response: &Response) -> Headers {
|
||||
}
|
||||
|
||||
fn headers_to_nu(headers: &Headers, span: Span) -> Result<PipelineData, ShellError> {
|
||||
let cols = vec!["name".to_string(), "value".to_string()];
|
||||
let mut vals = Vec::with_capacity(headers.len());
|
||||
|
||||
for (name, values) in headers {
|
||||
let is_duplicate = vals.iter().any(|val| {
|
||||
if let Value::Record { vals, .. } = val {
|
||||
if let Value::Record { val, .. } = val {
|
||||
if let Some(Value::String {
|
||||
val: header_name, ..
|
||||
}) = vals.get(0)
|
||||
}) = val.vals.get(0)
|
||||
{
|
||||
return name == header_name;
|
||||
}
|
||||
@ -616,11 +609,11 @@ fn headers_to_nu(headers: &Headers, span: Span) -> Result<PipelineData, ShellErr
|
||||
// A single header can hold multiple values
|
||||
// This interface is why we needed to check if we've already parsed this header name.
|
||||
for str_value in values {
|
||||
let header = vec![
|
||||
Value::string(name, span),
|
||||
Value::string(str_value.to_string(), span),
|
||||
];
|
||||
vals.push(Value::record(cols.clone(), header, span));
|
||||
let record = record! {
|
||||
"name" => Value::string(name, span),
|
||||
"value" => Value::string(str_value, span),
|
||||
};
|
||||
vals.push(Value::record(record, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,13 +65,9 @@ fn to_url(input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
||||
let output: Result<String, ShellError> = input
|
||||
.into_iter()
|
||||
.map(move |value| match value {
|
||||
Value::Record {
|
||||
ref cols,
|
||||
ref vals,
|
||||
span,
|
||||
} => {
|
||||
Value::Record { ref val, span } => {
|
||||
let mut row_vec = vec![];
|
||||
for (k, v) in cols.iter().zip(vals.iter()) {
|
||||
for (k, v) in val {
|
||||
match v.as_string() {
|
||||
Ok(s) => {
|
||||
row_vec.push((k.clone(), s.to_string()));
|
||||
|
@ -92,16 +92,11 @@ impl Command for SubCommand {
|
||||
let output: Result<String, ShellError> = input
|
||||
.into_iter()
|
||||
.map(move |value| match value {
|
||||
Value::Record {
|
||||
ref cols,
|
||||
ref vals,
|
||||
span,
|
||||
} => {
|
||||
let url_components = cols
|
||||
.iter()
|
||||
.zip(vals.iter())
|
||||
Value::Record { val, span } => {
|
||||
let url_components = val
|
||||
.into_iter()
|
||||
.try_fold(UrlComponents::new(), |url, (k, v)| {
|
||||
url.add_component(k.clone(), v.clone(), span)
|
||||
url.add_component(k, v, span)
|
||||
});
|
||||
|
||||
url_components?.to_url(span)
|
||||
@ -176,14 +171,9 @@ impl UrlComponents {
|
||||
|
||||
if key == "params" {
|
||||
return match value {
|
||||
Value::Record {
|
||||
ref cols,
|
||||
ref vals,
|
||||
span,
|
||||
} => {
|
||||
let mut qs = cols
|
||||
Value::Record { ref val, span } => {
|
||||
let mut qs = val
|
||||
.iter()
|
||||
.zip(vals.iter())
|
||||
.map(|(k, v)| match v.as_string() {
|
||||
Ok(val) => Ok(format!("{k}={val}")),
|
||||
Err(err) => Err(err),
|
||||
|
@ -2,7 +2,8 @@ use super::url;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
use url::Url;
|
||||
@ -55,7 +56,7 @@ impl Command for SubCommand {
|
||||
vec![Example {
|
||||
description: "Parses a url",
|
||||
example: "'http://user123:pass567@www.example.com:8081/foo/bar?param1=section&p2=&f[name]=vldc#hello' | url parse",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"scheme".to_string(),
|
||||
"username".to_string(),
|
||||
@ -76,18 +77,16 @@ impl Command for SubCommand {
|
||||
Value::test_string("/foo/bar"),
|
||||
Value::test_string("param1=section&p2=&f[name]=vldc"),
|
||||
Value::test_string("hello"),
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["param1".to_string(), "p2".to_string(), "f[name]".to_string()],
|
||||
vals: vec![
|
||||
Value::test_string("section"),
|
||||
Value::test_string(""),
|
||||
Value::test_string("vldc"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -106,80 +105,31 @@ fn parse(value: Value, head: Span, engine_state: &EngineState) -> Result<Pipelin
|
||||
|
||||
match result_url {
|
||||
Ok(url) => {
|
||||
let cols = vec![
|
||||
String::from("scheme"),
|
||||
String::from("username"),
|
||||
String::from("password"),
|
||||
String::from("host"),
|
||||
String::from("port"),
|
||||
String::from("path"),
|
||||
String::from("query"),
|
||||
String::from("fragment"),
|
||||
String::from("params"),
|
||||
];
|
||||
let mut vals: Vec<Value> = vec![
|
||||
Value::String {
|
||||
val: String::from(url.scheme()),
|
||||
span: head,
|
||||
},
|
||||
Value::String {
|
||||
val: String::from(url.username()),
|
||||
span: head,
|
||||
},
|
||||
Value::String {
|
||||
val: String::from(url.password().unwrap_or("")),
|
||||
span: head,
|
||||
},
|
||||
Value::String {
|
||||
val: String::from(url.host_str().unwrap_or("")),
|
||||
span: head,
|
||||
},
|
||||
Value::String {
|
||||
val: url
|
||||
.port()
|
||||
.map(|p| p.to_string())
|
||||
.unwrap_or_else(|| "".into()),
|
||||
span: head,
|
||||
},
|
||||
Value::String {
|
||||
val: String::from(url.path()),
|
||||
span: head,
|
||||
},
|
||||
Value::String {
|
||||
val: String::from(url.query().unwrap_or("")),
|
||||
span: head,
|
||||
},
|
||||
Value::String {
|
||||
val: String::from(url.fragment().unwrap_or("")),
|
||||
span: head,
|
||||
},
|
||||
];
|
||||
|
||||
let params =
|
||||
serde_urlencoded::from_str::<Vec<(String, String)>>(url.query().unwrap_or(""));
|
||||
match params {
|
||||
Ok(result) => {
|
||||
let (param_cols, param_vals) = result
|
||||
let params = result
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, Value::String { val: v, span: head }))
|
||||
.unzip();
|
||||
.map(|(k, v)| (k, Value::string(v, head)))
|
||||
.collect();
|
||||
|
||||
vals.push(Value::Record {
|
||||
cols: param_cols,
|
||||
vals: param_vals,
|
||||
span: head,
|
||||
});
|
||||
let port = url.port().map(|p| p.to_string()).unwrap_or_default();
|
||||
|
||||
Ok(PipelineData::Value(
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
},
|
||||
None,
|
||||
))
|
||||
let record = record! {
|
||||
"scheme" => Value::string(url.scheme(), head),
|
||||
"username" => Value::string(url.username(), head),
|
||||
"password" => Value::string(url.password().unwrap_or(""), head),
|
||||
"host" => Value::string(url.host_str().unwrap_or(""), head),
|
||||
"port" => Value::string(port, head),
|
||||
"path" => Value::string(url.path(), head),
|
||||
"query" => Value::string(url.query().unwrap_or(""), head),
|
||||
"fragment" => Value::string(url.fragment().unwrap_or(""), head),
|
||||
"params" => Value::record(params, head),
|
||||
};
|
||||
|
||||
Ok(PipelineData::Value(Value::record(record, head), None))
|
||||
}
|
||||
|
||||
_ => Err(ShellError::UnsupportedInput(
|
||||
"String not compatible with url-encoding".to_string(),
|
||||
"value originates from here".into(),
|
||||
|
@ -5,7 +5,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
engine::Command, Category, Example, PipelineData, ShellError, Signature, Span, Spanned,
|
||||
engine::Command, Category, Example, PipelineData, Record, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
@ -150,7 +150,7 @@ the output of 'path parse' and 'path split' subcommands."#
|
||||
fn handle_value(v: Value, args: &Arguments, head: Span) -> Value {
|
||||
match v {
|
||||
Value::String { ref val, .. } => join_single(Path::new(val), head, args),
|
||||
Value::Record { cols, vals, span } => join_record(&cols, &vals, head, span, args),
|
||||
Value::Record { val, span } => join_record(&val, head, span, args),
|
||||
Value::List { vals, span } => join_list(&vals, head, span, args),
|
||||
|
||||
_ => super::handle_invalid_values(v, head),
|
||||
@ -177,7 +177,7 @@ fn join_list(parts: &[Value], head: Span, span: Span, args: &Arguments) -> Value
|
||||
Ok(vals) => {
|
||||
let vals = vals
|
||||
.iter()
|
||||
.map(|(k, v)| join_record(k, v, head, span, args))
|
||||
.map(|r| join_record(r, head, span, args))
|
||||
.collect();
|
||||
|
||||
Value::List { vals, span }
|
||||
@ -194,8 +194,8 @@ fn join_list(parts: &[Value], head: Span, span: Span, args: &Arguments) -> Value
|
||||
}
|
||||
}
|
||||
|
||||
fn join_record(cols: &[String], vals: &[Value], head: Span, span: Span, args: &Arguments) -> Value {
|
||||
match merge_record(cols, vals, head, span) {
|
||||
fn join_record(record: &Record, head: Span, span: Span, args: &Arguments) -> Value {
|
||||
match merge_record(record, head, span) {
|
||||
Ok(p) => join_single(p.as_path(), head, args),
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
@ -203,13 +203,8 @@ fn join_record(cols: &[String], vals: &[Value], head: Span, span: Span, args: &A
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_record(
|
||||
cols: &[String],
|
||||
vals: &[Value],
|
||||
head: Span,
|
||||
span: Span,
|
||||
) -> Result<PathBuf, ShellError> {
|
||||
for key in cols {
|
||||
fn merge_record(record: &Record, head: Span, span: Span) -> Result<PathBuf, ShellError> {
|
||||
for key in &record.cols {
|
||||
if !super::ALLOWED_COLUMNS.contains(&key.as_str()) {
|
||||
let allowed_cols = super::ALLOWED_COLUMNS.join(", ");
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
@ -223,7 +218,13 @@ fn merge_record(
|
||||
}
|
||||
}
|
||||
|
||||
let entries: HashMap<&str, &Value> = cols.iter().map(String::as_str).zip(vals).collect();
|
||||
let entries: HashMap<&str, &Value> = record
|
||||
.cols
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.zip(&record.vals)
|
||||
.collect();
|
||||
|
||||
let mut result = PathBuf::new();
|
||||
|
||||
#[cfg(windows)]
|
||||
|
@ -1,11 +1,10 @@
|
||||
use std::path::Path;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
engine::Command, Category, Example, PipelineData, ShellError, Signature, Span, Spanned,
|
||||
engine::Command, Category, Example, PipelineData, Record, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
@ -77,7 +76,7 @@ On Windows, an extra 'prefix' column is added."#
|
||||
Example {
|
||||
description: "Parse a single path",
|
||||
example: r"'C:\Users\viking\spam.txt' | path parse",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"prefix".into(),
|
||||
"parent".into(),
|
||||
@ -90,8 +89,7 @@ On Windows, an extra 'prefix' column is added."#
|
||||
Value::test_string("spam"),
|
||||
Value::test_string("txt"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Replace a complex extension",
|
||||
@ -101,7 +99,7 @@ On Windows, an extra 'prefix' column is added."#
|
||||
Example {
|
||||
description: "Ignore the extension",
|
||||
example: r"'C:\Users\viking.d' | path parse -e ''",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"prefix".into(),
|
||||
"parent".into(),
|
||||
@ -114,14 +112,13 @@ On Windows, an extra 'prefix' column is added."#
|
||||
Value::test_string("viking.d"),
|
||||
Value::test_string(""),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Parse all paths in a list",
|
||||
example: r"[ C:\Users\viking.d C:\Users\spam.txt ] | path parse",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"prefix".into(),
|
||||
"parent".into(),
|
||||
@ -134,9 +131,8 @@ On Windows, an extra 'prefix' column is added."#
|
||||
Value::test_string("viking"),
|
||||
Value::test_string("d"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"prefix".into(),
|
||||
"parent".into(),
|
||||
@ -149,8 +145,7 @@ On Windows, an extra 'prefix' column is added."#
|
||||
Value::test_string("spam"),
|
||||
Value::test_string("txt"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
])),
|
||||
},
|
||||
]
|
||||
@ -162,15 +157,14 @@ On Windows, an extra 'prefix' column is added."#
|
||||
Example {
|
||||
description: "Parse a path",
|
||||
example: r"'/home/viking/spam.txt' | path parse",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["parent".into(), "stem".into(), "extension".into()],
|
||||
vals: vec![
|
||||
Value::test_string("/home/viking"),
|
||||
Value::test_string("spam"),
|
||||
Value::test_string("txt"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Replace a complex extension",
|
||||
@ -180,38 +174,35 @@ On Windows, an extra 'prefix' column is added."#
|
||||
Example {
|
||||
description: "Ignore the extension",
|
||||
example: r"'/etc/conf.d' | path parse -e ''",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["parent".into(), "stem".into(), "extension".into()],
|
||||
vals: vec![
|
||||
Value::test_string("/etc"),
|
||||
Value::test_string("conf.d"),
|
||||
Value::test_string(""),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Parse all paths in a list",
|
||||
example: r"[ /home/viking.d /home/spam.txt ] | path parse",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["parent".into(), "stem".into(), "extension".into()],
|
||||
vals: vec![
|
||||
Value::test_string("/home"),
|
||||
Value::test_string("viking"),
|
||||
Value::test_string("d"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["parent".into(), "stem".into(), "extension".into()],
|
||||
vals: vec![
|
||||
Value::test_string("/home"),
|
||||
Value::test_string("spam"),
|
||||
Value::test_string("txt"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
])),
|
||||
},
|
||||
]
|
||||
@ -219,7 +210,7 @@ On Windows, an extra 'prefix' column is added."#
|
||||
}
|
||||
|
||||
fn parse(path: &Path, span: Span, args: &Arguments) -> Value {
|
||||
let mut map: IndexMap<String, Value> = IndexMap::new();
|
||||
let mut record = Record::new();
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
@ -231,7 +222,7 @@ fn parse(path: &Path, span: Span, args: &Arguments) -> Value {
|
||||
}
|
||||
_ => "".into(),
|
||||
};
|
||||
map.insert("prefix".into(), Value::string(prefix, span));
|
||||
record.push("prefix", Value::string(prefix, span));
|
||||
}
|
||||
|
||||
let parent = path
|
||||
@ -239,7 +230,7 @@ fn parse(path: &Path, span: Span, args: &Arguments) -> Value {
|
||||
.unwrap_or_else(|| "".as_ref())
|
||||
.to_string_lossy();
|
||||
|
||||
map.insert("parent".into(), Value::string(parent, span));
|
||||
record.push("parent", Value::string(parent, span));
|
||||
|
||||
let basename = path
|
||||
.file_name()
|
||||
@ -254,14 +245,11 @@ fn parse(path: &Path, span: Span, args: &Arguments) -> Value {
|
||||
let ext_with_dot = [".", extension].concat();
|
||||
if basename.ends_with(&ext_with_dot) && !extension.is_empty() {
|
||||
let stem = basename.trim_end_matches(&ext_with_dot);
|
||||
map.insert("stem".into(), Value::string(stem, span));
|
||||
map.insert(
|
||||
"extension".into(),
|
||||
Value::string(extension, *extension_span),
|
||||
);
|
||||
record.push("stem", Value::string(stem, span));
|
||||
record.push("extension", Value::string(extension, *extension_span));
|
||||
} else {
|
||||
map.insert("stem".into(), Value::string(basename, span));
|
||||
map.insert("extension".into(), Value::string("", span));
|
||||
record.push("stem", Value::string(basename, span));
|
||||
record.push("extension", Value::string("", span));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@ -274,12 +262,12 @@ fn parse(path: &Path, span: Span, args: &Arguments) -> Value {
|
||||
.unwrap_or_else(|| "".as_ref())
|
||||
.to_string_lossy();
|
||||
|
||||
map.insert("stem".into(), Value::string(stem, span));
|
||||
map.insert("extension".into(), Value::string(extension, span));
|
||||
record.push("stem", Value::string(stem, span));
|
||||
record.push("extension", Value::string(extension, span));
|
||||
}
|
||||
}
|
||||
|
||||
Value::from(Spanned { item: map, span })
|
||||
Value::record(record, span)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,6 +1,7 @@
|
||||
use nu_ansi_term::*;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::record;
|
||||
use nu_protocol::{
|
||||
ast::Call, engine::Command, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData,
|
||||
PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
@ -763,7 +764,7 @@ Operating system commands:
|
||||
attr: None,
|
||||
};
|
||||
// Iterate and populate NuStyle with real values
|
||||
for (k, v) in record.0.iter().zip(record.1) {
|
||||
for (k, v) in record {
|
||||
match k.as_str() {
|
||||
"fg" => nu_style.fg = Some(v.as_string()?),
|
||||
"bg" => nu_style.bg = Some(v.as_string()?),
|
||||
@ -799,17 +800,7 @@ fn generate_ansi_code_list(
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(move |(i, ansi_code)| {
|
||||
let cols = if use_ansi_coloring {
|
||||
vec![
|
||||
"name".into(),
|
||||
"preview".into(),
|
||||
"short name".into(),
|
||||
"code".into(),
|
||||
]
|
||||
} else {
|
||||
vec!["name".into(), "short name".into(), "code".into()]
|
||||
};
|
||||
let name: Value = Value::string(String::from(ansi_code.long_name), call_span);
|
||||
let name = Value::string(ansi_code.long_name, call_span);
|
||||
let short_name = Value::string(ansi_code.short_name.unwrap_or(""), call_span);
|
||||
// The first 102 items in the ansi array are colors
|
||||
let preview = if i < 389 {
|
||||
@ -817,18 +808,24 @@ fn generate_ansi_code_list(
|
||||
} else {
|
||||
Value::string("\u{1b}[0m", call_span)
|
||||
};
|
||||
let code_string = String::from(&ansi_code.code.replace('\u{1b}', "\\e"));
|
||||
let code = Value::string(code_string, call_span);
|
||||
let vals = if use_ansi_coloring {
|
||||
vec![name, preview, short_name, code]
|
||||
let code = Value::string(ansi_code.code.replace('\u{1b}', "\\e"), call_span);
|
||||
|
||||
let record = if use_ansi_coloring {
|
||||
record! {
|
||||
"name" => name,
|
||||
"preview" => preview,
|
||||
"short name" => short_name,
|
||||
"code" => code,
|
||||
}
|
||||
} else {
|
||||
vec![name, short_name, code]
|
||||
record! {
|
||||
"name" => name,
|
||||
"short name" => short_name,
|
||||
"code" => code,
|
||||
}
|
||||
};
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: call_span,
|
||||
}
|
||||
|
||||
Value::record(record, call_span)
|
||||
})
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()));
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use filesize::file_real_size_fast;
|
||||
use nu_glob::Pattern;
|
||||
use nu_protocol::{ShellError, Span, Value};
|
||||
use nu_protocol::{record, ShellError, Span, Value};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
@ -185,30 +185,6 @@ impl DirInfo {
|
||||
|
||||
impl From<DirInfo> for Value {
|
||||
fn from(d: DirInfo) -> Self {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
cols.push("path".into());
|
||||
vals.push(Value::string(d.path.display().to_string(), d.tag));
|
||||
|
||||
cols.push("apparent".into());
|
||||
vals.push(Value::Filesize {
|
||||
val: d.size as i64,
|
||||
span: d.tag,
|
||||
});
|
||||
|
||||
cols.push("physical".into());
|
||||
vals.push(Value::Filesize {
|
||||
val: d.blocks as i64,
|
||||
span: d.tag,
|
||||
});
|
||||
|
||||
cols.push("directories".into());
|
||||
vals.push(value_from_vec(d.dirs, d.tag));
|
||||
|
||||
cols.push("files".into());
|
||||
vals.push(value_from_vec(d.files, d.tag));
|
||||
|
||||
// if !d.errors.is_empty() {
|
||||
// let v = d
|
||||
// .errors
|
||||
@ -223,51 +199,34 @@ impl From<DirInfo> for Value {
|
||||
// })
|
||||
// }
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: d.tag,
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"path" => Value::string(d.path.display().to_string(), d.tag),
|
||||
"apparent" => Value::filesize(d.size as i64, d.tag),
|
||||
"physical" => Value::filesize(d.blocks as i64, d.tag),
|
||||
"directories" => value_from_vec(d.dirs, d.tag),
|
||||
"files" => value_from_vec(d.files, d.tag)
|
||||
},
|
||||
d.tag,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FileInfo> for Value {
|
||||
fn from(f: FileInfo) -> Self {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
cols.push("path".into());
|
||||
vals.push(Value::string(f.path.display().to_string(), f.tag));
|
||||
|
||||
cols.push("apparent".into());
|
||||
vals.push(Value::Filesize {
|
||||
val: f.size as i64,
|
||||
span: f.tag,
|
||||
});
|
||||
|
||||
cols.push("physical".into());
|
||||
vals.push(Value::Filesize {
|
||||
val: match f.blocks {
|
||||
Some(b) => b as i64,
|
||||
None => 0i64,
|
||||
},
|
||||
span: f.tag,
|
||||
});
|
||||
|
||||
cols.push("directories".into());
|
||||
vals.push(Value::nothing(Span::unknown()));
|
||||
|
||||
cols.push("files".into());
|
||||
vals.push(Value::nothing(Span::unknown()));
|
||||
|
||||
// cols.push("errors".into());
|
||||
// vals.push(Value::nothing(Span::unknown()));
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: f.tag,
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"path" => Value::string(f.path.display().to_string(), f.tag),
|
||||
"apparent" => Value::filesize(f.size as i64, f.tag),
|
||||
"physical" => Value::filesize(f.blocks.unwrap_or(0) as i64, f.tag),
|
||||
"directories" => Value::nothing(Span::unknown()),
|
||||
"files" => Value::nothing(Span::unknown()),
|
||||
},
|
||||
f.tag,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user