forked from extern/nushell
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:
parent
030e749fe7
commit
8da27a1a09
@ -114,13 +114,14 @@ fn eval_benchmarks(c: &mut Criterion) {
|
||||
|
||||
// generate a new table data with `row_cnt` rows, `col_cnt` columns.
|
||||
fn encoding_test_data(row_cnt: usize, col_cnt: usize) -> Value {
|
||||
let columns: Vec<String> = (0..col_cnt).map(|x| format!("col_{x}")).collect();
|
||||
let vals: Vec<Value> = (0..col_cnt as i64).map(Value::test_int).collect();
|
||||
let record = Value::test_record(
|
||||
(0..col_cnt)
|
||||
.map(|x| (format!("col_{x}"), Value::test_int(x as i64)))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
Value::List {
|
||||
vals: (0..row_cnt)
|
||||
.map(|_| Value::test_record(columns.clone(), vals.clone()))
|
||||
.collect(),
|
||||
vals: vec![record; row_cnt],
|
||||
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, HistoryFileFormat, IntoInterruptiblePipelineData, PipelineData, ShellError,
|
||||
Signature, Span, Type, Value,
|
||||
record, Category, Example, HistoryFileFormat, IntoInterruptiblePipelineData, PipelineData,
|
||||
ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
use reedline::{
|
||||
FileBackedHistory, History as ReedlineHistory, HistoryItem, SearchDirection, SearchQuery,
|
||||
@ -95,19 +95,14 @@ impl Command for History {
|
||||
.ok()
|
||||
})
|
||||
.map(move |entries| {
|
||||
entries
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(move |(idx, entry)| Value::Record {
|
||||
cols: vec!["command".to_string(), "index".to_string()],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
val: entry.command_line,
|
||||
span: head,
|
||||
entries.into_iter().enumerate().map(move |(idx, entry)| {
|
||||
Value::record(
|
||||
record! {
|
||||
"command" => Value::string(entry.command_line, head),
|
||||
"index" => Value::int(idx as i64, head),
|
||||
},
|
||||
Value::int(idx as i64, head),
|
||||
],
|
||||
span: head,
|
||||
head,
|
||||
)
|
||||
})
|
||||
})
|
||||
.ok_or(ShellError::FileNotFound(head))?
|
||||
@ -217,48 +212,30 @@ fn create_history_record(idx: usize, entry: HistoryItem, long: bool, head: Span)
|
||||
let exit_status_value = Value::int(entry.exit_status.unwrap_or(0), head);
|
||||
let index_value = Value::int(idx as i64, head);
|
||||
if long {
|
||||
Value::Record {
|
||||
cols: vec![
|
||||
"item_id".into(),
|
||||
"start_timestamp".into(),
|
||||
"command".to_string(),
|
||||
"session_id".into(),
|
||||
"hostname".into(),
|
||||
"cwd".into(),
|
||||
"duration".into(),
|
||||
"exit_status".into(),
|
||||
"idx".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
item_id_value,
|
||||
start_timestamp_value,
|
||||
command_value,
|
||||
session_id_value,
|
||||
hostname_value,
|
||||
cwd_value,
|
||||
duration_value,
|
||||
exit_status_value,
|
||||
index_value,
|
||||
],
|
||||
span: head,
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"item_id" => item_id_value,
|
||||
"start_timestamp" => start_timestamp_value,
|
||||
"command" => command_value,
|
||||
"session_id" => session_id_value,
|
||||
"hostname" => hostname_value,
|
||||
"cwd" => cwd_value,
|
||||
"duration" => duration_value,
|
||||
"exit_status" => exit_status_value,
|
||||
"idx" => index_value,
|
||||
},
|
||||
head,
|
||||
)
|
||||
} else {
|
||||
Value::Record {
|
||||
cols: vec![
|
||||
"start_timestamp".into(),
|
||||
"command".to_string(),
|
||||
"cwd".into(),
|
||||
"duration".into(),
|
||||
"exit_status".into(),
|
||||
],
|
||||
vals: vec![
|
||||
start_timestamp_value,
|
||||
command_value,
|
||||
cwd_value,
|
||||
duration_value,
|
||||
exit_status_value,
|
||||
],
|
||||
span: head,
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"start_timestamp" => start_timestamp_value,
|
||||
"command" => command_value,
|
||||
"cwd" => cwd_value,
|
||||
"duration" => duration_value,
|
||||
"exit_status" => exit_status_value,
|
||||
},
|
||||
head,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
||||
};
|
||||
use reedline::get_reedline_default_keybindings;
|
||||
|
||||
@ -41,36 +41,15 @@ impl Command for KeybindingsDefault {
|
||||
let records = get_reedline_default_keybindings()
|
||||
.into_iter()
|
||||
.map(|(mode, modifier, code, event)| {
|
||||
let mode = Value::String {
|
||||
val: mode,
|
||||
span: call.head,
|
||||
};
|
||||
|
||||
let modifier = Value::String {
|
||||
val: modifier,
|
||||
span: call.head,
|
||||
};
|
||||
|
||||
let code = Value::String {
|
||||
val: code,
|
||||
span: call.head,
|
||||
};
|
||||
|
||||
let event = Value::String {
|
||||
val: event,
|
||||
span: call.head,
|
||||
};
|
||||
|
||||
Value::Record {
|
||||
cols: vec![
|
||||
"mode".to_string(),
|
||||
"modifier".to_string(),
|
||||
"code".to_string(),
|
||||
"event".to_string(),
|
||||
],
|
||||
vals: vec![mode, modifier, code, event],
|
||||
span: call.head,
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"mode" => Value::string(mode, call.head),
|
||||
"modifier" => Value::string(modifier, call.head),
|
||||
"code" => Value::string(code, call.head),
|
||||
"event" => Value::string(event, call.head),
|
||||
},
|
||||
call.head,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type,
|
||||
Value,
|
||||
};
|
||||
use reedline::{
|
||||
get_reedline_edit_commands, get_reedline_keybinding_modifiers, get_reedline_keycodes,
|
||||
@ -96,15 +97,13 @@ fn get_records(entry_type: &str, span: Span) -> Vec<Value> {
|
||||
}
|
||||
|
||||
fn convert_to_record(edit: &str, entry_type: &str, span: Span) -> Value {
|
||||
let entry_type = Value::string(entry_type, span);
|
||||
|
||||
let name = Value::string(edit, span);
|
||||
|
||||
Value::Record {
|
||||
cols: vec!["type".to_string(), "name".to_string()],
|
||||
vals: vec![entry_type, name],
|
||||
Value::record(
|
||||
record! {
|
||||
"type" => Value::string(entry_type, span),
|
||||
"name" => Value::string(edit, span),
|
||||
},
|
||||
span,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Helper to sort a vec and return a vec
|
||||
|
@ -3,7 +3,8 @@ use crossterm::{event::Event, event::KeyCode, event::KeyEvent, terminal};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type,
|
||||
Value,
|
||||
};
|
||||
use std::io::{stdout, Write};
|
||||
|
||||
@ -78,9 +79,8 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
||||
let v = print_events_helper(event)?;
|
||||
// Print out the record
|
||||
let o = match v {
|
||||
Value::Record { cols, vals, .. } => cols
|
||||
Value::Record { val, .. } => val
|
||||
.iter()
|
||||
.zip(vals.iter())
|
||||
.map(|(x, y)| format!("{}: {}", x, y.into_string("", config)))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
@ -111,54 +111,29 @@ fn print_events_helper(event: Event) -> Result<Value, ShellError> {
|
||||
{
|
||||
match code {
|
||||
KeyCode::Char(c) => {
|
||||
let record = Value::Record {
|
||||
cols: vec![
|
||||
"char".into(),
|
||||
"code".into(),
|
||||
"modifier".into(),
|
||||
"flags".into(),
|
||||
"kind".into(),
|
||||
"state".into(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(format!("{c}"), Span::unknown()),
|
||||
Value::string(format!("{:#08x}", u32::from(c)), Span::unknown()),
|
||||
Value::string(format!("{modifiers:?}"), Span::unknown()),
|
||||
Value::string(format!("{modifiers:#08b}"), Span::unknown()),
|
||||
Value::string(format!("{kind:?}"), Span::unknown()),
|
||||
Value::string(format!("{state:?}"), Span::unknown()),
|
||||
],
|
||||
span: Span::unknown(),
|
||||
let record = record! {
|
||||
"char" => Value::string(format!("{c}"), Span::unknown()),
|
||||
"code" => Value::string(format!("{:#08x}", u32::from(c)), Span::unknown()),
|
||||
"modifier" => Value::string(format!("{modifiers:?}"), Span::unknown()),
|
||||
"flags" => Value::string(format!("{modifiers:#08b}"), Span::unknown()),
|
||||
"kind" => Value::string(format!("{kind:?}"), Span::unknown()),
|
||||
"state" => Value::string(format!("{state:?}"), Span::unknown()),
|
||||
};
|
||||
Ok(record)
|
||||
Ok(Value::record(record, Span::unknown()))
|
||||
}
|
||||
_ => {
|
||||
let record = Value::Record {
|
||||
cols: vec![
|
||||
"code".into(),
|
||||
"modifier".into(),
|
||||
"flags".into(),
|
||||
"kind".into(),
|
||||
"state".into(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::string(format!("{code:?}"), Span::unknown()),
|
||||
Value::string(format!("{modifiers:?}"), Span::unknown()),
|
||||
Value::string(format!("{modifiers:#08b}"), Span::unknown()),
|
||||
Value::string(format!("{kind:?}"), Span::unknown()),
|
||||
Value::string(format!("{state:?}"), Span::unknown()),
|
||||
],
|
||||
span: Span::unknown(),
|
||||
let record = record! {
|
||||
"code" => Value::string(format!("{code:?}"), Span::unknown()),
|
||||
"modifier" => Value::string(format!("{modifiers:?}"), Span::unknown()),
|
||||
"flags" => Value::string(format!("{modifiers:#08b}"), Span::unknown()),
|
||||
"kind" => Value::string(format!("{kind:?}"), Span::unknown()),
|
||||
"state" => Value::string(format!("{state:?}"), Span::unknown()),
|
||||
};
|
||||
Ok(record)
|
||||
Ok(Value::record(record, Span::unknown()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let record = Value::Record {
|
||||
cols: vec!["event".into()],
|
||||
vals: vec![Value::string(format!("{event:?}"), Span::unknown())],
|
||||
span: Span::unknown(),
|
||||
};
|
||||
Ok(record)
|
||||
let record = record! { "event" => Value::string(format!("{event:?}"), Span::unknown()) };
|
||||
Ok(Value::record(record, Span::unknown()))
|
||||
}
|
||||
}
|
||||
|
@ -454,7 +454,7 @@ pub fn map_value_completions<'a>(
|
||||
}
|
||||
|
||||
// Match for record values
|
||||
if let Ok((cols, vals)) = x.as_record() {
|
||||
if let Ok(record) = x.as_record() {
|
||||
let mut suggestion = Suggestion {
|
||||
value: String::from(""), // Initialize with empty string
|
||||
description: None,
|
||||
@ -467,7 +467,7 @@ pub fn map_value_completions<'a>(
|
||||
};
|
||||
|
||||
// Iterate the cols looking for `value` and `description`
|
||||
cols.iter().zip(vals).for_each(|it| {
|
||||
record.iter().for_each(|it| {
|
||||
// Match `value` column
|
||||
if it.0 == "value" {
|
||||
// Convert the value to string
|
||||
|
@ -235,13 +235,9 @@ fn nested_suggestions(
|
||||
let value = recursive_value(val, sublevels);
|
||||
|
||||
match value {
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: _,
|
||||
span: _,
|
||||
} => {
|
||||
Value::Record { val, .. } => {
|
||||
// Add all the columns as completion
|
||||
for item in cols {
|
||||
for item in val.cols {
|
||||
output.push(Suggestion {
|
||||
value: item,
|
||||
description: None,
|
||||
@ -289,12 +285,8 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
|
||||
// Go to next sublevel
|
||||
if let Some(next_sublevel) = sublevels.clone().into_iter().next() {
|
||||
match val {
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: _,
|
||||
} => {
|
||||
for item in cols.into_iter().zip(vals) {
|
||||
Value::Record { val, .. } => {
|
||||
for item in val {
|
||||
// Check if index matches with sublevel
|
||||
if item.0.as_bytes().to_vec() == next_sublevel {
|
||||
// If matches try to fetch recursively the next
|
||||
|
@ -7,7 +7,8 @@ use nu_parser::parse;
|
||||
use nu_protocol::{
|
||||
create_menus,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
extract_value, Config, ParsedKeybinding, ParsedMenu, PipelineData, ShellError, Span, Value,
|
||||
extract_value, Config, ParsedKeybinding, ParsedMenu, PipelineData, Record, ShellError, Span,
|
||||
Value,
|
||||
};
|
||||
use reedline::{
|
||||
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||
@ -130,8 +131,8 @@ fn add_menu(
|
||||
stack: &Stack,
|
||||
config: &Config,
|
||||
) -> Result<Reedline, ShellError> {
|
||||
if let Value::Record { cols, vals, span } = &menu.menu_type {
|
||||
let layout = extract_value("layout", cols, vals, *span)?.into_string("", config);
|
||||
if let Value::Record { val, span } = &menu.menu_type {
|
||||
let layout = extract_value("layout", val, *span)?.into_string("", config);
|
||||
|
||||
match layout.as_str() {
|
||||
"columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, config),
|
||||
@ -154,8 +155,8 @@ fn add_menu(
|
||||
|
||||
macro_rules! add_style {
|
||||
// first arm match add!(1,2), add!(2,3) etc
|
||||
($name:expr, $cols: expr, $vals:expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
|
||||
$menu = match extract_value($name, $cols, $vals, *$span) {
|
||||
($name:expr, $record: expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
|
||||
$menu = match extract_value($name, $record, *$span) {
|
||||
Ok(text) => {
|
||||
let style = match text {
|
||||
Value::String { val, .. } => lookup_ansi_color_style(&val),
|
||||
@ -180,8 +181,8 @@ pub(crate) fn add_columnar_menu(
|
||||
let name = menu.name.into_string("", config);
|
||||
let mut columnar_menu = ColumnarMenu::default().with_name(&name);
|
||||
|
||||
if let Value::Record { cols, vals, span } = &menu.menu_type {
|
||||
columnar_menu = match extract_value("columns", cols, vals, *span) {
|
||||
if let Value::Record { val, span } = &menu.menu_type {
|
||||
columnar_menu = match extract_value("columns", val, *span) {
|
||||
Ok(columns) => {
|
||||
let columns = columns.as_int()?;
|
||||
columnar_menu.with_columns(columns as u16)
|
||||
@ -189,7 +190,7 @@ pub(crate) fn add_columnar_menu(
|
||||
Err(_) => columnar_menu,
|
||||
};
|
||||
|
||||
columnar_menu = match extract_value("col_width", cols, vals, *span) {
|
||||
columnar_menu = match extract_value("col_width", val, *span) {
|
||||
Ok(col_width) => {
|
||||
let col_width = col_width.as_int()?;
|
||||
columnar_menu.with_column_width(Some(col_width as usize))
|
||||
@ -197,7 +198,7 @@ pub(crate) fn add_columnar_menu(
|
||||
Err(_) => columnar_menu.with_column_width(None),
|
||||
};
|
||||
|
||||
columnar_menu = match extract_value("col_padding", cols, vals, *span) {
|
||||
columnar_menu = match extract_value("col_padding", val, *span) {
|
||||
Ok(col_padding) => {
|
||||
let col_padding = col_padding.as_int()?;
|
||||
columnar_menu.with_column_padding(col_padding as usize)
|
||||
@ -206,11 +207,10 @@ pub(crate) fn add_columnar_menu(
|
||||
};
|
||||
}
|
||||
|
||||
if let Value::Record { cols, vals, span } = &menu.style {
|
||||
if let Value::Record { val, span } = &menu.style {
|
||||
add_style!(
|
||||
"text",
|
||||
cols,
|
||||
vals,
|
||||
val,
|
||||
span,
|
||||
config,
|
||||
columnar_menu,
|
||||
@ -218,8 +218,7 @@ pub(crate) fn add_columnar_menu(
|
||||
);
|
||||
add_style!(
|
||||
"selected_text",
|
||||
cols,
|
||||
vals,
|
||||
val,
|
||||
span,
|
||||
config,
|
||||
columnar_menu,
|
||||
@ -227,8 +226,7 @@ pub(crate) fn add_columnar_menu(
|
||||
);
|
||||
add_style!(
|
||||
"description_text",
|
||||
cols,
|
||||
vals,
|
||||
val,
|
||||
span,
|
||||
config,
|
||||
columnar_menu,
|
||||
@ -282,8 +280,8 @@ pub(crate) fn add_list_menu(
|
||||
let name = menu.name.into_string("", config);
|
||||
let mut list_menu = ListMenu::default().with_name(&name);
|
||||
|
||||
if let Value::Record { cols, vals, span } = &menu.menu_type {
|
||||
list_menu = match extract_value("page_size", cols, vals, *span) {
|
||||
if let Value::Record { val, span } = &menu.menu_type {
|
||||
list_menu = match extract_value("page_size", val, *span) {
|
||||
Ok(page_size) => {
|
||||
let page_size = page_size.as_int()?;
|
||||
list_menu.with_page_size(page_size as usize)
|
||||
@ -292,11 +290,10 @@ pub(crate) fn add_list_menu(
|
||||
};
|
||||
}
|
||||
|
||||
if let Value::Record { cols, vals, span } = &menu.style {
|
||||
if let Value::Record { val, span } = &menu.style {
|
||||
add_style!(
|
||||
"text",
|
||||
cols,
|
||||
vals,
|
||||
val,
|
||||
span,
|
||||
config,
|
||||
list_menu,
|
||||
@ -304,8 +301,7 @@ pub(crate) fn add_list_menu(
|
||||
);
|
||||
add_style!(
|
||||
"selected_text",
|
||||
cols,
|
||||
vals,
|
||||
val,
|
||||
span,
|
||||
config,
|
||||
list_menu,
|
||||
@ -313,8 +309,7 @@ pub(crate) fn add_list_menu(
|
||||
);
|
||||
add_style!(
|
||||
"description_text",
|
||||
cols,
|
||||
vals,
|
||||
val,
|
||||
span,
|
||||
config,
|
||||
list_menu,
|
||||
@ -368,8 +363,8 @@ pub(crate) fn add_description_menu(
|
||||
let name = menu.name.into_string("", config);
|
||||
let mut description_menu = DescriptionMenu::default().with_name(&name);
|
||||
|
||||
if let Value::Record { cols, vals, span } = &menu.menu_type {
|
||||
description_menu = match extract_value("columns", cols, vals, *span) {
|
||||
if let Value::Record { val, span } = &menu.menu_type {
|
||||
description_menu = match extract_value("columns", val, *span) {
|
||||
Ok(columns) => {
|
||||
let columns = columns.as_int()?;
|
||||
description_menu.with_columns(columns as u16)
|
||||
@ -377,7 +372,7 @@ pub(crate) fn add_description_menu(
|
||||
Err(_) => description_menu,
|
||||
};
|
||||
|
||||
description_menu = match extract_value("col_width", cols, vals, *span) {
|
||||
description_menu = match extract_value("col_width", val, *span) {
|
||||
Ok(col_width) => {
|
||||
let col_width = col_width.as_int()?;
|
||||
description_menu.with_column_width(Some(col_width as usize))
|
||||
@ -385,7 +380,7 @@ pub(crate) fn add_description_menu(
|
||||
Err(_) => description_menu.with_column_width(None),
|
||||
};
|
||||
|
||||
description_menu = match extract_value("col_padding", cols, vals, *span) {
|
||||
description_menu = match extract_value("col_padding", val, *span) {
|
||||
Ok(col_padding) => {
|
||||
let col_padding = col_padding.as_int()?;
|
||||
description_menu.with_column_padding(col_padding as usize)
|
||||
@ -393,7 +388,7 @@ pub(crate) fn add_description_menu(
|
||||
Err(_) => description_menu,
|
||||
};
|
||||
|
||||
description_menu = match extract_value("selection_rows", cols, vals, *span) {
|
||||
description_menu = match extract_value("selection_rows", val, *span) {
|
||||
Ok(selection_rows) => {
|
||||
let selection_rows = selection_rows.as_int()?;
|
||||
description_menu.with_selection_rows(selection_rows as u16)
|
||||
@ -401,7 +396,7 @@ pub(crate) fn add_description_menu(
|
||||
Err(_) => description_menu,
|
||||
};
|
||||
|
||||
description_menu = match extract_value("description_rows", cols, vals, *span) {
|
||||
description_menu = match extract_value("description_rows", val, *span) {
|
||||
Ok(description_rows) => {
|
||||
let description_rows = description_rows.as_int()?;
|
||||
description_menu.with_description_rows(description_rows as usize)
|
||||
@ -410,11 +405,10 @@ pub(crate) fn add_description_menu(
|
||||
};
|
||||
}
|
||||
|
||||
if let Value::Record { cols, vals, span } = &menu.style {
|
||||
if let Value::Record { val, span } = &menu.style {
|
||||
add_style!(
|
||||
"text",
|
||||
cols,
|
||||
vals,
|
||||
val,
|
||||
span,
|
||||
config,
|
||||
description_menu,
|
||||
@ -422,8 +416,7 @@ pub(crate) fn add_description_menu(
|
||||
);
|
||||
add_style!(
|
||||
"selected_text",
|
||||
cols,
|
||||
vals,
|
||||
val,
|
||||
span,
|
||||
config,
|
||||
description_menu,
|
||||
@ -431,8 +424,7 @@ pub(crate) fn add_description_menu(
|
||||
);
|
||||
add_style!(
|
||||
"description_text",
|
||||
cols,
|
||||
vals,
|
||||
val,
|
||||
span,
|
||||
config,
|
||||
description_menu,
|
||||
@ -722,27 +714,21 @@ enum EventType<'config> {
|
||||
}
|
||||
|
||||
impl<'config> EventType<'config> {
|
||||
fn try_from_columns(
|
||||
cols: &'config [String],
|
||||
vals: &'config [Value],
|
||||
span: Span,
|
||||
) -> Result<Self, ShellError> {
|
||||
extract_value("send", cols, vals, span)
|
||||
fn try_from_record(record: &'config Record, span: Span) -> Result<Self, ShellError> {
|
||||
extract_value("send", record, span)
|
||||
.map(Self::Send)
|
||||
.or_else(|_| extract_value("edit", cols, vals, span).map(Self::Edit))
|
||||
.or_else(|_| extract_value("until", cols, vals, span).map(Self::Until))
|
||||
.or_else(|_| extract_value("edit", record, span).map(Self::Edit))
|
||||
.or_else(|_| extract_value("until", record, span).map(Self::Until))
|
||||
.map_err(|_| ShellError::MissingConfigValue("send, edit or until".to_string(), span))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_event(value: &Value, config: &Config) -> Result<Option<ReedlineEvent>, ShellError> {
|
||||
match value {
|
||||
Value::Record { cols, vals, span } => {
|
||||
match EventType::try_from_columns(cols, vals, *span)? {
|
||||
Value::Record { val: record, span } => match EventType::try_from_record(record, *span)? {
|
||||
EventType::Send(value) => event_from_record(
|
||||
value.into_string("", config).to_lowercase().as_str(),
|
||||
cols,
|
||||
vals,
|
||||
record,
|
||||
config,
|
||||
*span,
|
||||
)
|
||||
@ -750,8 +736,7 @@ fn parse_event(value: &Value, config: &Config) -> Result<Option<ReedlineEvent>,
|
||||
EventType::Edit(value) => {
|
||||
let edit = edit_from_record(
|
||||
value.into_string("", config).to_lowercase().as_str(),
|
||||
cols,
|
||||
vals,
|
||||
record,
|
||||
config,
|
||||
*span,
|
||||
)?;
|
||||
@ -782,8 +767,7 @@ fn parse_event(value: &Value, config: &Config) -> Result<Option<ReedlineEvent>,
|
||||
v.span()?,
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
Value::List { vals, .. } => {
|
||||
let events = vals
|
||||
.iter()
|
||||
@ -813,8 +797,7 @@ fn parse_event(value: &Value, config: &Config) -> Result<Option<ReedlineEvent>,
|
||||
|
||||
fn event_from_record(
|
||||
name: &str,
|
||||
cols: &[String],
|
||||
vals: &[Value],
|
||||
record: &Record,
|
||||
config: &Config,
|
||||
span: Span,
|
||||
) -> Result<ReedlineEvent, ShellError> {
|
||||
@ -848,11 +831,11 @@ fn event_from_record(
|
||||
"menupageprevious" => ReedlineEvent::MenuPagePrevious,
|
||||
"openeditor" => ReedlineEvent::OpenEditor,
|
||||
"menu" => {
|
||||
let menu = extract_value("name", cols, vals, span)?;
|
||||
let menu = extract_value("name", record, span)?;
|
||||
ReedlineEvent::Menu(menu.into_string("", config))
|
||||
}
|
||||
"executehostcommand" => {
|
||||
let cmd = extract_value("cmd", cols, vals, span)?;
|
||||
let cmd = extract_value("cmd", record, span)?;
|
||||
ReedlineEvent::ExecuteHostCommand(cmd.into_string("", config))
|
||||
}
|
||||
v => {
|
||||
@ -869,8 +852,7 @@ fn event_from_record(
|
||||
|
||||
fn edit_from_record(
|
||||
name: &str,
|
||||
cols: &[String],
|
||||
vals: &[Value],
|
||||
record: &Record,
|
||||
config: &Config,
|
||||
span: Span,
|
||||
) -> Result<EditCommand, ShellError> {
|
||||
@ -889,16 +871,16 @@ fn edit_from_record(
|
||||
"movewordrightstart" => EditCommand::MoveWordRightStart,
|
||||
"movebigwordrightstart" => EditCommand::MoveBigWordRightStart,
|
||||
"movetoposition" => {
|
||||
let value = extract_value("value", cols, vals, span)?;
|
||||
let value = extract_value("value", record, span)?;
|
||||
EditCommand::MoveToPosition(value.as_int()? as usize)
|
||||
}
|
||||
"insertchar" => {
|
||||
let value = extract_value("value", cols, vals, span)?;
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value, config)?;
|
||||
EditCommand::InsertChar(char)
|
||||
}
|
||||
"insertstring" => {
|
||||
let value = extract_value("value", cols, vals, span)?;
|
||||
let value = extract_value("value", record, span)?;
|
||||
EditCommand::InsertString(value.into_string("", config))
|
||||
}
|
||||
"insertnewline" => EditCommand::InsertNewline,
|
||||
@ -930,42 +912,42 @@ fn edit_from_record(
|
||||
"undo" => EditCommand::Undo,
|
||||
"redo" => EditCommand::Redo,
|
||||
"cutrightuntil" => {
|
||||
let value = extract_value("value", cols, vals, span)?;
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value, config)?;
|
||||
EditCommand::CutRightUntil(char)
|
||||
}
|
||||
"cutrightbefore" => {
|
||||
let value = extract_value("value", cols, vals, span)?;
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value, config)?;
|
||||
EditCommand::CutRightBefore(char)
|
||||
}
|
||||
"moverightuntil" => {
|
||||
let value = extract_value("value", cols, vals, span)?;
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value, config)?;
|
||||
EditCommand::MoveRightUntil(char)
|
||||
}
|
||||
"moverightbefore" => {
|
||||
let value = extract_value("value", cols, vals, span)?;
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value, config)?;
|
||||
EditCommand::MoveRightBefore(char)
|
||||
}
|
||||
"cutleftuntil" => {
|
||||
let value = extract_value("value", cols, vals, span)?;
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value, config)?;
|
||||
EditCommand::CutLeftUntil(char)
|
||||
}
|
||||
"cutleftbefore" => {
|
||||
let value = extract_value("value", cols, vals, span)?;
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value, config)?;
|
||||
EditCommand::CutLeftBefore(char)
|
||||
}
|
||||
"moveleftuntil" => {
|
||||
let value = extract_value("value", cols, vals, span)?;
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value, config)?;
|
||||
EditCommand::MoveLeftUntil(char)
|
||||
}
|
||||
"moveleftbefore" => {
|
||||
let value = extract_value("value", cols, vals, span)?;
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value, config)?;
|
||||
EditCommand::MoveLeftBefore(char)
|
||||
}
|
||||
@ -999,16 +981,13 @@ mod test {
|
||||
fn test_send_event() {
|
||||
let cols = vec!["send".to_string()];
|
||||
let vals = vec![Value::test_string("Enter")];
|
||||
let event = Record { vals, cols };
|
||||
|
||||
let span = Span::test_data();
|
||||
let b = EventType::try_from_columns(&cols, &vals, span).unwrap();
|
||||
let b = EventType::try_from_record(&event, span).unwrap();
|
||||
assert!(matches!(b, EventType::Send(_)));
|
||||
|
||||
let event = Value::Record {
|
||||
vals,
|
||||
cols,
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let event = Value::test_record(event);
|
||||
let config = Config::default();
|
||||
|
||||
let parsed_event = parse_event(&event, &config).unwrap();
|
||||
@ -1019,16 +998,13 @@ mod test {
|
||||
fn test_edit_event() {
|
||||
let cols = vec!["edit".to_string()];
|
||||
let vals = vec![Value::test_string("Clear")];
|
||||
let event = Record { vals, cols };
|
||||
|
||||
let span = Span::test_data();
|
||||
let b = EventType::try_from_columns(&cols, &vals, span).unwrap();
|
||||
let b = EventType::try_from_record(&event, span).unwrap();
|
||||
assert!(matches!(b, EventType::Edit(_)));
|
||||
|
||||
let event = Value::Record {
|
||||
vals,
|
||||
cols,
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let event = Value::test_record(event);
|
||||
let config = Config::default();
|
||||
|
||||
let parsed_event = parse_event(&event, &config).unwrap();
|
||||
@ -1045,16 +1021,13 @@ mod test {
|
||||
Value::test_string("Menu"),
|
||||
Value::test_string("history_menu"),
|
||||
];
|
||||
let event = Record { vals, cols };
|
||||
|
||||
let span = Span::test_data();
|
||||
let b = EventType::try_from_columns(&cols, &vals, span).unwrap();
|
||||
let b = EventType::try_from_record(&event, span).unwrap();
|
||||
assert!(matches!(b, EventType::Send(_)));
|
||||
|
||||
let event = Value::Record {
|
||||
vals,
|
||||
cols,
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let event = Value::test_record(event);
|
||||
let config = Config::default();
|
||||
|
||||
let parsed_event = parse_event(&event, &config).unwrap();
|
||||
@ -1073,21 +1046,13 @@ mod test {
|
||||
Value::test_string("history_menu"),
|
||||
];
|
||||
|
||||
let menu_event = Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let menu_event = Value::test_record(Record { cols, vals });
|
||||
|
||||
// Enter event
|
||||
let cols = vec!["send".to_string()];
|
||||
let vals = vec![Value::test_string("Enter")];
|
||||
|
||||
let enter_event = Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let enter_event = Value::test_record(Record { cols, vals });
|
||||
|
||||
// Until event
|
||||
let cols = vec!["until".to_string()];
|
||||
@ -1095,16 +1060,13 @@ mod test {
|
||||
vals: vec![menu_event, enter_event],
|
||||
span: Span::test_data(),
|
||||
}];
|
||||
let event = Record { cols, vals };
|
||||
|
||||
let span = Span::test_data();
|
||||
let b = EventType::try_from_columns(&cols, &vals, span).unwrap();
|
||||
let b = EventType::try_from_record(&event, span).unwrap();
|
||||
assert!(matches!(b, EventType::Until(_)));
|
||||
|
||||
let event = Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let event = Value::test_record(event);
|
||||
let config = Config::default();
|
||||
|
||||
let parsed_event = parse_event(&event, &config).unwrap();
|
||||
@ -1126,21 +1088,13 @@ mod test {
|
||||
Value::test_string("history_menu"),
|
||||
];
|
||||
|
||||
let menu_event = Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let menu_event = Value::test_record(Record { cols, vals });
|
||||
|
||||
// Enter event
|
||||
let cols = vec!["send".to_string()];
|
||||
let vals = vec![Value::test_string("Enter")];
|
||||
|
||||
let enter_event = Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let enter_event = Value::test_record(Record { cols, vals });
|
||||
|
||||
// Multiple event
|
||||
let event = Value::List {
|
||||
@ -1163,9 +1117,10 @@ mod test {
|
||||
fn test_error() {
|
||||
let cols = vec!["not_exist".to_string()];
|
||||
let vals = vec![Value::test_string("Enter")];
|
||||
let event = Record { cols, vals };
|
||||
|
||||
let span = Span::test_data();
|
||||
let b = EventType::try_from_columns(&cols, &vals, span);
|
||||
let b = EventType::try_from_record(&event, span);
|
||||
assert!(matches!(b, Err(ShellError::MissingConfigValue(_, _))));
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ pub fn merge_descriptors(values: &[Value]) -> Vec<String> {
|
||||
let mut seen: IndexSet<String> = indexset! {};
|
||||
for value in values {
|
||||
let data_descriptors = match value {
|
||||
Value::Record { cols, .. } => cols.to_owned(),
|
||||
Value::Record { val, .. } => val.cols.clone(),
|
||||
_ => vec!["".to_string()],
|
||||
};
|
||||
for desc in data_descriptors {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Value,
|
||||
};
|
||||
|
||||
use crate::dataframe::values::NuDataFrame;
|
||||
@ -55,27 +55,14 @@ impl Command for ListDF {
|
||||
NuDataFrame::try_from_value(value).ok().map(|df| (name, df))
|
||||
})
|
||||
.map(|(name, df)| {
|
||||
let name = Value::String {
|
||||
val: name,
|
||||
span: call.head,
|
||||
};
|
||||
|
||||
let columns = Value::int(df.as_ref().width() as i64, call.head);
|
||||
|
||||
let rows = Value::int(df.as_ref().height() as i64, call.head);
|
||||
|
||||
let cols = vec![
|
||||
"name".to_string(),
|
||||
"columns".to_string(),
|
||||
"rows".to_string(),
|
||||
];
|
||||
let vals = vec![name, columns, rows];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: call.head,
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"name" => Value::string(name, call.head),
|
||||
"columns" => Value::int(df.as_ref().width() as i64, call.head),
|
||||
"rows" => Value::int(df.as_ref().height() as i64, call.head),
|
||||
},
|
||||
call.head,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Value>>();
|
||||
|
||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use crate::dataframe::values::NuExpression;
|
||||
@ -40,21 +40,18 @@ impl Command for ToNu {
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
let cols = vec!["index".into(), "a".into(), "b".into()];
|
||||
let rec_1 = Value::Record {
|
||||
let rec_1 = Value::test_record(Record {
|
||||
cols: cols.clone(),
|
||||
vals: vec![Value::test_int(0), Value::test_int(1), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let rec_2 = Value::Record {
|
||||
});
|
||||
let rec_2 = Value::test_record(Record {
|
||||
cols: cols.clone(),
|
||||
vals: vec![Value::test_int(1), Value::test_int(3), Value::test_int(4)],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let rec_3 = Value::Record {
|
||||
});
|
||||
let rec_3 = Value::test_record(Record {
|
||||
cols,
|
||||
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(4)],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
});
|
||||
|
||||
vec![
|
||||
Example {
|
||||
@ -76,11 +73,10 @@ impl Command for ToNu {
|
||||
Example {
|
||||
description: "Convert a col expression into a nushell value",
|
||||
example: "dfr col a | dfr into-nu",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["expr".into(), "value".into()],
|
||||
vals: vec![Value::test_string("column"), Value::test_string("a")],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -41,20 +41,18 @@ impl Command for ExprAlias {
|
||||
let cols = vec!["expr".into(), "value".into()];
|
||||
let expr = Value::test_string("column");
|
||||
let value = Value::test_string("a");
|
||||
let expr = Value::Record {
|
||||
let expr = Value::test_record(Record {
|
||||
cols,
|
||||
vals: vec![expr, value],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
});
|
||||
|
||||
let cols = vec!["expr".into(), "alias".into()];
|
||||
let value = Value::test_string("new_a");
|
||||
|
||||
let record = Value::Record {
|
||||
let record = Value::test_record(Record {
|
||||
cols,
|
||||
vals: vec![expr, value],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
});
|
||||
|
||||
Some(record)
|
||||
},
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
use polars::prelude::col;
|
||||
|
||||
@ -34,11 +34,10 @@ impl Command for ExprCol {
|
||||
vec![Example {
|
||||
description: "Creates a named column expression and converts it to a nu object",
|
||||
example: "dfr col a | dfr into-nu",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["expr".into(), "value".into()],
|
||||
vals: vec![Value::test_string("column"), Value::test_string("a")],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -33,11 +33,10 @@ impl Command for ExprLit {
|
||||
vec![Example {
|
||||
description: "Created a literal expression and converts it to a nu object",
|
||||
example: "dfr lit 2 | dfr into-nu",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["expr".into(), "value".into()],
|
||||
vals: vec![Value::test_string("literal"), Value::test_string("2")],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ use super::{DataFrameValue, NuDataFrame};
|
||||
|
||||
use chrono::{DateTime, FixedOffset, NaiveDateTime};
|
||||
use indexmap::map::{Entry, IndexMap};
|
||||
use nu_protocol::{ShellError, Span, Value};
|
||||
use nu_protocol::{Record, ShellError, Span, Value};
|
||||
use polars::chunked_array::builder::AnonymousOwnedListBuilder;
|
||||
use polars::chunked_array::object::builder::ObjectChunkedBuilder;
|
||||
use polars::chunked_array::ChunkedArray;
|
||||
@ -128,36 +128,21 @@ pub fn create_column(
|
||||
// Adds a separator to the vector of values using the column names from the
|
||||
// dataframe to create the Values Row
|
||||
pub fn add_separator(values: &mut Vec<Value>, df: &DataFrame, span: Span) {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
|
||||
cols.push("index".to_string());
|
||||
vals.push(Value::String {
|
||||
val: "...".into(),
|
||||
span,
|
||||
});
|
||||
record.push("index", Value::string("...", span));
|
||||
|
||||
for name in df.get_column_names() {
|
||||
cols.push(name.to_string());
|
||||
vals.push(Value::String {
|
||||
val: "...".into(),
|
||||
span,
|
||||
})
|
||||
record.push(name, Value::string("...", span))
|
||||
}
|
||||
|
||||
let extra_record = Value::Record { cols, vals, span };
|
||||
|
||||
values.push(extra_record);
|
||||
values.push(Value::record(record, span));
|
||||
}
|
||||
|
||||
// Inserting the values found in a Value::List
|
||||
pub fn insert_record(
|
||||
column_values: &mut ColumnMap,
|
||||
cols: &[String],
|
||||
values: &[Value],
|
||||
) -> Result<(), ShellError> {
|
||||
for (col, value) in cols.iter().zip(values.iter()) {
|
||||
insert_value(value.clone(), col.clone(), column_values)?;
|
||||
// Inserting the values found in a Value::List or Value::Record
|
||||
pub fn insert_record(column_values: &mut ColumnMap, record: Record) -> Result<(), ShellError> {
|
||||
for (col, value) in record {
|
||||
insert_value(value, col, column_values)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -7,7 +7,7 @@ pub use conversion::{Column, ColumnMap};
|
||||
pub use operations::Axis;
|
||||
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_protocol::{did_you_mean, PipelineData, ShellError, Span, Value};
|
||||
use nu_protocol::{did_you_mean, PipelineData, Record, ShellError, Span, Value};
|
||||
use polars::prelude::{DataFrame, DataType, IntoLazy, LazyFrame, PolarsObject, Series};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{cmp::Ordering, fmt::Display, hash::Hasher};
|
||||
@ -162,10 +162,10 @@ impl NuDataFrame {
|
||||
.map(|i| format!("{i}"))
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
conversion::insert_record(&mut column_values, &cols, &vals)?
|
||||
conversion::insert_record(&mut column_values, Record { cols, vals })?
|
||||
}
|
||||
Value::Record { cols, vals, .. } => {
|
||||
conversion::insert_record(&mut column_values, &cols, &vals)?
|
||||
Value::Record { val: record, .. } => {
|
||||
conversion::insert_record(&mut column_values, record)?
|
||||
}
|
||||
_ => {
|
||||
let key = "0".to_string();
|
||||
@ -427,25 +427,15 @@ impl NuDataFrame {
|
||||
|
||||
let values = (0..size)
|
||||
.map(|i| {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
|
||||
cols.push("index".into());
|
||||
vals.push(Value::Int {
|
||||
val: (i + from_row) as i64,
|
||||
span,
|
||||
});
|
||||
record.push("index", Value::int((i + from_row) as i64, span));
|
||||
|
||||
for (name, col) in &mut iterators {
|
||||
cols.push(name.clone());
|
||||
|
||||
match col.next() {
|
||||
Some(v) => vals.push(v),
|
||||
None => vals.push(Value::Nothing { span }),
|
||||
};
|
||||
record.push(name.clone(), col.next().unwrap_or(Value::nothing(span)));
|
||||
}
|
||||
|
||||
Value::Record { cols, vals, span }
|
||||
Value::record(record, span)
|
||||
})
|
||||
.collect::<Vec<Value>>();
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
mod custom_value;
|
||||
|
||||
use nu_protocol::{PipelineData, ShellError, Span, Value};
|
||||
use nu_protocol::{record, PipelineData, ShellError, Span, Value};
|
||||
use polars::prelude::{col, AggExpr, Expr, Literal};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
@ -165,103 +165,59 @@ impl ExtractedExpr {
|
||||
}
|
||||
|
||||
pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
let cols = vec!["expr".to_string(), "value".to_string()];
|
||||
|
||||
match expr {
|
||||
Expr::Alias(expr, alias) => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let alias = Value::String {
|
||||
val: alias.as_ref().into(),
|
||||
Expr::Alias(expr, alias) => Value::record(
|
||||
record! {
|
||||
"expr" => expr_to_value(expr.as_ref(), span),
|
||||
"alias" => Value::string(alias.as_ref(), span),
|
||||
},
|
||||
span,
|
||||
};
|
||||
),
|
||||
|
||||
let cols = vec!["expr".into(), "alias".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr, alias],
|
||||
Expr::Column(name) => Value::record(
|
||||
record! {
|
||||
"expr" => Value::string("column", span),
|
||||
"value" => Value::string(name.to_string(), span),
|
||||
},
|
||||
span,
|
||||
}
|
||||
}
|
||||
Expr::Column(name) => {
|
||||
let expr_type = Value::String {
|
||||
val: "column".to_string(),
|
||||
span,
|
||||
};
|
||||
let value = Value::String {
|
||||
val: name.to_string(),
|
||||
span,
|
||||
};
|
||||
|
||||
let vals = vec![expr_type, value];
|
||||
Value::Record { cols, vals, span }
|
||||
}
|
||||
),
|
||||
Expr::Columns(columns) => {
|
||||
let expr_type = Value::String {
|
||||
val: "columns".into(),
|
||||
let value = columns.iter().map(|col| Value::string(col, span)).collect();
|
||||
Value::record(
|
||||
record! {
|
||||
"expr" => Value::string("columns", span),
|
||||
"value" => Value::list(value, span),
|
||||
},
|
||||
span,
|
||||
};
|
||||
let value = Value::List {
|
||||
vals: columns
|
||||
.iter()
|
||||
.map(|col| Value::String {
|
||||
val: col.clone(),
|
||||
span,
|
||||
})
|
||||
.collect(),
|
||||
span,
|
||||
};
|
||||
|
||||
let vals = vec![expr_type, value];
|
||||
Value::Record { cols, vals, span }
|
||||
)
|
||||
}
|
||||
Expr::Literal(literal) => {
|
||||
let expr_type = Value::String {
|
||||
val: "literal".into(),
|
||||
Expr::Literal(literal) => Value::record(
|
||||
record! {
|
||||
"expr" => Value::string("literal", span),
|
||||
"value" => Value::string(format!("{literal:?}"), span),
|
||||
},
|
||||
span,
|
||||
};
|
||||
let value = Value::String {
|
||||
val: format!("{literal:?}"),
|
||||
),
|
||||
Expr::BinaryExpr { left, op, right } => Value::record(
|
||||
record! {
|
||||
"left" => expr_to_value(left, span),
|
||||
"op" => Value::string(format!("{op:?}"), span),
|
||||
"right" => expr_to_value(right, span),
|
||||
},
|
||||
span,
|
||||
};
|
||||
|
||||
let vals = vec![expr_type, value];
|
||||
Value::Record { cols, vals, span }
|
||||
}
|
||||
Expr::BinaryExpr { left, op, right } => {
|
||||
let left_val = expr_to_value(left, span);
|
||||
let right_val = expr_to_value(right, span);
|
||||
|
||||
let operator = Value::String {
|
||||
val: format!("{op:?}"),
|
||||
span,
|
||||
};
|
||||
|
||||
let cols = vec!["left".into(), "op".into(), "right".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![left_val, operator, right_val],
|
||||
span,
|
||||
}
|
||||
}
|
||||
),
|
||||
Expr::Ternary {
|
||||
predicate,
|
||||
truthy,
|
||||
falsy,
|
||||
} => {
|
||||
let predicate = expr_to_value(predicate.as_ref(), span);
|
||||
let truthy = expr_to_value(truthy.as_ref(), span);
|
||||
let falsy = expr_to_value(falsy.as_ref(), span);
|
||||
|
||||
let cols = vec!["predicate".into(), "truthy".into(), "falsy".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![predicate, truthy, falsy],
|
||||
} => Value::record(
|
||||
record! {
|
||||
"predicate" => expr_to_value(predicate.as_ref(), span),
|
||||
"truthy" => expr_to_value(truthy.as_ref(), span),
|
||||
"falsy" => expr_to_value(falsy.as_ref(), span),
|
||||
},
|
||||
span,
|
||||
}
|
||||
}
|
||||
),
|
||||
Expr::Agg(agg_expr) => {
|
||||
let value = match agg_expr {
|
||||
AggExpr::Min { input: expr, .. }
|
||||
@ -281,88 +237,37 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
expr,
|
||||
quantile,
|
||||
interpol,
|
||||
} => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let quantile = expr_to_value(quantile.as_ref(), span);
|
||||
let interpol = Value::String {
|
||||
val: format!("{interpol:?}"),
|
||||
} => Value::record(
|
||||
record! {
|
||||
"expr" => expr_to_value(expr.as_ref(), span),
|
||||
"quantile" => expr_to_value(quantile.as_ref(), span),
|
||||
"interpol" => Value::string(format!("{interpol:?}"), span),
|
||||
},
|
||||
span,
|
||||
),
|
||||
};
|
||||
|
||||
let cols = vec!["expr".into(), "quantile".into(), "interpol".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr, quantile, interpol],
|
||||
Value::record(
|
||||
record! {
|
||||
"expr" => Value::string("agg", span),
|
||||
"value" => value,
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let expr_type = Value::String {
|
||||
val: "agg".into(),
|
||||
span,
|
||||
};
|
||||
|
||||
let vals = vec![expr_type, value];
|
||||
Value::Record { cols, vals, span }
|
||||
}
|
||||
Expr::Count => {
|
||||
let expr = Value::String {
|
||||
val: "count".into(),
|
||||
span,
|
||||
};
|
||||
let cols = vec!["expr".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr],
|
||||
span,
|
||||
}
|
||||
}
|
||||
Expr::Count => Value::record(record! { "expr" => Value::string("count", span) }, span),
|
||||
Expr::Wildcard => {
|
||||
let expr = Value::String {
|
||||
val: "wildcard".into(),
|
||||
Value::record(record! { "expr" => Value::string("wildcard", span) }, span)
|
||||
}
|
||||
Expr::Explode(expr) => Value::record(
|
||||
record! { "expr" => expr_to_value(expr.as_ref(), span) },
|
||||
span,
|
||||
};
|
||||
let cols = vec!["expr".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr],
|
||||
),
|
||||
Expr::KeepName(expr) => Value::record(
|
||||
record! { "expr" => expr_to_value(expr.as_ref(), span) },
|
||||
span,
|
||||
}
|
||||
}
|
||||
Expr::Explode(expr) => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let cols = vec!["expr".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr],
|
||||
span,
|
||||
}
|
||||
}
|
||||
Expr::KeepName(expr) => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let cols = vec!["expr".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr],
|
||||
span,
|
||||
}
|
||||
}
|
||||
Expr::Nth(i) => {
|
||||
let expr = Value::int(*i, span);
|
||||
let cols = vec!["expr".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr],
|
||||
span,
|
||||
}
|
||||
}
|
||||
),
|
||||
Expr::Nth(i) => Value::record(record! { "expr" => Value::int(*i, span) }, span),
|
||||
Expr::DtypeColumn(dtypes) => {
|
||||
let vals = dtypes
|
||||
.iter()
|
||||
@ -374,109 +279,72 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
|
||||
Value::List { vals, span }
|
||||
}
|
||||
Expr::Sort { expr, options } => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let options = Value::String {
|
||||
val: format!("{options:?}"),
|
||||
Expr::Sort { expr, options } => Value::record(
|
||||
record! {
|
||||
"expr" => expr_to_value(expr.as_ref(), span),
|
||||
"options" => Value::string(format!("{options:?}"), span),
|
||||
},
|
||||
span,
|
||||
};
|
||||
let cols = vec!["expr".into(), "options".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr, options],
|
||||
span,
|
||||
}
|
||||
}
|
||||
),
|
||||
Expr::Cast {
|
||||
expr,
|
||||
data_type,
|
||||
strict,
|
||||
} => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let dtype = Value::String {
|
||||
val: format!("{data_type:?}"),
|
||||
} => Value::record(
|
||||
record! {
|
||||
"expr" => expr_to_value(expr.as_ref(), span),
|
||||
"dtype" => Value::string(format!("{data_type:?}"), span),
|
||||
"strict" => Value::bool(*strict, span),
|
||||
},
|
||||
span,
|
||||
};
|
||||
let strict = Value::Bool { val: *strict, span };
|
||||
|
||||
let cols = vec!["expr".into(), "dtype".into(), "strict".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr, dtype, strict],
|
||||
),
|
||||
Expr::Take { expr, idx } => Value::record(
|
||||
record! {
|
||||
"expr" => expr_to_value(expr.as_ref(), span),
|
||||
"idx" => expr_to_value(idx.as_ref(), span),
|
||||
},
|
||||
span,
|
||||
}
|
||||
}
|
||||
Expr::Take { expr, idx } => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let idx = expr_to_value(idx.as_ref(), span);
|
||||
|
||||
let cols = vec!["expr".into(), "idx".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr, idx],
|
||||
span,
|
||||
}
|
||||
}
|
||||
),
|
||||
Expr::SortBy {
|
||||
expr,
|
||||
by,
|
||||
descending,
|
||||
} => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let by: Vec<Value> = by.iter().map(|b| expr_to_value(b, span)).collect();
|
||||
let by = Value::List { vals: by, span };
|
||||
|
||||
let descending: Vec<Value> = descending
|
||||
.iter()
|
||||
.map(|r| Value::Bool { val: *r, span })
|
||||
.collect();
|
||||
let descending = Value::List {
|
||||
vals: descending,
|
||||
|
||||
Value::record(
|
||||
record! {
|
||||
"expr" => expr_to_value(expr.as_ref(), span),
|
||||
"by" => Value::list(by, span),
|
||||
"descending" => Value::list(descending, span),
|
||||
},
|
||||
span,
|
||||
};
|
||||
|
||||
let cols = vec!["expr".into(), "by".into(), "descending".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr, by, descending],
|
||||
)
|
||||
}
|
||||
Expr::Filter { input, by } => Value::record(
|
||||
record! {
|
||||
"input" => expr_to_value(input.as_ref(), span),
|
||||
"by" => expr_to_value(by.as_ref(), span),
|
||||
},
|
||||
span,
|
||||
}
|
||||
}
|
||||
Expr::Filter { input, by } => {
|
||||
let input = expr_to_value(input.as_ref(), span);
|
||||
let by = expr_to_value(by.as_ref(), span);
|
||||
|
||||
let cols = vec!["input".into(), "by".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![input, by],
|
||||
span,
|
||||
}
|
||||
}
|
||||
),
|
||||
Expr::Slice {
|
||||
input,
|
||||
offset,
|
||||
length,
|
||||
} => {
|
||||
let input = expr_to_value(input.as_ref(), span);
|
||||
let offset = expr_to_value(offset.as_ref(), span);
|
||||
let length = expr_to_value(length.as_ref(), span);
|
||||
|
||||
let cols = vec!["input".into(), "offset".into(), "length".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![input, offset, length],
|
||||
} => Value::record(
|
||||
record! {
|
||||
"input" => expr_to_value(input.as_ref(), span),
|
||||
"offset" => expr_to_value(offset.as_ref(), span),
|
||||
"length" => expr_to_value(length.as_ref(), span),
|
||||
},
|
||||
span,
|
||||
}
|
||||
}
|
||||
),
|
||||
Expr::Exclude(expr, excluded) => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let excluded = excluded
|
||||
.iter()
|
||||
.map(|e| Value::String {
|
||||
@ -484,34 +352,22 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
span,
|
||||
})
|
||||
.collect();
|
||||
let excluded = Value::List {
|
||||
vals: excluded,
|
||||
span,
|
||||
};
|
||||
|
||||
let cols = vec!["expr".into(), "excluded".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr, excluded],
|
||||
Value::record(
|
||||
record! {
|
||||
"expr" => expr_to_value(expr.as_ref(), span),
|
||||
"excluded" => Value::list(excluded, span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
}
|
||||
Expr::RenameAlias { expr, function } => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let function = Value::String {
|
||||
val: format!("{function:?}"),
|
||||
Expr::RenameAlias { expr, function } => Value::record(
|
||||
record! {
|
||||
"expr" => expr_to_value(expr.as_ref(), span),
|
||||
"function" => Value::string(format!("{function:?}"), span),
|
||||
},
|
||||
span,
|
||||
};
|
||||
|
||||
let cols = vec!["expr".into(), "function".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![expr, function],
|
||||
span,
|
||||
}
|
||||
}
|
||||
),
|
||||
Expr::AnonymousFunction {
|
||||
input,
|
||||
function,
|
||||
@ -519,33 +375,15 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
options,
|
||||
} => {
|
||||
let input: Vec<Value> = input.iter().map(|e| expr_to_value(e, span)).collect();
|
||||
let input = Value::List { vals: input, span };
|
||||
|
||||
let function = Value::String {
|
||||
val: format!("{function:?}"),
|
||||
Value::record(
|
||||
record! {
|
||||
"input" => Value::list(input, span),
|
||||
"function" => Value::string(format!("{function:?}"), span),
|
||||
"output_type" => Value::string(format!("{output_type:?}"), span),
|
||||
"options" => Value::string(format!("{options:?}"), span),
|
||||
},
|
||||
span,
|
||||
};
|
||||
let output_type = Value::String {
|
||||
val: format!("{output_type:?}"),
|
||||
span,
|
||||
};
|
||||
let options = Value::String {
|
||||
val: format!("{options:?}"),
|
||||
span,
|
||||
};
|
||||
|
||||
let cols = vec![
|
||||
"input".into(),
|
||||
"function".into(),
|
||||
"output_type".into(),
|
||||
"options".into(),
|
||||
];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![input, function, output_type, options],
|
||||
span,
|
||||
}
|
||||
)
|
||||
}
|
||||
Expr::Function {
|
||||
input,
|
||||
@ -553,79 +391,47 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
options,
|
||||
} => {
|
||||
let input: Vec<Value> = input.iter().map(|e| expr_to_value(e, span)).collect();
|
||||
let input = Value::List { vals: input, span };
|
||||
|
||||
let function = Value::String {
|
||||
val: format!("{function:?}"),
|
||||
span,
|
||||
};
|
||||
let options = Value::String {
|
||||
val: format!("{options:?}"),
|
||||
span,
|
||||
};
|
||||
|
||||
let cols = vec!["input".into(), "function".into(), "options".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![input, function, options],
|
||||
Value::record(
|
||||
record! {
|
||||
"input" => Value::list(input, span),
|
||||
"function" => Value::string(format!("{function:?}"), span),
|
||||
"options" => Value::string(format!("{options:?}"), span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
}
|
||||
Expr::Cache { input, id } => {
|
||||
let input = expr_to_value(input.as_ref(), span);
|
||||
let id = Value::String {
|
||||
val: format!("{id:?}"),
|
||||
Expr::Cache { input, id } => Value::record(
|
||||
record! {
|
||||
"input" => expr_to_value(input.as_ref(), span),
|
||||
"id" => Value::string(format!("{id:?}"), span),
|
||||
},
|
||||
span,
|
||||
};
|
||||
|
||||
let cols = vec!["input".into(), "id".into()];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![input, id],
|
||||
span,
|
||||
}
|
||||
}
|
||||
),
|
||||
Expr::Window {
|
||||
function,
|
||||
partition_by,
|
||||
order_by,
|
||||
options,
|
||||
} => {
|
||||
let function = expr_to_value(function, span);
|
||||
|
||||
let partition_by: Vec<Value> = partition_by
|
||||
.iter()
|
||||
.map(|e| expr_to_value(e, span))
|
||||
.collect();
|
||||
let partition_by = Value::List {
|
||||
vals: partition_by,
|
||||
span,
|
||||
};
|
||||
|
||||
let order_by = order_by
|
||||
.as_ref()
|
||||
.map(|e| expr_to_value(e.as_ref(), span))
|
||||
.unwrap_or_else(|| Value::nothing(span));
|
||||
|
||||
let options = Value::String {
|
||||
val: format!("{options:?}"),
|
||||
span,
|
||||
};
|
||||
|
||||
let cols = vec![
|
||||
"function".into(),
|
||||
"partition_by".into(),
|
||||
"order_by".into(),
|
||||
"options".into(),
|
||||
];
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: vec![function, partition_by, order_by, options],
|
||||
Value::record(
|
||||
record! {
|
||||
"function" => expr_to_value(function, span),
|
||||
"partition_by" => Value::list(partition_by, span),
|
||||
"order_by" => order_by,
|
||||
"options" => Value::string(format!("{options:?}"), span),
|
||||
},
|
||||
span,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::NuLazyFrame;
|
||||
use nu_protocol::{CustomValue, ShellError, Span, Value};
|
||||
use nu_protocol::{record, CustomValue, ShellError, Span, Value};
|
||||
|
||||
// CustomValue implementation for NuDataFrame
|
||||
impl CustomValue for NuLazyFrame {
|
||||
@ -29,22 +29,18 @@ impl CustomValue for NuLazyFrame {
|
||||
}
|
||||
|
||||
fn to_base_value(&self, span: Span) -> Result<Value, ShellError> {
|
||||
let cols = vec!["plan".into(), "optimized_plan".into()];
|
||||
let vals = vec![
|
||||
Value::String {
|
||||
val: self.as_ref().describe_plan(),
|
||||
span,
|
||||
},
|
||||
Value::String {
|
||||
val: self
|
||||
let optimized_plan = self
|
||||
.as_ref()
|
||||
.describe_optimized_plan()
|
||||
.unwrap_or_else(|_| "<NOT AVAILABLE>".to_string()),
|
||||
span,
|
||||
},
|
||||
];
|
||||
.unwrap_or_else(|_| "<NOT AVAILABLE>".to_string());
|
||||
|
||||
Ok(Value::Record { cols, vals, span })
|
||||
Ok(Value::record(
|
||||
record! {
|
||||
"plan" => Value::string(self.as_ref().describe_plan(), span),
|
||||
"optimized_plan" => Value::string(optimized_plan, span),
|
||||
},
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::NuLazyGroupBy;
|
||||
use nu_protocol::{CustomValue, ShellError, Span, Value};
|
||||
use nu_protocol::{record, CustomValue, ShellError, Span, Value};
|
||||
|
||||
// CustomValue implementation for NuDataFrame
|
||||
impl CustomValue for NuLazyGroupBy {
|
||||
@ -29,13 +29,12 @@ impl CustomValue for NuLazyGroupBy {
|
||||
}
|
||||
|
||||
fn to_base_value(&self, span: Span) -> Result<Value, ShellError> {
|
||||
let cols = vec!["LazyGroupBy".into()];
|
||||
let vals = vec![Value::String {
|
||||
val: "apply aggregation to complete execution plan".into(),
|
||||
Ok(Value::record(
|
||||
record! {
|
||||
"LazyGroupBy" => Value::string("apply aggregation to complete execution plan", span)
|
||||
},
|
||||
span,
|
||||
}];
|
||||
|
||||
Ok(Value::Record { cols, vals, span })
|
||||
))
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
record, Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -32,7 +32,7 @@ impl Command for Fmt {
|
||||
vec![Example {
|
||||
description: "Get a record containing multiple formats for the number 42",
|
||||
example: "42 | fmt",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"binary".into(),
|
||||
"debug".into(),
|
||||
@ -53,8 +53,7 @@ impl Command for Fmt {
|
||||
Value::test_string("4.2E1"),
|
||||
Value::test_string("0x2A"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
|
||||
@ -99,71 +98,35 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
}
|
||||
|
||||
fn fmt_it(num: i64, span: Span) -> Value {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
cols.push("binary".into());
|
||||
vals.push(Value::string(format!("{num:#b}"), span));
|
||||
|
||||
cols.push("debug".into());
|
||||
vals.push(Value::string(format!("{num:#?}"), span));
|
||||
|
||||
cols.push("display".into());
|
||||
vals.push(Value::string(format!("{num}"), span));
|
||||
|
||||
cols.push("lowerexp".into());
|
||||
vals.push(Value::string(format!("{num:#e}"), span));
|
||||
|
||||
cols.push("lowerhex".into());
|
||||
vals.push(Value::string(format!("{num:#x}"), span));
|
||||
|
||||
cols.push("octal".into());
|
||||
vals.push(Value::string(format!("{num:#o}"), span));
|
||||
|
||||
// cols.push("pointer".into());
|
||||
// vals.push(Value::string(format!("{:#p}", &num), span));
|
||||
|
||||
cols.push("upperexp".into());
|
||||
vals.push(Value::string(format!("{num:#E}"), span));
|
||||
|
||||
cols.push("upperhex".into());
|
||||
vals.push(Value::string(format!("{num:#X}"), span));
|
||||
|
||||
Value::Record { cols, vals, span }
|
||||
Value::record(
|
||||
record! {
|
||||
"binary" => Value::string(format!("{num:#b}"), span),
|
||||
"debug" => Value::string(format!("{num:#?}"), span),
|
||||
"display" => Value::string(format!("{num}"), span),
|
||||
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
||||
"lowerhex" => Value::string(format!("{num:#x}"), span),
|
||||
"octal" => Value::string(format!("{num:#o}"), span),
|
||||
"upperexp" => Value::string(format!("{num:#E}"), span),
|
||||
"upperhex" => Value::string(format!("{num:#X}"), span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
fn fmt_it_64(num: f64, span: Span) -> Value {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
cols.push("binary".into());
|
||||
vals.push(Value::string(format!("{:b}", num.to_bits()), span));
|
||||
|
||||
cols.push("debug".into());
|
||||
vals.push(Value::string(format!("{num:#?}"), span));
|
||||
|
||||
cols.push("display".into());
|
||||
vals.push(Value::string(format!("{num}"), span));
|
||||
|
||||
cols.push("lowerexp".into());
|
||||
vals.push(Value::string(format!("{num:#e}"), span));
|
||||
|
||||
cols.push("lowerhex".into());
|
||||
vals.push(Value::string(format!("{:0x}", num.to_bits()), span));
|
||||
|
||||
cols.push("octal".into());
|
||||
vals.push(Value::string(format!("{:0o}", num.to_bits()), span));
|
||||
|
||||
// cols.push("pointer".into());
|
||||
// vals.push(Value::string(format!("{:#p}", &num), span));
|
||||
|
||||
cols.push("upperexp".into());
|
||||
vals.push(Value::string(format!("{num:#E}"), span));
|
||||
|
||||
cols.push("upperhex".into());
|
||||
vals.push(Value::string(format!("{:0X}", num.to_bits()), span));
|
||||
|
||||
Value::Record { cols, vals, span }
|
||||
Value::record(
|
||||
record! {
|
||||
"binary" => Value::string(format!("{:b}", num.to_bits()), span),
|
||||
"debug" => Value::string(format!("{num:#?}"), span),
|
||||
"display" => Value::string(format!("{num}"), span),
|
||||
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
||||
"lowerhex" => Value::string(format!("{:0x}", num.to_bits()), span),
|
||||
"octal" => Value::string(format!("{:0o}", num.to_bits()), span),
|
||||
"upperexp" => Value::string(format!("{num:#E}"), span),
|
||||
"upperhex" => Value::string(format!("{:0X}", num.to_bits()), span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -56,37 +56,24 @@ fn horizontal_rotate_value(
|
||||
) -> Result<Value, ShellError> {
|
||||
match value {
|
||||
Value::Record {
|
||||
mut cols,
|
||||
mut vals,
|
||||
val: mut record,
|
||||
span,
|
||||
} => {
|
||||
let rotations = by.map(|n| n % vals.len()).unwrap_or(1);
|
||||
|
||||
let columns = if cells_only {
|
||||
cols
|
||||
} else {
|
||||
let columns = cols.as_mut_slice();
|
||||
let rotations = by.map(|n| n % record.len()).unwrap_or(1);
|
||||
|
||||
if !cells_only {
|
||||
match direction {
|
||||
HorizontalDirection::Right => columns.rotate_right(rotations),
|
||||
HorizontalDirection::Left => columns.rotate_left(rotations),
|
||||
HorizontalDirection::Right => record.cols.rotate_right(rotations),
|
||||
HorizontalDirection::Left => record.cols.rotate_left(rotations),
|
||||
}
|
||||
|
||||
columns.to_owned()
|
||||
};
|
||||
|
||||
let values = vals.as_mut_slice();
|
||||
|
||||
match direction {
|
||||
HorizontalDirection::Right => values.rotate_right(rotations),
|
||||
HorizontalDirection::Left => values.rotate_left(rotations),
|
||||
HorizontalDirection::Right => record.vals.rotate_right(rotations),
|
||||
HorizontalDirection::Left => record.vals.rotate_left(rotations),
|
||||
}
|
||||
|
||||
Ok(Value::Record {
|
||||
cols: columns,
|
||||
vals: values.to_owned(),
|
||||
span,
|
||||
})
|
||||
Ok(Value::record(record, span))
|
||||
}
|
||||
Value::List { vals, span } => {
|
||||
let values = vals
|
||||
|
@ -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, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use super::{vertical_rotate_value, VerticalDirection};
|
||||
@ -39,21 +39,18 @@ impl Command for RollDown {
|
||||
example: "[[a b]; [1 2] [3 4] [5 6]] | roll down",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(5), Value::test_int(6)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(1), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns,
|
||||
vals: vec![Value::test_int(3), Value::test_int(4)],
|
||||
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, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use super::{horizontal_rotate_value, HorizontalDirection};
|
||||
@ -51,27 +51,24 @@ impl Command for RollLeft {
|
||||
Example {
|
||||
description: "Rolls columns of a record to the left",
|
||||
example: "{a:1 b:2 c:3} | roll left",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: rotated_columns.clone(),
|
||||
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Rolls columns of a table to the left",
|
||||
example: "[[a b c]; [1 2 3] [4 5 6]] | roll left",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: rotated_columns.clone(),
|
||||
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: rotated_columns,
|
||||
vals: vec![Value::test_int(5), Value::test_int(6), Value::test_int(4)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -81,16 +78,14 @@ impl Command for RollLeft {
|
||||
example: "[[a b c]; [1 2 3] [4 5 6]] | roll left --cells-only",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns,
|
||||
vals: vec![Value::test_int(5), Value::test_int(6), Value::test_int(4)],
|
||||
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, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use super::{horizontal_rotate_value, HorizontalDirection};
|
||||
@ -51,27 +51,24 @@ impl Command for RollRight {
|
||||
Example {
|
||||
description: "Rolls columns of a record to the right",
|
||||
example: "{a:1 b:2 c:3} | roll right",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: rotated_columns.clone(),
|
||||
vals: vec![Value::test_int(3), Value::test_int(1), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Rolls columns to the right",
|
||||
example: "[[a b c]; [1 2 3] [4 5 6]] | roll right",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: rotated_columns.clone(),
|
||||
vals: vec![Value::test_int(3), Value::test_int(1), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: rotated_columns,
|
||||
vals: vec![Value::test_int(6), Value::test_int(4), Value::test_int(5)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -81,16 +78,14 @@ impl Command for RollRight {
|
||||
example: "[[a b c]; [1 2 3] [4 5 6]] | roll right --cells-only",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(3), Value::test_int(1), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns,
|
||||
vals: vec![Value::test_int(6), Value::test_int(4), Value::test_int(5)],
|
||||
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, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use super::{vertical_rotate_value, VerticalDirection};
|
||||
@ -39,21 +39,18 @@ impl Command for RollUp {
|
||||
example: "[[a b]; [1 2] [3 4] [5 6]] | roll up",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(3), Value::test_int(4)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(5), Value::test_int(6)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns,
|
||||
vals: vec![Value::test_int(1), 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,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -40,16 +40,14 @@ impl Command for Rotate {
|
||||
example: "{a:1, b:2} | rotate",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column0".to_string(), "column1".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_string("a")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column0".to_string(), "column1".to_string()],
|
||||
vals: vec![Value::test_int(2), Value::test_string("b")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -59,7 +57,7 @@ impl Command for Rotate {
|
||||
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"column0".to_string(),
|
||||
"column1".to_string(),
|
||||
@ -72,9 +70,8 @@ impl Command for Rotate {
|
||||
Value::test_int(1),
|
||||
Value::test_string("a"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"column0".to_string(),
|
||||
"column1".to_string(),
|
||||
@ -87,8 +84,7 @@ impl Command for Rotate {
|
||||
Value::test_int(2),
|
||||
Value::test_string("b"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -98,16 +94,14 @@ impl Command for Rotate {
|
||||
example: "[[a b]; [1 2]] | rotate col_a col_b",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["col_a".to_string(), "col_b".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_string("a")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["col_a".to_string(), "col_b".to_string()],
|
||||
vals: vec![Value::test_int(2), Value::test_string("b")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -117,16 +111,14 @@ impl Command for Rotate {
|
||||
example: "[[a b]; [1 2]] | rotate --ccw",
|
||||
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("b"), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column0".to_string(), "column1".to_string()],
|
||||
vals: vec![Value::test_string("a"), Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -136,7 +128,7 @@ impl Command for Rotate {
|
||||
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate --ccw",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"column0".to_string(),
|
||||
"column1".to_string(),
|
||||
@ -149,9 +141,8 @@ impl Command for Rotate {
|
||||
Value::test_int(4),
|
||||
Value::test_int(6),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"column0".to_string(),
|
||||
"column1".to_string(),
|
||||
@ -164,8 +155,7 @@ impl Command for Rotate {
|
||||
Value::test_int(3),
|
||||
Value::test_int(5),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -175,16 +165,14 @@ impl Command for Rotate {
|
||||
example: "[[a b]; [1 2]] | rotate --ccw col_a col_b",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["col_a".to_string(), "col_b".to_string()],
|
||||
vals: vec![Value::test_string("b"), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["col_a".to_string(), "col_b".to_string()],
|
||||
vals: vec![Value::test_string("a"), Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -226,9 +214,9 @@ pub fn rotate(
|
||||
if !values.is_empty() {
|
||||
for val in values.into_iter() {
|
||||
match val {
|
||||
Value::Record { cols, vals, .. } => {
|
||||
old_column_names = cols;
|
||||
for v in vals {
|
||||
Value::Record { val: record, .. } => {
|
||||
old_column_names = record.cols;
|
||||
for v in record.vals {
|
||||
new_values.push(v)
|
||||
}
|
||||
}
|
||||
@ -286,11 +274,13 @@ pub fn rotate(
|
||||
|
||||
if not_a_record {
|
||||
return Ok(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::record(
|
||||
Record {
|
||||
cols: new_column_names,
|
||||
vals: new_values,
|
||||
span: call.head,
|
||||
}],
|
||||
},
|
||||
call.head,
|
||||
)],
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data()
|
||||
@ -333,11 +323,13 @@ pub fn rotate(
|
||||
}
|
||||
res.to_vec()
|
||||
};
|
||||
final_values.push(Value::Record {
|
||||
final_values.push(Value::record(
|
||||
Record {
|
||||
cols: new_column_names.clone(),
|
||||
vals: new_vals,
|
||||
span: call.head,
|
||||
})
|
||||
},
|
||||
call.head,
|
||||
))
|
||||
}
|
||||
|
||||
Ok(Value::List {
|
||||
|
@ -3,7 +3,7 @@ use nu_protocol::ast::{Block, Call};
|
||||
use nu_protocol::engine::{Closure, 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;
|
||||
use std::iter::FromIterator;
|
||||
@ -52,7 +52,7 @@ impl Command for UpdateCells {
|
||||
}
|
||||
}"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
"2021-04-16".into(),
|
||||
"2021-06-10".into(),
|
||||
@ -71,8 +71,7 @@ impl Command for UpdateCells {
|
||||
Value::test_string(""),
|
||||
Value::test_string(""),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -89,7 +88,7 @@ impl Command for UpdateCells {
|
||||
}
|
||||
}"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
"2021-04-16".into(),
|
||||
"2021-06-10".into(),
|
||||
@ -108,8 +107,7 @@ impl Command for UpdateCells {
|
||||
Value::test_string(""),
|
||||
Value::test_string(""),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -194,13 +192,13 @@ impl Iterator for UpdateCellIterator {
|
||||
}
|
||||
|
||||
match val {
|
||||
Value::Record { vals, cols, span } => Some(Value::Record {
|
||||
vals: cols
|
||||
.iter()
|
||||
.zip(vals)
|
||||
Value::Record { val, span } => Some(Value::record(
|
||||
val.into_iter()
|
||||
.map(|(col, val)| match &self.columns {
|
||||
Some(cols) if !cols.contains(col) => val,
|
||||
_ => process_cell(
|
||||
Some(cols) if !cols.contains(&col) => (col, val),
|
||||
_ => (
|
||||
col,
|
||||
process_cell(
|
||||
val,
|
||||
&self.engine_state,
|
||||
&mut self.stack,
|
||||
@ -209,11 +207,11 @@ impl Iterator for UpdateCellIterator {
|
||||
self.redirect_stderr,
|
||||
span,
|
||||
),
|
||||
),
|
||||
})
|
||||
.collect(),
|
||||
cols,
|
||||
span,
|
||||
}),
|
||||
)),
|
||||
val => Some(process_cell(
|
||||
val,
|
||||
&self.engine_state,
|
||||
|
@ -1,6 +1,8 @@
|
||||
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 FromUrl;
|
||||
@ -35,7 +37,7 @@ impl Command for FromUrl {
|
||||
vec![Example {
|
||||
example: "'bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter' | from url",
|
||||
description: "Convert url encoded string into a record",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"bread".to_string(),
|
||||
"cheese".to_string(),
|
||||
@ -48,8 +50,7 @@ impl Command for FromUrl {
|
||||
Value::test_string("ham"),
|
||||
Value::test_string("butter"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -61,21 +62,12 @@ fn from_url(input: PipelineData, head: Span) -> Result<PipelineData, ShellError>
|
||||
|
||||
match result {
|
||||
Ok(result) => {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
for (k, v) in result {
|
||||
cols.push(k);
|
||||
vals.push(Value::String { val: v, span: head })
|
||||
}
|
||||
let record = result
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, Value::string(v, head)))
|
||||
.collect();
|
||||
|
||||
Ok(PipelineData::Value(
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
},
|
||||
metadata,
|
||||
))
|
||||
Ok(PipelineData::Value(Value::record(record, head), metadata))
|
||||
}
|
||||
_ => Err(ShellError::UnsupportedInput(
|
||||
"String not compatible with URL encoding".to_string(),
|
||||
|
@ -4,8 +4,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Config, DataSource, Example, IntoPipelineData, PipelineData, PipelineMetadata,
|
||||
ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||
record, Category, Config, DataSource, Example, IntoPipelineData, PipelineData,
|
||||
PipelineMetadata, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
use rust_embed::RustEmbed;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -258,62 +258,34 @@ fn to_html(
|
||||
// If asset doesn't work, make sure to return the default theme
|
||||
let html_themes = get_html_themes("228_themes.json").unwrap_or_default();
|
||||
|
||||
let cols = vec![
|
||||
"name".into(),
|
||||
"black".into(),
|
||||
"red".into(),
|
||||
"green".into(),
|
||||
"yellow".into(),
|
||||
"blue".into(),
|
||||
"purple".into(),
|
||||
"cyan".into(),
|
||||
"white".into(),
|
||||
"brightBlack".into(),
|
||||
"brightRed".into(),
|
||||
"brightGreen".into(),
|
||||
"brightYellow".into(),
|
||||
"brightBlue".into(),
|
||||
"brightPurple".into(),
|
||||
"brightCyan".into(),
|
||||
"brightWhite".into(),
|
||||
"background".into(),
|
||||
"foreground".into(),
|
||||
];
|
||||
|
||||
let result: Vec<Value> = html_themes
|
||||
.themes
|
||||
.into_iter()
|
||||
.map(|n| {
|
||||
let vals = vec![
|
||||
n.name,
|
||||
n.black,
|
||||
n.red,
|
||||
n.green,
|
||||
n.yellow,
|
||||
n.blue,
|
||||
n.purple,
|
||||
n.cyan,
|
||||
n.white,
|
||||
n.brightBlack,
|
||||
n.brightRed,
|
||||
n.brightGreen,
|
||||
n.brightYellow,
|
||||
n.brightBlue,
|
||||
n.brightPurple,
|
||||
n.brightCyan,
|
||||
n.brightWhite,
|
||||
n.background,
|
||||
n.foreground,
|
||||
]
|
||||
.into_iter()
|
||||
.map(|val| Value::String { val, span: head })
|
||||
.collect();
|
||||
|
||||
Value::Record {
|
||||
cols: cols.clone(),
|
||||
vals,
|
||||
span: head,
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"name" => Value::string(n.name, head),
|
||||
"black" => Value::string(n.black, head),
|
||||
"red" => Value::string(n.red, head),
|
||||
"green" => Value::string(n.green, head),
|
||||
"yellow" => Value::string(n.yellow, head),
|
||||
"blue" => Value::string(n.blue, head),
|
||||
"purple" => Value::string(n.purple, head),
|
||||
"cyan" => Value::string(n.cyan, head),
|
||||
"white" => Value::string(n.white, head),
|
||||
"brightBlack" => Value::string(n.brightBlack, head),
|
||||
"brightRed" => Value::string(n.brightRed, head),
|
||||
"brightGreen" => Value::string(n.brightGreen, head),
|
||||
"brightYellow" => Value::string(n.brightYellow, head),
|
||||
"brightBlue" => Value::string(n.brightBlue, head),
|
||||
"brightPurple" => Value::string(n.brightPurple, head),
|
||||
"brightCyan" => Value::string(n.brightCyan, head),
|
||||
"brightWhite" => Value::string(n.brightWhite, head),
|
||||
"background" => Value::string(n.background, head),
|
||||
"foreground" => Value::string(n.foreground, head),
|
||||
},
|
||||
head,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
return Ok(Value::List {
|
||||
|
@ -2,7 +2,7 @@ use inflector::cases::camelcase::to_camel_case;
|
||||
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,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -74,11 +74,10 @@ impl Command for SubCommand {
|
||||
description: "convert a column from a table to camelCase",
|
||||
example: r#"[[lang, gems]; [nu_test, 100]] | str camel-case lang"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
span: Span::test_data(),
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["lang".to_string(), "gems".to_string()],
|
||||
vals: vec![Value::test_string("nuTest"), Value::test_int(100)],
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -2,7 +2,7 @@ use inflector::cases::kebabcase::to_kebab_case;
|
||||
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,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -74,11 +74,10 @@ impl Command for SubCommand {
|
||||
description: "convert a column from a table to kebab-case",
|
||||
example: r#"[[lang, gems]; [nuTest, 100]] | str kebab-case lang"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
span: Span::test_data(),
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["lang".to_string(), "gems".to_string()],
|
||||
vals: vec![Value::test_string("nu-test"), Value::test_int(100)],
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -2,7 +2,7 @@ use inflector::cases::pascalcase::to_pascal_case;
|
||||
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,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -74,11 +74,10 @@ impl Command for SubCommand {
|
||||
description: "convert a column from a table to PascalCase",
|
||||
example: r#"[[lang, gems]; [nu_test, 100]] | str pascal-case lang"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
span: Span::test_data(),
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["lang".to_string(), "gems".to_string()],
|
||||
vals: vec![Value::test_string("NuTest"), Value::test_int(100)],
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -2,7 +2,7 @@ use inflector::cases::screamingsnakecase::to_screaming_snake_case;
|
||||
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,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -74,11 +74,10 @@ impl Command for SubCommand {
|
||||
description: "convert a column from a table to SCREAMING_SNAKE_CASE",
|
||||
example: r#"[[lang, gems]; [nu_test, 100]] | str screaming-snake-case lang"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
span: Span::test_data(),
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["lang".to_string(), "gems".to_string()],
|
||||
vals: vec![Value::test_string("NU_TEST"), Value::test_int(100)],
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -2,7 +2,7 @@ use inflector::cases::snakecase::to_snake_case;
|
||||
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,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -73,11 +73,10 @@ impl Command for SubCommand {
|
||||
description: "convert a column from a table to snake_case",
|
||||
example: r#"[[lang, gems]; [nuTest, 100]] | str snake-case lang"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
span: Span::test_data(),
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["lang".to_string(), "gems".to_string()],
|
||||
vals: vec![Value::test_string("nu_test"), Value::test_int(100)],
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -2,7 +2,7 @@ use inflector::cases::titlecase::to_title_case;
|
||||
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,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -69,11 +69,10 @@ impl Command for SubCommand {
|
||||
description: "convert a column from a table to Title Case",
|
||||
example: r#"[[title, count]; ['nu test', 100]] | str title-case title"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
span: Span::test_data(),
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["title".to_string(), "count".to_string()],
|
||||
vals: vec![Value::test_string("Nu Test"), Value::test_int(100)],
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -2,7 +2,8 @@ use nu_engine::{eval_block, eval_expression, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Block, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
record, Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -91,11 +92,13 @@ impl Command for For {
|
||||
stack.add_var(
|
||||
var_id,
|
||||
if numbered {
|
||||
Value::Record {
|
||||
cols: vec!["index".into(), "item".into()],
|
||||
vals: vec![Value::int(idx as i64, head), x],
|
||||
span: head,
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"index" => Value::int(idx as i64, head),
|
||||
"item" => x,
|
||||
},
|
||||
head,
|
||||
)
|
||||
} else {
|
||||
x
|
||||
},
|
||||
@ -135,11 +138,13 @@ impl Command for For {
|
||||
stack.add_var(
|
||||
var_id,
|
||||
if numbered {
|
||||
Value::Record {
|
||||
cols: vec!["index".into(), "item".into()],
|
||||
vals: vec![Value::int(idx as i64, head), x],
|
||||
span: head,
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"index" => Value::int(idx as i64, head),
|
||||
"item" => x,
|
||||
},
|
||||
head,
|
||||
)
|
||||
} else {
|
||||
x
|
||||
},
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Block, Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -137,15 +137,14 @@ fn intercept_block_control(error: ShellError) -> Result<ShellError, ShellError>
|
||||
|
||||
/// Convert from `error` to [`Value::Record`] so the error information can be easily accessed in catch.
|
||||
fn err_to_record(error: ShellError, head: Span) -> Value {
|
||||
let cols = vec!["msg".to_string(), "debug".to_string(), "raw".to_string()];
|
||||
let vals = vec![
|
||||
Value::string(error.to_string(), head),
|
||||
Value::string(format!("{error:?}"), head),
|
||||
Value::Error {
|
||||
error: Box::new(error),
|
||||
Value::record(
|
||||
record! {
|
||||
"msg" => Value::string(error.to_string(), head),
|
||||
"debug" => Value::string(format!("{error:?}"), head),
|
||||
"raw" => Value::error(error),
|
||||
},
|
||||
];
|
||||
Value::record(cols, vals, head)
|
||||
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,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Type, Value,
|
||||
};
|
||||
use shadow_rs::shadow;
|
||||
|
||||
@ -63,74 +63,64 @@ pub fn version(
|
||||
// - build_rust_channel
|
||||
// - features
|
||||
// - installed_plugins
|
||||
let mut cols = Vec::with_capacity(12);
|
||||
let mut vals = Vec::with_capacity(12);
|
||||
let mut record = Record::with_capacity(12);
|
||||
|
||||
cols.push("version".to_string());
|
||||
vals.push(Value::string(env!("CARGO_PKG_VERSION"), call.head));
|
||||
record.push(
|
||||
"version",
|
||||
Value::string(env!("CARGO_PKG_VERSION"), call.head),
|
||||
);
|
||||
|
||||
cols.push("branch".to_string());
|
||||
vals.push(Value::string(build::BRANCH, call.head));
|
||||
record.push("branch", Value::string(build::BRANCH, call.head));
|
||||
|
||||
let commit_hash = option_env!("NU_COMMIT_HASH");
|
||||
if let Some(commit_hash) = commit_hash {
|
||||
cols.push("commit_hash".to_string());
|
||||
vals.push(Value::string(commit_hash, call.head));
|
||||
record.push("commit_hash", Value::string(commit_hash, call.head));
|
||||
}
|
||||
|
||||
let build_os = Some(build::BUILD_OS).filter(|x| !x.is_empty());
|
||||
if let Some(build_os) = build_os {
|
||||
cols.push("build_os".to_string());
|
||||
vals.push(Value::string(build_os, call.head));
|
||||
record.push("build_os", Value::string(build_os, call.head));
|
||||
}
|
||||
|
||||
let build_target = Some(build::BUILD_TARGET).filter(|x| !x.is_empty());
|
||||
if let Some(build_target) = build_target {
|
||||
cols.push("build_target".to_string());
|
||||
vals.push(Value::string(build_target, call.head));
|
||||
record.push("build_target", Value::string(build_target, call.head));
|
||||
}
|
||||
|
||||
let rust_version = Some(build::RUST_VERSION).filter(|x| !x.is_empty());
|
||||
if let Some(rust_version) = rust_version {
|
||||
cols.push("rust_version".to_string());
|
||||
vals.push(Value::string(rust_version, call.head));
|
||||
record.push("rust_version", Value::string(rust_version, call.head));
|
||||
}
|
||||
|
||||
let rust_channel = Some(build::RUST_CHANNEL).filter(|x| !x.is_empty());
|
||||
if let Some(rust_channel) = rust_channel {
|
||||
cols.push("rust_channel".to_string());
|
||||
vals.push(Value::string(rust_channel, call.head));
|
||||
record.push("rust_channel", Value::string(rust_channel, call.head));
|
||||
}
|
||||
|
||||
let cargo_version = Some(build::CARGO_VERSION).filter(|x| !x.is_empty());
|
||||
if let Some(cargo_version) = cargo_version {
|
||||
cols.push("cargo_version".to_string());
|
||||
vals.push(Value::string(cargo_version, call.head));
|
||||
record.push("cargo_version", Value::string(cargo_version, call.head));
|
||||
}
|
||||
|
||||
let build_time = Some(build::BUILD_TIME).filter(|x| !x.is_empty());
|
||||
if let Some(build_time) = build_time {
|
||||
cols.push("build_time".to_string());
|
||||
vals.push(Value::string(build_time, call.head));
|
||||
record.push("build_time", Value::string(build_time, call.head));
|
||||
}
|
||||
|
||||
let build_rust_channel = Some(build::BUILD_RUST_CHANNEL).filter(|x| !x.is_empty());
|
||||
if let Some(build_rust_channel) = build_rust_channel {
|
||||
cols.push("build_rust_channel".to_string());
|
||||
vals.push(Value::string(build_rust_channel, call.head));
|
||||
record.push(
|
||||
"build_rust_channel",
|
||||
Value::string(build_rust_channel, call.head),
|
||||
);
|
||||
}
|
||||
|
||||
cols.push("allocator".to_string());
|
||||
vals.push(Value::String {
|
||||
val: global_allocator().to_string(),
|
||||
span: call.head,
|
||||
});
|
||||
record.push("allocator", Value::string(global_allocator(), call.head));
|
||||
|
||||
cols.push("features".to_string());
|
||||
vals.push(Value::String {
|
||||
val: features_enabled().join(", "),
|
||||
span: call.head,
|
||||
});
|
||||
record.push(
|
||||
"features",
|
||||
Value::string(features_enabled().join(", "), call.head),
|
||||
);
|
||||
|
||||
// Get a list of command names and check for plugins
|
||||
let installed_plugins = engine_state
|
||||
@ -139,18 +129,12 @@ pub fn version(
|
||||
.map(|x| x.name())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
cols.push("installed_plugins".to_string());
|
||||
vals.push(Value::String {
|
||||
val: installed_plugins.join(", "),
|
||||
span: call.head,
|
||||
});
|
||||
record.push(
|
||||
"installed_plugins",
|
||||
Value::string(installed_plugins.join(", "), call.head),
|
||||
);
|
||||
|
||||
Ok(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
Ok(Value::record(record, call.head).into_pipeline_data())
|
||||
}
|
||||
|
||||
fn global_allocator() -> &'static str {
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
parse_nustyle, NuStyle,
|
||||
};
|
||||
use nu_ansi_term::Style;
|
||||
use nu_protocol::Value;
|
||||
use nu_protocol::{Record, Value};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn lookup_ansi_color_style(s: &str) -> Style {
|
||||
@ -32,7 +32,7 @@ pub fn get_color_map(colors: &HashMap<String, Value>) -> HashMap<String, Style>
|
||||
fn parse_map_entry(hm: &mut HashMap<String, Style>, key: &str, value: &Value) {
|
||||
let value = match value {
|
||||
Value::String { val, .. } => Some(lookup_ansi_color_style(val)),
|
||||
Value::Record { cols, vals, .. } => get_style_from_value(cols, vals).map(parse_nustyle),
|
||||
Value::Record { val, .. } => get_style_from_value(val).map(parse_nustyle),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(value) = value {
|
||||
@ -40,10 +40,10 @@ fn parse_map_entry(hm: &mut HashMap<String, Style>, key: &str, value: &Value) {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_style_from_value(cols: &[String], vals: &[Value]) -> Option<NuStyle> {
|
||||
fn get_style_from_value(record: &Record) -> Option<NuStyle> {
|
||||
let mut was_set = false;
|
||||
let mut style = NuStyle::from(Style::default());
|
||||
for (col, val) in cols.iter().zip(vals) {
|
||||
for (col, val) in record {
|
||||
match col.as_str() {
|
||||
"bg" => {
|
||||
if let Value::String { val, .. } = val {
|
||||
@ -120,8 +120,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_get_style_from_value() {
|
||||
// Test case 1: all values are valid
|
||||
let cols = vec!["bg".to_string(), "fg".to_string(), "attr".to_string()];
|
||||
let vals = vec![
|
||||
let record = Record {
|
||||
cols: vec!["bg".to_string(), "fg".to_string(), "attr".to_string()],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
val: "red".to_string(),
|
||||
span: Span::unknown(),
|
||||
@ -134,34 +135,39 @@ mod tests {
|
||||
val: "bold".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
];
|
||||
],
|
||||
};
|
||||
let expected_style = NuStyle {
|
||||
bg: Some("red".to_string()),
|
||||
fg: Some("blue".to_string()),
|
||||
attr: Some("bold".to_string()),
|
||||
};
|
||||
assert_eq!(get_style_from_value(&cols, &vals), Some(expected_style));
|
||||
assert_eq!(get_style_from_value(&record), Some(expected_style));
|
||||
|
||||
// Test case 2: no values are valid
|
||||
let cols = vec!["invalid".to_string()];
|
||||
let vals = vec![Value::nothing(Span::unknown())];
|
||||
assert_eq!(get_style_from_value(&cols, &vals), None);
|
||||
let record = Record {
|
||||
cols: vec!["invalid".to_string()],
|
||||
vals: vec![Value::nothing(Span::unknown())],
|
||||
};
|
||||
assert_eq!(get_style_from_value(&record), None);
|
||||
|
||||
// Test case 3: some values are valid
|
||||
let cols = vec!["bg".to_string(), "invalid".to_string()];
|
||||
let vals = vec![
|
||||
let record = Record {
|
||||
cols: vec!["bg".to_string(), "invalid".to_string()],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
val: "green".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::nothing(Span::unknown()),
|
||||
];
|
||||
],
|
||||
};
|
||||
let expected_style = NuStyle {
|
||||
bg: Some("green".to_string()),
|
||||
fg: None,
|
||||
attr: None,
|
||||
};
|
||||
assert_eq!(get_style_from_value(&cols, &vals), Some(expected_style));
|
||||
assert_eq!(get_style_from_value(&record), Some(expected_style));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -100,8 +100,8 @@ pub fn color_record_to_nustyle(value: &Value) -> Style {
|
||||
let mut bg = None;
|
||||
let mut attr = None;
|
||||
let v = value.as_record();
|
||||
if let Ok((cols, inner_vals)) = v {
|
||||
for (k, v) in cols.iter().zip(inner_vals) {
|
||||
if let Ok(record) = v {
|
||||
for (k, v) in record {
|
||||
// Because config already type-checked the color_config records, this doesn't bother giving errors
|
||||
// if there are unrecognised keys or bad values.
|
||||
if let Ok(v) = v.as_string() {
|
||||
|
@ -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,7 +269,8 @@ fn histogram_impl(
|
||||
|
||||
result.push((
|
||||
count, // attach count first for easily sorting.
|
||||
Value::Record {
|
||||
Value::record(
|
||||
Record {
|
||||
cols: result_cols.clone(),
|
||||
vals: vec![
|
||||
val.into_value(),
|
||||
@ -287,8 +285,9 @@ fn histogram_impl(
|
||||
},
|
||||
Value::String { val: freq, span },
|
||||
],
|
||||
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,
|
||||
_ => 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,
|
||||
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),
|
||||
},
|
||||
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_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,
|
||||
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("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 });
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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 {
|
||||
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,
|
||||
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),
|
||||
},
|
||||
Value::string(
|
||||
"Parsing only: Same as %z but allows minutes to be missing or present.",
|
||||
head,
|
||||
),
|
||||
],
|
||||
span: 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,112 +431,78 @@ 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 {
|
||||
record.push(
|
||||
"target",
|
||||
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,
|
||||
});
|
||||
Value::string(path_to_link.to_string_lossy(), span)
|
||||
} else {
|
||||
vals.push(Value::String {
|
||||
val: "Could not obtain target file's path".to_string(),
|
||||
span,
|
||||
});
|
||||
Value::string("Could not obtain target file's path", 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());
|
||||
record.push(
|
||||
"user",
|
||||
if let Some(user) = users::get_user_by_uid(md.uid()) {
|
||||
vals.push(Value::String {
|
||||
val: user.name,
|
||||
span,
|
||||
});
|
||||
Value::string(user.name, span)
|
||||
} else {
|
||||
vals.push(Value::Int {
|
||||
val: md.uid() as i64,
|
||||
span,
|
||||
})
|
||||
}
|
||||
Value::int(md.uid() as i64, span)
|
||||
},
|
||||
);
|
||||
|
||||
cols.push("group".into());
|
||||
record.push(
|
||||
"group",
|
||||
if let Some(group) = users::get_group_by_gid(md.gid()) {
|
||||
vals.push(Value::String {
|
||||
val: group.name,
|
||||
span,
|
||||
});
|
||||
Value::string(group.name, span)
|
||||
} else {
|
||||
vals.push(Value::Int {
|
||||
val: md.gid() as i64,
|
||||
span,
|
||||
})
|
||||
}
|
||||
Value::int(md.gid() as i64, span)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cols.push("size".to_string());
|
||||
record.push(
|
||||
"size",
|
||||
if let Some(md) = metadata {
|
||||
let zero_sized = file_type == "pipe"
|
||||
|| file_type == "socket"
|
||||
@ -548,102 +514,72 @@ pub(crate) fn dir_entry_dict(
|
||||
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,
|
||||
});
|
||||
Value::filesize(dir_size as i64, span)
|
||||
} else {
|
||||
let dir_size: u64 = md.len();
|
||||
|
||||
vals.push(Value::Filesize {
|
||||
val: dir_size as i64,
|
||||
span,
|
||||
});
|
||||
};
|
||||
Value::filesize(dir_size as i64, span)
|
||||
}
|
||||
} else if md.is_file() {
|
||||
vals.push(Value::Filesize {
|
||||
val: md.len() as i64,
|
||||
span,
|
||||
});
|
||||
Value::filesize(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(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));
|
||||
}
|
||||
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());
|
||||
record.push(
|
||||
"target",
|
||||
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,
|
||||
});
|
||||
Value::string(path_to_link.to_string_lossy(), span)
|
||||
} else {
|
||||
vals.push(Value::String {
|
||||
val: "Could not obtain target file's path".to_string(),
|
||||
span,
|
||||
});
|
||||
Value::string("Could not obtain target file's path", 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),
|
||||
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,
|
||||
};
|
||||
}
|
||||
vals.push(val);
|
||||
val = Value::date(local.with_timezone(local.offset()), span);
|
||||
}
|
||||
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,
|
||||
};
|
||||
}
|
||||
vals.push(val);
|
||||
val = Value::date(local.with_timezone(local.offset()), span);
|
||||
}
|
||||
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,
|
||||
};
|
||||
}
|
||||
vals.push(val);
|
||||
val = Value::date(local.with_timezone(local.offset()), span);
|
||||
}
|
||||
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,
|
||||
)
|
||||
})
|
||||
.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(),
|
||||
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(
|
||||
Value::Record { val, span } => highlight_terms_in_record_with_search_columns(
|
||||
&cols_to_search_in_map,
|
||||
cols,
|
||||
vals,
|
||||
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(
|
||||
Value::Record { val, span } => highlight_terms_in_record_with_search_columns(
|
||||
&cols_to_search_in_map,
|
||||
cols,
|
||||
vals,
|
||||
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,27 +84,51 @@ 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()
|
||||
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::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(2), 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(2),
|
||||
Value::List {
|
||||
vals: vec![Value::test_int(4), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
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(3), 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(3),
|
||||
Value::List {
|
||||
vals: vec![Value::test_int(4), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
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(4), Value::List{vals: vec![Value::test_int(4), Value::test_int(3)], 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,17 +181,12 @@ 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;
|
||||
|
||||
let records = match item {
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: _,
|
||||
} => (cols, vals),
|
||||
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 => {
|
||||
@ -187,36 +206,18 @@ fn flat_value(columns: &[CellPath], item: &Value, _name_tag: Span, all: bool) ->
|
||||
Err(e) => return vec![Value::Error { error: Box::new(e) }],
|
||||
};
|
||||
|
||||
let records_iterator = {
|
||||
let cols = records.0;
|
||||
let vals = records.1;
|
||||
|
||||
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() {
|
||||
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() };
|
||||
|
||||
match value {
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: _,
|
||||
} => {
|
||||
Value::Record { val, .. } => {
|
||||
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(),
|
||||
);
|
||||
val.iter().for_each(|(col, val)| {
|
||||
if out.contains_key(col) {
|
||||
out.insert(format!("{column}_{col}"), val.clone());
|
||||
} else {
|
||||
out.insert(inner_record_col.to_string(), vals[idx].clone());
|
||||
out.insert(col.to_string(), val.clone());
|
||||
}
|
||||
})
|
||||
} else if out.contains_key(column) {
|
||||
@ -225,9 +226,7 @@ fn flat_value(columns: &[CellPath], item: &Value, _name_tag: Span, all: bool) ->
|
||||
out.insert(column.to_string(), value.clone());
|
||||
}
|
||||
}
|
||||
Value::List { vals, span }
|
||||
if all && vals.iter().all(|f| f.as_record().is_ok()) =>
|
||||
{
|
||||
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(),
|
||||
@ -238,19 +237,17 @@ fn flat_value(columns: &[CellPath], item: &Value, _name_tag: Span, all: bool) ->
|
||||
];
|
||||
}
|
||||
// it's a table (a list of record, we can flatten inner record)
|
||||
let mut cs = vec![];
|
||||
let mut vs = vec![];
|
||||
let mut records = vec![];
|
||||
|
||||
for v in vals {
|
||||
if let Ok(r) = v.as_record() {
|
||||
cs.push(r.0);
|
||||
vs.push(r.1)
|
||||
records.push(r)
|
||||
}
|
||||
}
|
||||
|
||||
if need_flatten {
|
||||
let cols = cs.into_iter().map(|f| f.to_vec());
|
||||
let vals = vs.into_iter().map(|f| f.to_vec());
|
||||
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(),
|
||||
@ -277,8 +274,7 @@ fn flat_value(columns: &[CellPath], item: &Value, _name_tag: Span, all: bool) ->
|
||||
}
|
||||
|
||||
if !columns.is_empty() {
|
||||
let cell_path =
|
||||
column_requested.and_then(|x| match x.members.first() {
|
||||
let cell_path = column_requested.and_then(|x| match x.members.first() {
|
||||
Some(PathMember::String { val, span: _, .. }) => Some(val),
|
||||
_ => None,
|
||||
});
|
||||
@ -313,30 +309,22 @@ fn flat_value(columns: &[CellPath], item: &Value, _name_tag: Span, all: bool) ->
|
||||
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 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());
|
||||
}
|
||||
record_cols.push(col);
|
||||
record_vals.push(val);
|
||||
record.push(col, val);
|
||||
index += 1;
|
||||
}
|
||||
// the flattened column may be the last column in the original table.
|
||||
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);
|
||||
expanded.push(Value::record(record, tag));
|
||||
}
|
||||
}
|
||||
Some(TableInside::FlattenedRows {
|
||||
@ -348,7 +336,7 @@ fn flat_value(columns: &[CellPath], item: &Value, _name_tag: Span, all: bool) ->
|
||||
}) => {
|
||||
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 record = Record::new();
|
||||
let mut index = 0;
|
||||
|
||||
for (base_col, base_val) in base.into_iter() {
|
||||
@ -356,46 +344,33 @@ fn flat_value(columns: &[CellPath], item: &Value, _name_tag: Span, all: bool) ->
|
||||
// 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());
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
record_cols.push(base_col);
|
||||
record_vals.push(base_val);
|
||||
record.push(base_col, base_val);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
// 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_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);
|
||||
}
|
||||
expanded.push(Value::record(record, tag));
|
||||
}
|
||||
}
|
||||
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);
|
||||
expanded.push(Value::record(out.into_iter().collect(), tag));
|
||||
}
|
||||
}
|
||||
expanded
|
||||
@ -408,8 +383,6 @@ fn flat_value(columns: &[CellPath], item: &Value, _name_tag: Span, all: bool) ->
|
||||
} else {
|
||||
vec![item.clone()]
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
#[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 {
|
||||
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),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}, Value::Record {
|
||||
}),
|
||||
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),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}, Value::Record {
|
||||
}),
|
||||
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(),
|
||||
}],
|
||||
}),
|
||||
],
|
||||
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,
|
||||
}
|
||||
}
|
||||
(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(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,25 +316,19 @@ 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,
|
||||
}
|
||||
Ok(Value::record(record, call_span)
|
||||
.into_pipeline_data()
|
||||
.set_metadata(metadata))
|
||||
} else {
|
||||
|
@ -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(),
|
||||
vals: vec.iter().map(clone_to_lowercase).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(),
|
||||
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 {
|
||||
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",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user