diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index be14a9878c..7729c000e1 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -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 = (0..col_cnt).map(|x| format!("col_{x}")).collect(); - let vals: Vec = (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(), } } diff --git a/crates/nu-cli/src/commands/history.rs b/crates/nu-cli/src/commands/history.rs index 722cc6493e..0ecea17edf 100644 --- a/crates/nu-cli/src/commands/history.rs +++ b/crates/nu-cli/src/commands/history.rs @@ -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,20 +95,15 @@ 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, - }, - Value::int(idx as i64, head), - ], - 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), + }, + head, + ) + }) }) .ok_or(ShellError::FileNotFound(head))? .into_pipeline_data(ctrlc)), @@ -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, + ) } } diff --git a/crates/nu-cli/src/commands/keybindings_default.rs b/crates/nu-cli/src/commands/keybindings_default.rs index 3700fe30ce..8670150f0a 100644 --- a/crates/nu-cli/src/commands/keybindings_default.rs +++ b/crates/nu-cli/src/commands/keybindings_default.rs @@ -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(); diff --git a/crates/nu-cli/src/commands/keybindings_list.rs b/crates/nu-cli/src/commands/keybindings_list.rs index 6aa96b1867..efa2d12bbb 100644 --- a/crates/nu-cli/src/commands/keybindings_list.rs +++ b/crates/nu-cli/src/commands/keybindings_list.rs @@ -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 { } 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 diff --git a/crates/nu-cli/src/commands/keybindings_listen.rs b/crates/nu-cli/src/commands/keybindings_listen.rs index 234540b99a..cdda374aee 100644 --- a/crates/nu-cli/src/commands/keybindings_listen.rs +++ b/crates/nu-cli/src/commands/keybindings_listen.rs @@ -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 { 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::>() .join(", "), @@ -111,54 +111,29 @@ fn print_events_helper(event: Event) -> Result { { 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())) } } diff --git a/crates/nu-cli/src/completions/completer.rs b/crates/nu-cli/src/completions/completer.rs index 094040a745..13a2410bae 100644 --- a/crates/nu-cli/src/completions/completer.rs +++ b/crates/nu-cli/src/completions/completer.rs @@ -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 diff --git a/crates/nu-cli/src/completions/variable_completions.rs b/crates/nu-cli/src/completions/variable_completions.rs index b750c3331e..2072f9ceba 100644 --- a/crates/nu-cli/src/completions/variable_completions.rs +++ b/crates/nu-cli/src/completions/variable_completions.rs @@ -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>) -> 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 diff --git a/crates/nu-cli/src/reedline_config.rs b/crates/nu-cli/src/reedline_config.rs index 8899e46d6f..fad763503b 100644 --- a/crates/nu-cli/src/reedline_config.rs +++ b/crates/nu-cli/src/reedline_config.rs @@ -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 { - 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,68 +714,60 @@ enum EventType<'config> { } impl<'config> EventType<'config> { - fn try_from_columns( - cols: &'config [String], - vals: &'config [Value], - span: Span, - ) -> Result { - extract_value("send", cols, vals, span) + fn try_from_record(record: &'config Record, span: Span) -> Result { + 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, ShellError> { match value { - Value::Record { cols, vals, span } => { - match EventType::try_from_columns(cols, vals, *span)? { - EventType::Send(value) => event_from_record( + 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(), + record, + config, + *span, + ) + .map(Some), + EventType::Edit(value) => { + let edit = edit_from_record( value.into_string("", config).to_lowercase().as_str(), - cols, - vals, + record, config, *span, - ) - .map(Some), - EventType::Edit(value) => { - let edit = edit_from_record( - value.into_string("", config).to_lowercase().as_str(), - cols, - vals, - config, - *span, - )?; - Ok(Some(ReedlineEvent::Edit(vec![edit]))) - } - EventType::Until(value) => match value { - Value::List { vals, .. } => { - let events = vals - .iter() - .map(|value| match parse_event(value, config) { - Ok(inner) => match inner { - None => Err(ShellError::UnsupportedConfigValue( - "List containing valid events".to_string(), - "Nothing value (null)".to_string(), - value.span()?, - )), - Some(event) => Ok(event), - }, - Err(e) => Err(e), - }) - .collect::, ShellError>>()?; - - Ok(Some(ReedlineEvent::UntilFound(events))) - } - v => Err(ShellError::UnsupportedConfigValue( - "list of events".to_string(), - v.into_abbreviated_string(config), - v.span()?, - )), - }, + )?; + Ok(Some(ReedlineEvent::Edit(vec![edit]))) } - } + EventType::Until(value) => match value { + Value::List { vals, .. } => { + let events = vals + .iter() + .map(|value| match parse_event(value, config) { + Ok(inner) => match inner { + None => Err(ShellError::UnsupportedConfigValue( + "List containing valid events".to_string(), + "Nothing value (null)".to_string(), + value.span()?, + )), + Some(event) => Ok(event), + }, + Err(e) => Err(e), + }) + .collect::, ShellError>>()?; + + Ok(Some(ReedlineEvent::UntilFound(events))) + } + v => Err(ShellError::UnsupportedConfigValue( + "list of events".to_string(), + v.into_abbreviated_string(config), + v.span()?, + )), + }, + }, Value::List { vals, .. } => { let events = vals .iter() @@ -813,8 +797,7 @@ fn parse_event(value: &Value, config: &Config) -> Result, fn event_from_record( name: &str, - cols: &[String], - vals: &[Value], + record: &Record, config: &Config, span: Span, ) -> Result { @@ -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 { @@ -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(_, _)))); } } diff --git a/crates/nu-cmd-base/src/formats/to/delimited.rs b/crates/nu-cmd-base/src/formats/to/delimited.rs index ecfdaba5d1..97f63affe0 100644 --- a/crates/nu-cmd-base/src/formats/to/delimited.rs +++ b/crates/nu-cmd-base/src/formats/to/delimited.rs @@ -6,7 +6,7 @@ pub fn merge_descriptors(values: &[Value]) -> Vec { let mut seen: IndexSet = 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 { diff --git a/crates/nu-cmd-dataframe/src/dataframe/eager/list.rs b/crates/nu-cmd-dataframe/src/dataframe/eager/list.rs index 05bcdfe52a..78b5f3a5de 100644 --- a/crates/nu-cmd-dataframe/src/dataframe/eager/list.rs +++ b/crates/nu-cmd-dataframe/src/dataframe/eager/list.rs @@ -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::>(); diff --git a/crates/nu-cmd-dataframe/src/dataframe/eager/to_nu.rs b/crates/nu-cmd-dataframe/src/dataframe/eager/to_nu.rs index 1ab8fce211..faeb7fd4e7 100644 --- a/crates/nu-cmd-dataframe/src/dataframe/eager/to_nu.rs +++ b/crates/nu-cmd-dataframe/src/dataframe/eager/to_nu.rs @@ -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 { 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(), - }), + })), }, ] } diff --git a/crates/nu-cmd-dataframe/src/dataframe/expressions/alias.rs b/crates/nu-cmd-dataframe/src/dataframe/expressions/alias.rs index c5fa821045..7759d6b6bf 100644 --- a/crates/nu-cmd-dataframe/src/dataframe/expressions/alias.rs +++ b/crates/nu-cmd-dataframe/src/dataframe/expressions/alias.rs @@ -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) }, diff --git a/crates/nu-cmd-dataframe/src/dataframe/expressions/col.rs b/crates/nu-cmd-dataframe/src/dataframe/expressions/col.rs index 70110459ef..59f9c467bf 100644 --- a/crates/nu-cmd-dataframe/src/dataframe/expressions/col.rs +++ b/crates/nu-cmd-dataframe/src/dataframe/expressions/col.rs @@ -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(), - }), + })), }] } diff --git a/crates/nu-cmd-dataframe/src/dataframe/expressions/lit.rs b/crates/nu-cmd-dataframe/src/dataframe/expressions/lit.rs index ed9d8a362f..50599db023 100644 --- a/crates/nu-cmd-dataframe/src/dataframe/expressions/lit.rs +++ b/crates/nu-cmd-dataframe/src/dataframe/expressions/lit.rs @@ -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(), - }), + })), }] } diff --git a/crates/nu-cmd-dataframe/src/dataframe/values/nu_dataframe/conversion.rs b/crates/nu-cmd-dataframe/src/dataframe/values/nu_dataframe/conversion.rs index 2b3e3b18b7..e4cc880ce6 100644 --- a/crates/nu-cmd-dataframe/src/dataframe/values/nu_dataframe/conversion.rs +++ b/crates/nu-cmd-dataframe/src/dataframe/values/nu_dataframe/conversion.rs @@ -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, 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(()) diff --git a/crates/nu-cmd-dataframe/src/dataframe/values/nu_dataframe/mod.rs b/crates/nu-cmd-dataframe/src/dataframe/values/nu_dataframe/mod.rs index 2823e5ae70..286e6e7fe6 100644 --- a/crates/nu-cmd-dataframe/src/dataframe/values/nu_dataframe/mod.rs +++ b/crates/nu-cmd-dataframe/src/dataframe/values/nu_dataframe/mod.rs @@ -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::>(); - 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::>(); diff --git a/crates/nu-cmd-dataframe/src/dataframe/values/nu_expression/mod.rs b/crates/nu-cmd-dataframe/src/dataframe/values/nu_expression/mod.rs index 5743fa8414..da01ca72d7 100644 --- a/crates/nu-cmd-dataframe/src/dataframe/values/nu_expression/mod.rs +++ b/crates/nu-cmd-dataframe/src/dataframe/values/nu_expression/mod.rs @@ -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(), - span, - }; + 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], - 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::Column(name) => Value::record( + record! { + "expr" => Value::string("column", span), + "value" => Value::string(name.to_string(), span), + }, + 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(), - span, - }; - let value = Value::String { - val: format!("{literal:?}"), - 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::Literal(literal) => Value::record( + record! { + "expr" => Value::string("literal", span), + "value" => Value::string(format!("{literal:?}"), span), + }, + span, + ), + 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, + ), 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], - span, - } - } + } => 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:?}"), - span, - }; - - let cols = vec!["expr".into(), "quantile".into(), "interpol".into()]; - - Value::Record { - cols, - vals: vec![expr, quantile, interpol], - span, - } - } + } => 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 expr_type = Value::String { - val: "agg".into(), + Value::record( + record! { + "expr" => Value::string("agg", span), + "value" => value, + }, 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(), - span, - }; - let cols = vec!["expr".into()]; - - Value::Record { - cols, - vals: vec![expr], - 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, - } + Value::record(record! { "expr" => Value::string("wildcard", span) }, span) } + Expr::Explode(expr) => Value::record( + record! { "expr" => expr_to_value(expr.as_ref(), span) }, + span, + ), + Expr::KeepName(expr) => Value::record( + record! { "expr" => expr_to_value(expr.as_ref(), span) }, + 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:?}"), - span, - }; - let cols = vec!["expr".into(), "options".into()]; - - Value::Record { - cols, - vals: vec![expr, options], - span, - } - } + Expr::Sort { expr, options } => Value::record( + record! { + "expr" => expr_to_value(expr.as_ref(), span), + "options" => Value::string(format!("{options:?}"), span), + }, + span, + ), Expr::Cast { expr, data_type, strict, - } => { - let expr = expr_to_value(expr.as_ref(), span); - let dtype = Value::String { - val: format!("{data_type:?}"), - 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], - 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, - } - } + } => Value::record( + record! { + "expr" => expr_to_value(expr.as_ref(), span), + "dtype" => Value::string(format!("{data_type:?}"), span), + "strict" => Value::bool(*strict, span), + }, + span, + ), + 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::SortBy { expr, by, descending, } => { - let expr = expr_to_value(expr.as_ref(), span); let by: Vec = by.iter().map(|b| expr_to_value(b, span)).collect(); - let by = Value::List { vals: by, span }; - let descending: Vec = descending .iter() .map(|r| Value::Bool { val: *r, span }) .collect(); - let descending = Value::List { - vals: descending, - span, - }; - let cols = vec!["expr".into(), "by".into(), "descending".into()]; - - Value::Record { - cols, - vals: vec![expr, by, descending], + Value::record( + record! { + "expr" => expr_to_value(expr.as_ref(), span), + "by" => Value::list(by, span), + "descending" => Value::list(descending, 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::Filter { input, by } => Value::record( + record! { + "input" => expr_to_value(input.as_ref(), span), + "by" => expr_to_value(by.as_ref(), span), + }, + 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], - span, - } - } + } => 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:?}"), - span, - }; - - let cols = vec!["expr".into(), "function".into()]; - - Value::Record { - cols, - vals: vec![expr, function], - span, - } + ) } + Expr::RenameAlias { expr, function } => Value::record( + record! { + "expr" => expr_to_value(expr.as_ref(), span), + "function" => Value::string(format!("{function:?}"), span), + }, + span, + ), Expr::AnonymousFunction { input, function, @@ -519,33 +375,15 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value { options, } => { let input: Vec = 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 = 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), + "options" => Value::string(format!("{options:?}"), span), + }, 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], - span, - } - } - Expr::Cache { input, id } => { - let input = expr_to_value(input.as_ref(), span); - let id = Value::String { - val: format!("{id:?}"), - span, - }; - - let cols = vec!["input".into(), "id".into()]; - - Value::Record { - cols, - vals: vec![input, id], - span, - } + ) } + Expr::Cache { input, id } => Value::record( + record! { + "input" => expr_to_value(input.as_ref(), span), + "id" => Value::string(format!("{id:?}"), span), + }, + span, + ), Expr::Window { function, partition_by, order_by, options, } => { - let function = expr_to_value(function, span); - let partition_by: Vec = 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:?}"), + 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, - }; - - let cols = vec![ - "function".into(), - "partition_by".into(), - "order_by".into(), - "options".into(), - ]; - - Value::Record { - cols, - vals: vec![function, partition_by, order_by, options], - span, - } + ) } } } diff --git a/crates/nu-cmd-dataframe/src/dataframe/values/nu_lazyframe/custom_value.rs b/crates/nu-cmd-dataframe/src/dataframe/values/nu_lazyframe/custom_value.rs index 5a64534701..65821f7fc1 100644 --- a/crates/nu-cmd-dataframe/src/dataframe/values/nu_lazyframe/custom_value.rs +++ b/crates/nu-cmd-dataframe/src/dataframe/values/nu_lazyframe/custom_value.rs @@ -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 { - let cols = vec!["plan".into(), "optimized_plan".into()]; - let vals = vec![ - Value::String { - val: self.as_ref().describe_plan(), - span, - }, - Value::String { - val: self - .as_ref() - .describe_optimized_plan() - .unwrap_or_else(|_| "".to_string()), - span, - }, - ]; + let optimized_plan = self + .as_ref() + .describe_optimized_plan() + .unwrap_or_else(|_| "".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 { diff --git a/crates/nu-cmd-dataframe/src/dataframe/values/nu_lazygroupby/custom_value.rs b/crates/nu-cmd-dataframe/src/dataframe/values/nu_lazygroupby/custom_value.rs index 3a52d872dc..a800b1a887 100644 --- a/crates/nu-cmd-dataframe/src/dataframe/values/nu_lazygroupby/custom_value.rs +++ b/crates/nu-cmd-dataframe/src/dataframe/values/nu_lazygroupby/custom_value.rs @@ -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 { - 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 { diff --git a/crates/nu-cmd-extra/src/extra/conversions/fmt.rs b/crates/nu-cmd-extra/src/extra/conversions/fmt.rs index fdbf961d5c..ae8531ffca 100644 --- a/crates/nu-cmd-extra/src/extra/conversions/fmt.rs +++ b/crates/nu-cmd-extra/src/extra/conversions/fmt.rs @@ -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)] diff --git a/crates/nu-cmd-extra/src/extra/filters/roll/mod.rs b/crates/nu-cmd-extra/src/extra/filters/roll/mod.rs index 0c94a856ce..46fe8d8938 100644 --- a/crates/nu-cmd-extra/src/extra/filters/roll/mod.rs +++ b/crates/nu-cmd-extra/src/extra/filters/roll/mod.rs @@ -56,37 +56,24 @@ fn horizontal_rotate_value( ) -> Result { 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 diff --git a/crates/nu-cmd-extra/src/extra/filters/roll/roll_down.rs b/crates/nu-cmd-extra/src/extra/filters/roll/roll_down.rs index feb6ecb136..ba2303fdcf 100644 --- a/crates/nu-cmd-extra/src/extra/filters/roll/roll_down.rs +++ b/crates/nu-cmd-extra/src/extra/filters/roll/roll_down.rs @@ -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(), }), diff --git a/crates/nu-cmd-extra/src/extra/filters/roll/roll_left.rs b/crates/nu-cmd-extra/src/extra/filters/roll/roll_left.rs index c562825ced..021b82e409 100644 --- a/crates/nu-cmd-extra/src/extra/filters/roll/roll_left.rs +++ b/crates/nu-cmd-extra/src/extra/filters/roll/roll_left.rs @@ -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(), }), diff --git a/crates/nu-cmd-extra/src/extra/filters/roll/roll_right.rs b/crates/nu-cmd-extra/src/extra/filters/roll/roll_right.rs index 9286c34ed1..d35e13608c 100644 --- a/crates/nu-cmd-extra/src/extra/filters/roll/roll_right.rs +++ b/crates/nu-cmd-extra/src/extra/filters/roll/roll_right.rs @@ -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(), }), diff --git a/crates/nu-cmd-extra/src/extra/filters/roll/roll_up.rs b/crates/nu-cmd-extra/src/extra/filters/roll/roll_up.rs index 67243cb70b..963c5ccc5a 100644 --- a/crates/nu-cmd-extra/src/extra/filters/roll/roll_up.rs +++ b/crates/nu-cmd-extra/src/extra/filters/roll/roll_up.rs @@ -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(), }), diff --git a/crates/nu-cmd-extra/src/extra/filters/rotate.rs b/crates/nu-cmd-extra/src/extra/filters/rotate.rs index a4dace7702..43fced6fef 100644 --- a/crates/nu-cmd-extra/src/extra/filters/rotate.rs +++ b/crates/nu-cmd-extra/src/extra/filters/rotate.rs @@ -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 { - cols: new_column_names, - vals: new_values, - span: call.head, - }], + vals: vec![Value::record( + Record { + cols: new_column_names, + vals: new_values, + }, + call.head, + )], span: call.head, } .into_pipeline_data() @@ -333,11 +323,13 @@ pub fn rotate( } res.to_vec() }; - final_values.push(Value::Record { - cols: new_column_names.clone(), - vals: new_vals, - span: call.head, - }) + final_values.push(Value::record( + Record { + cols: new_column_names.clone(), + vals: new_vals, + }, + call.head, + )) } Ok(Value::List { diff --git a/crates/nu-cmd-extra/src/extra/filters/update_cells.rs b/crates/nu-cmd-extra/src/extra/filters/update_cells.rs index 239cbb1711..305260760a 100644 --- a/crates/nu-cmd-extra/src/extra/filters/update_cells.rs +++ b/crates/nu-cmd-extra/src/extra/filters/update_cells.rs @@ -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,26 +192,26 @@ 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( - val, - &self.engine_state, - &mut self.stack, - &self.block, - self.redirect_stdout, - self.redirect_stderr, - span, + Some(cols) if !cols.contains(&col) => (col, val), + _ => ( + col, + process_cell( + val, + &self.engine_state, + &mut self.stack, + &self.block, + self.redirect_stdout, + self.redirect_stderr, + span, + ), ), }) .collect(), - cols, span, - }), + )), val => Some(process_cell( val, &self.engine_state, diff --git a/crates/nu-cmd-extra/src/extra/formats/from/url.rs b/crates/nu-cmd-extra/src/extra/formats/from/url.rs index 881ee33a46..1a34e72456 100644 --- a/crates/nu-cmd-extra/src/extra/formats/from/url.rs +++ b/crates/nu-cmd-extra/src/extra/formats/from/url.rs @@ -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 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(), diff --git a/crates/nu-cmd-extra/src/extra/formats/to/html.rs b/crates/nu-cmd-extra/src/extra/formats/to/html.rs index 1949f73d16..b7b2ccadaf 100644 --- a/crates/nu-cmd-extra/src/extra/formats/to/html.rs +++ b/crates/nu-cmd-extra/src/extra/formats/to/html.rs @@ -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 = 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 { diff --git a/crates/nu-cmd-extra/src/extra/strings/str_/case/camel_case.rs b/crates/nu-cmd-extra/src/extra/strings/str_/case/camel_case.rs index e23bddbcad..b49d74bb24 100644 --- a/crates/nu-cmd-extra/src/extra/strings/str_/case/camel_case.rs +++ b/crates/nu-cmd-extra/src/extra/strings/str_/case/camel_case.rs @@ -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(), }), }, diff --git a/crates/nu-cmd-extra/src/extra/strings/str_/case/kebab_case.rs b/crates/nu-cmd-extra/src/extra/strings/str_/case/kebab_case.rs index fdf0fb81d9..d869d02306 100644 --- a/crates/nu-cmd-extra/src/extra/strings/str_/case/kebab_case.rs +++ b/crates/nu-cmd-extra/src/extra/strings/str_/case/kebab_case.rs @@ -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(), }), }, diff --git a/crates/nu-cmd-extra/src/extra/strings/str_/case/pascal_case.rs b/crates/nu-cmd-extra/src/extra/strings/str_/case/pascal_case.rs index b7517ae6f9..b694350f65 100644 --- a/crates/nu-cmd-extra/src/extra/strings/str_/case/pascal_case.rs +++ b/crates/nu-cmd-extra/src/extra/strings/str_/case/pascal_case.rs @@ -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(), }), }, diff --git a/crates/nu-cmd-extra/src/extra/strings/str_/case/screaming_snake_case.rs b/crates/nu-cmd-extra/src/extra/strings/str_/case/screaming_snake_case.rs index 082b0a3e9f..f78b029b8b 100644 --- a/crates/nu-cmd-extra/src/extra/strings/str_/case/screaming_snake_case.rs +++ b/crates/nu-cmd-extra/src/extra/strings/str_/case/screaming_snake_case.rs @@ -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(), }), }, diff --git a/crates/nu-cmd-extra/src/extra/strings/str_/case/snake_case.rs b/crates/nu-cmd-extra/src/extra/strings/str_/case/snake_case.rs index bd0c67494d..64d2b399ee 100644 --- a/crates/nu-cmd-extra/src/extra/strings/str_/case/snake_case.rs +++ b/crates/nu-cmd-extra/src/extra/strings/str_/case/snake_case.rs @@ -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(), }), }, diff --git a/crates/nu-cmd-extra/src/extra/strings/str_/case/title_case.rs b/crates/nu-cmd-extra/src/extra/strings/str_/case/title_case.rs index 7d102e51b9..16dd30f03d 100644 --- a/crates/nu-cmd-extra/src/extra/strings/str_/case/title_case.rs +++ b/crates/nu-cmd-extra/src/extra/strings/str_/case/title_case.rs @@ -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(), }), }, diff --git a/crates/nu-cmd-lang/src/core_commands/for_.rs b/crates/nu-cmd-lang/src/core_commands/for_.rs index fca6bbc5ef..0c37c131cc 100644 --- a/crates/nu-cmd-lang/src/core_commands/for_.rs +++ b/crates/nu-cmd-lang/src/core_commands/for_.rs @@ -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 }, diff --git a/crates/nu-cmd-lang/src/core_commands/try_.rs b/crates/nu-cmd-lang/src/core_commands/try_.rs index b272769c8b..6d7da363d4 100644 --- a/crates/nu-cmd-lang/src/core_commands/try_.rs +++ b/crates/nu-cmd-lang/src/core_commands/try_.rs @@ -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 /// 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)] diff --git a/crates/nu-cmd-lang/src/core_commands/version.rs b/crates/nu-cmd-lang/src/core_commands/version.rs index 747ee14964..19feea0f32 100644 --- a/crates/nu-cmd-lang/src/core_commands/version.rs +++ b/crates/nu-cmd-lang/src/core_commands/version.rs @@ -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::>(); - 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 { diff --git a/crates/nu-color-config/src/color_config.rs b/crates/nu-color-config/src/color_config.rs index 6dc81ec792..d339d56be2 100644 --- a/crates/nu-color-config/src/color_config.rs +++ b/crates/nu-color-config/src/color_config.rs @@ -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) -> HashMap fn parse_map_entry(hm: &mut HashMap, 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, key: &str, value: &Value) { } } -fn get_style_from_value(cols: &[String], vals: &[Value]) -> Option { +fn get_style_from_value(record: &Record) -> Option { 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,48 +120,54 @@ 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![ - Value::String { - val: "red".to_string(), - span: Span::unknown(), - }, - Value::String { - val: "blue".to_string(), - span: Span::unknown(), - }, - Value::String { - val: "bold".to_string(), - span: Span::unknown(), - }, - ]; + 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(), + }, + Value::String { + val: "blue".to_string(), + span: Span::unknown(), + }, + Value::String { + 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![ - Value::String { - val: "green".to_string(), - span: Span::unknown(), - }, - Value::nothing(Span::unknown()), - ]; + 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] diff --git a/crates/nu-color-config/src/nu_style.rs b/crates/nu-color-config/src/nu_style.rs index e8996328b6..99854100b7 100644 --- a/crates/nu-color-config/src/nu_style.rs +++ b/crates/nu-color-config/src/nu_style.rs @@ -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() { diff --git a/crates/nu-command/src/bytes/at.rs b/crates/nu-command/src/bytes/at.rs index 2e60c88a46..f1b6b01f98 100644 --- a/crates/nu-command/src/bytes/at.rs +++ b/crates/nu-command/src/bytes/at.rs @@ -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(), }), }, diff --git a/crates/nu-command/src/bytes/index_of.rs b/crates/nu-command/src/bytes/index_of.rs index 03c2981603..bcb02861b5 100644 --- a/crates/nu-command/src/bytes/index_of.rs +++ b/crates/nu-command/src/bytes/index_of.rs @@ -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(), }), }, diff --git a/crates/nu-command/src/bytes/remove.rs b/crates/nu-command/src/bytes/remove.rs index 86f3d7e732..bc43760bee 100644 --- a/crates/nu-command/src/bytes/remove.rs +++ b/crates/nu-command/src/bytes/remove.rs @@ -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(), }), }, diff --git a/crates/nu-command/src/bytes/replace.rs b/crates/nu-command/src/bytes/replace.rs index ee47a0c54f..a78fcae73f 100644 --- a/crates/nu-command/src/bytes/replace.rs +++ b/crates/nu-command/src/bytes/replace.rs @@ -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(), }), }, diff --git a/crates/nu-command/src/charting/histogram.rs b/crates/nu-command/src/charting/histogram.rs index b6e2d19b6c..35c1864ae1 100755 --- a/crates/nu-command/src/charting/histogram.rs +++ b/crates/nu-command/src/charting/histogram.rs @@ -4,11 +4,10 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Type, Value, + Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span, + Spanned, SyntaxShape, Type, Value, }; use std::collections::HashMap; -use std::iter; #[derive(Clone)] pub struct Histogram; @@ -53,7 +52,7 @@ impl Command for Histogram { description: "Compute a histogram for a list of numbers", example: "[1 2 1] | histogram", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()], vals: vec![ Value::test_int(1), @@ -62,9 +61,8 @@ impl Command for Histogram { Value::test_string("66.67%"), Value::test_string("******************************************************************"), ], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()], vals: vec![ Value::test_int(2), @@ -73,8 +71,7 @@ impl Command for Histogram { Value::test_string("33.33%"), Value::test_string("*********************************"), ], - span: Span::test_data(), - }], + })], span: Span::test_data(), } ), @@ -195,8 +192,8 @@ fn run_histogram( for v in values { match v { // parse record, and fill valid value to actual input. - Value::Record { cols, vals, .. } => { - for (c, v) in iter::zip(cols, vals) { + Value::Record { val, .. } => { + for (c, v) in val { if &c == col_name { if let Ok(v) = HashableValue::from_value(v, head_span) { inputs.push(v); @@ -272,23 +269,25 @@ fn histogram_impl( result.push(( count, // attach count first for easily sorting. - Value::Record { - cols: result_cols.clone(), - vals: vec![ - val.into_value(), - Value::Int { val: count, span }, - Value::Float { - val: quantile, - span, - }, - Value::String { - val: percentage, - span, - }, - Value::String { val: freq, span }, - ], + Value::record( + Record { + cols: result_cols.clone(), + vals: vec![ + val.into_value(), + Value::Int { val: count, span }, + Value::Float { + val: quantile, + span, + }, + Value::String { + val: percentage, + span, + }, + Value::String { val: freq, span }, + ], + }, span, - }, + ), )); } result.sort_by(|a, b| b.0.cmp(&a.0)); diff --git a/crates/nu-command/src/conversions/into/bool.rs b/crates/nu-command/src/conversions/into/bool.rs index 56de5b1b05..d86780ea4a 100644 --- a/crates/nu-command/src/conversions/into/bool.rs +++ b/crates/nu-command/src/conversions/into/bool.rs @@ -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, }), diff --git a/crates/nu-command/src/conversions/into/decimal.rs b/crates/nu-command/src/conversions/into/decimal.rs index 77aabaa47f..a9b0d982d6 100644 --- a/crates/nu-command/src/conversions/into/decimal.rs +++ b/crates/nu-command/src/conversions/into/decimal.rs @@ -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(), }), }, diff --git a/crates/nu-command/src/conversions/into/duration.rs b/crates/nu-command/src/conversions/into/duration.rs index ad0e721ed8..24fc069004 100644 --- a/crates/nu-command/src/conversions/into/duration.rs +++ b/crates/nu-command/src/conversions/into/duration.rs @@ -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, }), diff --git a/crates/nu-command/src/conversions/into/filesize.rs b/crates/nu-command/src/conversions/into/filesize.rs index 92b911057c..38303f0dea 100644 --- a/crates/nu-command/src/conversions/into/filesize.rs +++ b/crates/nu-command/src/conversions/into/filesize.rs @@ -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(), }), diff --git a/crates/nu-command/src/conversions/into/record.rs b/crates/nu-command/src/conversions/into/record.rs index 4f6f5af148..20af631996 100644 --- a/crates/nu-command/src/conversions/into/record.rs +++ b/crates/nu-command/src/conversions/into/record.rs @@ -1,10 +1,11 @@ use chrono::{DateTime, Datelike, FixedOffset, Timelike}; -use nu_protocol::format_duration_as_timeperiod; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value, + format_duration_as_timeperiod, record, Category, Example, IntoPipelineData, PipelineData, + Record, ShellError, Signature, Span, Type, Value, }; + #[derive(Clone)] pub struct SubCommand; @@ -50,42 +51,39 @@ impl Command for SubCommand { Example { description: "Convert from one row table to record", example: "[[value]; [false]] | into record", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["value".to_string()], vals: vec![Value::bool(false, span)], - span, - }), + })), }, Example { description: "Convert from list to record", example: "[1 2 3] | into record", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["0".to_string(), "1".to_string(), "2".to_string()], vals: vec![ Value::Int { val: 1, span }, Value::Int { val: 2, span }, Value::Int { val: 3, span }, ], - span, - }), + })), }, Example { description: "Convert from range to record", example: "0..2 | into record", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["0".to_string(), "1".to_string(), "2".to_string()], vals: vec![ Value::Int { val: 0, span }, Value::Int { val: 1, span }, Value::Int { val: 2, span }, ], - span, - }), + })), }, Example { description: "convert duration to record (weeks max)", example: "(-500day - 4hr - 5sec) | into record", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec![ "week".into(), "day".into(), @@ -103,22 +101,20 @@ impl Command for SubCommand { span, }, ], - span, - }), + })), }, Example { description: "convert record to record", example: "{a: 1, b: 2} | into record", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::Int { val: 1, span }, Value::Int { val: 2, span }], - span, - }), + })), }, Example { description: "convert date to record", example: "2020-04-12T22:10:57+02:00 | into record", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec![ "year".into(), "month".into(), @@ -140,8 +136,7 @@ impl Command for SubCommand { span, }, ], - span, - }), + })), }, ] } @@ -155,34 +150,26 @@ fn into_record( let input = input.into_value(call.head); let input_type = input.get_type(); let res = match input { - Value::Date { val, span } => parse_date_into_record(Ok(val), span), + Value::Date { val, span } => parse_date_into_record(val, span), Value::Duration { val, span } => parse_duration_into_record(val, span), Value::List { mut vals, span } => match input_type { Type::Table(..) if vals.len() == 1 => vals.pop().expect("already checked 1 item"), - _ => { - let mut cols = vec![]; - let mut values = vec![]; - for (idx, val) in vals.into_iter().enumerate() { - cols.push(format!("{idx}")); - values.push(val); - } - Value::Record { - cols, - vals: values, - span, - } - } + _ => Value::record( + vals.into_iter() + .enumerate() + .map(|(idx, val)| (format!("{idx}"), val)) + .collect(), + span, + ), }, - Value::Range { val, span } => { - let mut cols = vec![]; - let mut vals = vec![]; - for (idx, val) in val.into_range_iter(engine_state.ctrlc.clone())?.enumerate() { - cols.push(format!("{idx}")); - vals.push(val); - } - Value::Record { cols, vals, span } - } - Value::Record { cols, vals, span } => Value::Record { cols, vals, span }, + Value::Range { val, span } => Value::record( + val.into_range_iter(engine_state.ctrlc.clone())? + .enumerate() + .map(|(idx, val)| (format!("{idx}"), val)) + .collect(), + span, + ), + Value::Record { val, span } => Value::Record { val, span }, Value::Error { .. } => input, other => Value::Error { error: Box::new(ShellError::OnlySupportsThisInputType { @@ -196,87 +183,50 @@ fn into_record( Ok(res.into_pipeline_data()) } -fn parse_date_into_record(date: Result, Value>, span: Span) -> Value { - let cols = vec![ - "year".into(), - "month".into(), - "day".into(), - "hour".into(), - "minute".into(), - "second".into(), - "timezone".into(), - ]; - match date { - Ok(x) => { - let vals = vec![ - Value::Int { - val: x.year() as i64, - span, - }, - Value::Int { - val: x.month() as i64, - span, - }, - Value::Int { - val: x.day() as i64, - span, - }, - Value::Int { - val: x.hour() as i64, - span, - }, - Value::Int { - val: x.minute() as i64, - span, - }, - Value::Int { - val: x.second() as i64, - span, - }, - Value::String { - val: x.offset().to_string(), - span, - }, - ]; - Value::Record { cols, vals, span } - } - Err(e) => e, - } +fn parse_date_into_record(date: DateTime, span: Span) -> Value { + Value::record( + record! { + "year" => Value::int(date.year() as i64, span), + "month" => Value::int(date.month() as i64, span), + "day" => Value::int(date.day() as i64, span), + "hour" => Value::int(date.hour() as i64, span), + "minute" => Value::int(date.minute() as i64, span), + "second" => Value::int(date.second() as i64, span), + "timezone" => Value::string(date.offset().to_string(), span), + }, + span, + ) } fn parse_duration_into_record(duration: i64, span: Span) -> Value { let (sign, periods) = format_duration_as_timeperiod(duration); - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); for p in periods { let num_with_unit = p.to_text().to_string(); let split = num_with_unit.split(' ').collect::>(); - 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::().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)] diff --git a/crates/nu-command/src/conversions/into/string.rs b/crates/nu-command/src/conversions/into/string.rs index 85a4875c85..634fbf2d8f 100644 --- a/crates/nu-command/src/conversions/into/string.rs +++ b/crates/nu-command/src/conversions/into/string.rs @@ -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(), diff --git a/crates/nu-command/src/database/commands/into_sqlite.rs b/crates/nu-command/src/database/commands/into_sqlite.rs index 1d3aa60c5f..782f340383 100644 --- a/crates/nu-command/src/database/commands/into_sqlite.rs +++ b/crates/nu-command/src/database/commands/into_sqlite.rs @@ -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::>() .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::>() .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(), diff --git a/crates/nu-command/src/database/commands/schema.rs b/crates/nu-command/src/database/commands/schema.rs index 1ce80294f8..3f730f078b 100644 --- a/crates/nu-command/src/database/commands/schema.rs +++ b/crates/nu-command/src/database/commands/schema.rs @@ -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 { - let mut cols = vec![]; - let mut vals = vec![]; let span = call.head; let sqlite_db = SQLiteDatabase::try_from_pipeline(input, span)?; @@ -59,58 +57,32 @@ impl Command for SchemaDb { ) })?; - let mut table_names = vec![]; - let mut table_values = vec![]; + let mut tables_record = Record::new(); for table in tables { let column_info = get_table_columns(&sqlite_db, &conn, &table, span)?; let constraint_info = get_table_constraints(&sqlite_db, &conn, &table, span)?; let foreign_key_info = get_table_foreign_keys(&sqlite_db, &conn, &table, span)?; let index_info = get_table_indexes(&sqlite_db, &conn, &table, span)?; - let mut cols = vec![]; - let mut vals = vec![]; - - cols.push("columns".into()); - vals.push(Value::List { - vals: column_info, - span, - }); - - cols.push("constraints".into()); - vals.push(Value::List { - vals: constraint_info, - span, - }); - - cols.push("foreign_keys".into()); - vals.push(Value::List { - vals: foreign_key_info, - span, - }); - - cols.push("indexes".into()); - vals.push(Value::List { - vals: index_info, - span, - }); - - table_names.push(table.name); - table_values.push(Value::Record { cols, vals, span }); + tables_record.push( + table.name, + Value::record( + record! { + "columns" => Value::list(column_info, span), + "constraints" => Value::list(constraint_info, span), + "foreign_keys" => Value::list(foreign_key_info, span), + "indexes" => Value::list(index_info, span), + }, + span, + ), + ); } - cols.push("tables".into()); - vals.push(Value::Record { - cols: table_names, - vals: table_values, - span, - }); + let record = record! { "tables" => Value::record(tables_record, span) }; // TODO: add views and triggers - Ok(PipelineData::Value( - Value::Record { cols, vals, span }, - None, - )) + Ok(PipelineData::Value(Value::record(record, span), None)) } } @@ -145,19 +117,14 @@ fn get_table_columns( // a record of column name = column value let mut column_info = vec![]; for t in columns { - let mut col_names = vec![]; - let mut col_values = vec![]; - let fields = t.fields(); - let columns = t.columns(); - for (k, v) in fields.iter().zip(columns.iter()) { - col_names.push(k.clone()); - col_values.push(Value::string(v.clone(), span)); - } - column_info.push(Value::Record { - cols: col_names.clone(), - vals: col_values.clone(), + column_info.push(Value::record( + t.fields() + .into_iter() + .zip(t.columns()) + .map(|(k, v)| (k, Value::string(v, span))) + .collect(), span, - }); + )); } Ok(column_info) @@ -180,19 +147,15 @@ fn get_table_constraints( })?; let mut constraint_info = vec![]; for constraint in constraints { - let mut con_cols = vec![]; - let mut con_vals = vec![]; - let fields = constraint.fields(); - let columns = constraint.columns(); - for (k, v) in fields.iter().zip(columns.iter()) { - con_cols.push(k.clone()); - con_vals.push(Value::string(v.clone(), span)); - } - constraint_info.push(Value::Record { - cols: con_cols.clone(), - vals: con_vals.clone(), + constraint_info.push(Value::record( + constraint + .fields() + .into_iter() + .zip(constraint.columns()) + .map(|(k, v)| (k, Value::string(v, span))) + .collect(), span, - }); + )); } Ok(constraint_info) @@ -215,19 +178,14 @@ fn get_table_foreign_keys( })?; let mut foreign_key_info = vec![]; for fk in foreign_keys { - let mut fk_cols = vec![]; - let mut fk_vals = vec![]; - let fields = fk.fields(); - let columns = fk.columns(); - for (k, v) in fields.iter().zip(columns.iter()) { - fk_cols.push(k.clone()); - fk_vals.push(Value::string(v.clone(), span)); - } - foreign_key_info.push(Value::Record { - cols: fk_cols.clone(), - vals: fk_vals.clone(), + foreign_key_info.push(Value::record( + fk.fields() + .into_iter() + .zip(fk.columns()) + .map(|(k, v)| (k, Value::string(v, span))) + .collect(), span, - }); + )); } Ok(foreign_key_info) @@ -250,19 +208,15 @@ fn get_table_indexes( })?; let mut index_info = vec![]; for index in indexes { - let mut idx_cols = vec![]; - let mut idx_vals = vec![]; - let fields = index.fields(); - let columns = index.columns(); - for (k, v) in fields.iter().zip(columns.iter()) { - idx_cols.push(k.clone()); - idx_vals.push(Value::string(v.clone(), span)); - } - index_info.push(Value::Record { - cols: idx_cols.clone(), - vals: idx_vals.clone(), + index_info.push(Value::record( + index + .fields() + .into_iter() + .zip(index.columns()) + .map(|(k, v)| (k, Value::string(v, span))) + .collect(), span, - }); + )); } Ok(index_info) diff --git a/crates/nu-command/src/database/values/sqlite.rs b/crates/nu-command/src/database/values/sqlite.rs index f3aa5f69ec..99c51aa536 100644 --- a/crates/nu-command/src/database/values/sqlite.rs +++ b/crates/nu-command/src/database/values/sqlite.rs @@ -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>, ) -> Result { - let mut table_names: Vec = Vec::new(); - let mut tables: Vec = 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) -> Value { @@ -446,11 +439,13 @@ pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, column_names: Vec 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); } diff --git a/crates/nu-command/src/date/list_timezone.rs b/crates/nu-command/src/date/list_timezone.rs index 403902537e..a08453a0bb 100644 --- a/crates/nu-command/src/date/list_timezone.rs +++ b/crates/nu-command/src/date/list_timezone.rs @@ -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(), }), }] diff --git a/crates/nu-command/src/date/to_record.rs b/crates/nu-command/src/date/to_record.rs index 2475f962c4..396318d87a 100644 --- a/crates/nu-command/src/date/to_record.rs +++ b/crates/nu-command/src/date/to_record.rs @@ -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, 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, 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)), }, diff --git a/crates/nu-command/src/date/to_table.rs b/crates/nu-command/src/date/to_table.rs index aeb561bb7b..697de40eb6 100644 --- a/crates/nu-command/src/date/to_table.rs +++ b/crates/nu-command/src/date/to_table.rs @@ -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, 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, 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)), }, diff --git a/crates/nu-command/src/date/utils.rs b/crates/nu-command/src/date/utils.rs index e1f3c546fd..e90e34a3f1 100644 --- a/crates/nu-command/src/date/utils.rs +++ b/crates/nu-command/src/date/utils.rs @@ -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::>(); @@ -279,21 +275,16 @@ pub(crate) fn generate_strftime_list(head: Span, show_parse_only_formats: bool) .unwrap_or("") .to_string(); - records.push(Value::Record { - cols: column_names, - vals: vec![ - Value::string("%#z", head), - Value::String { - val: example, - span: head, - }, - Value::string( - "Parsing only: Same as %z but allows minutes to be missing or present.", - head, - ), - ], - span: head, - }); + let description = "Parsing only: Same as %z but allows minutes to be missing or present."; + + records.push(Value::record( + record! { + "Specification" => Value::string("%#z", head), + "Example" => Value::string(example, head), + "Description" => Value::string(description, head), + }, + head, + )); } Value::List { diff --git a/crates/nu-command/src/debug/ast.rs b/crates/nu-command/src/debug/ast.rs index b203d26806..ea3c6800bc 100644 --- a/crates/nu-command/src/debug/ast.rs +++ b/crates/nu-command/src/debug/ast.rs @@ -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()) } } diff --git a/crates/nu-command/src/debug/explain.rs b/crates/nu-command/src/debug/explain.rs index 852923ed49..32241d7712 100644 --- a/crates/nu-command/src/debug/explain.rs +++ b/crates/nu-command/src/debug/explain.rs @@ -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::>() .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::>() .join(" ") diff --git a/crates/nu-command/src/debug/inspect_table.rs b/crates/nu-command/src/debug/inspect_table.rs index 1a127ae798..447bf87ca3 100644 --- a/crates/nu-command/src/debug/inspect_table.rs +++ b/crates/nu-command/src/debug/inspect_table.rs @@ -198,9 +198,10 @@ mod util { /// Try to build column names and a table grid. pub fn collect_input(value: Value) -> (Vec, Vec>) { 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()], diff --git a/crates/nu-command/src/debug/metadata.rs b/crates/nu-command/src/debug/metadata.rs index 2faa35cd59..2f0dc2d616 100644 --- a/crates/nu-command/src/debug/metadata.rs +++ b/crates/nu-command/src/debug/metadata.rs @@ -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>, 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)] diff --git a/crates/nu-command/src/debug/view_files.rs b/crates/nu-command/src/debug/view_files.rs index 9fb18a6663..54581dfb3f 100644 --- a/crates/nu-command/src/debug/view_files.rs +++ b/crates/nu-command/src/debug/view_files.rs @@ -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 { diff --git a/crates/nu-command/src/env/load_env.rs b/crates/nu-command/src/env/load_env.rs index 9f1102903a..12687b3877 100644 --- a/crates/nu-command/src/env/load_env.rs +++ b/crates/nu-command/src/env/load_env.rs @@ -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 { - let arg: Option<(Vec, Vec)> = call.opt(engine_state, stack, 0)?; + let arg: Option = 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 { diff --git a/crates/nu-command/src/env/with_env.rs b/crates/nu-command/src/env/with_env.rs index 78ab778092..0e455f6c50 100644 --- a/crates/nu-command/src/env/with_env.rs +++ b/crates/nu-command/src/env/with_env.rs @@ -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()); } } diff --git a/crates/nu-command/src/filesystem/ls.rs b/crates/nu-command/src/filesystem/ls.rs index 0d7509a056..fd9a4c147e 100644 --- a/crates/nu-command/src/filesystem/ls.rs +++ b/crates/nu-command/src/filesystem/ls.rs @@ -9,7 +9,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, DataSource, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, - PipelineMetadata, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, + PipelineMetadata, Record, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; use pathdiff::diff_paths; @@ -431,219 +431,155 @@ pub(crate) fn dir_entry_dict( )); } - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); let mut file_type = "unknown".to_string(); - cols.push("name".into()); - vals.push(Value::String { - val: display_name.to_string(), - span, - }); + record.push("name", Value::string(display_name, span)); if let Some(md) = metadata { file_type = get_file_type(md, display_name, use_mime_type); - cols.push("type".into()); - vals.push(Value::String { - val: file_type.clone(), - span, - }); + record.push("type", Value::string(file_type.clone(), span)); } else { - cols.push("type".into()); - vals.push(Value::nothing(span)); + record.push("type", Value::nothing(span)); } if long { - cols.push("target".into()); if let Some(md) = metadata { - if md.file_type().is_symlink() { - if let Ok(path_to_link) = filename.read_link() { - vals.push(Value::String { - val: path_to_link.to_string_lossy().to_string(), - span, - }); + record.push( + "target", + if md.file_type().is_symlink() { + if let Ok(path_to_link) = filename.read_link() { + Value::string(path_to_link.to_string_lossy(), span) + } else { + Value::string("Could not obtain target file's path", span) + } } else { - vals.push(Value::String { - val: "Could not obtain target file's path".to_string(), - span, - }); - } - } else { - vals.push(Value::nothing(span)); - } + Value::nothing(span) + }, + ) } } if long { if let Some(md) = metadata { - cols.push("readonly".into()); - vals.push(Value::Bool { - val: md.permissions().readonly(), - span, - }); + record.push("readonly", Value::bool(md.permissions().readonly(), span)); #[cfg(unix)] { use crate::filesystem::util::users; use std::os::unix::fs::MetadataExt; let mode = md.permissions().mode(); - cols.push("mode".into()); - vals.push(Value::String { - val: umask::Mode::from(mode).to_string(), - span, - }); + record.push( + "mode", + Value::string(umask::Mode::from(mode).to_string(), span), + ); let nlinks = md.nlink(); - cols.push("num_links".into()); - vals.push(Value::Int { - val: nlinks as i64, - span, - }); + record.push("num_links", Value::int(nlinks as i64, span)); let inode = md.ino(); - cols.push("inode".into()); - vals.push(Value::Int { - val: inode as i64, - span, - }); + record.push("inode", Value::int(inode as i64, span)); - cols.push("user".into()); - if let Some(user) = users::get_user_by_uid(md.uid()) { - vals.push(Value::String { - val: user.name, - span, - }); - } else { - vals.push(Value::Int { - val: md.uid() as i64, - span, - }) - } + record.push( + "user", + if let Some(user) = users::get_user_by_uid(md.uid()) { + Value::string(user.name, span) + } else { + Value::int(md.uid() as i64, span) + }, + ); - cols.push("group".into()); - if let Some(group) = users::get_group_by_gid(md.gid()) { - vals.push(Value::String { - val: group.name, - span, - }); - } else { - vals.push(Value::Int { - val: md.gid() as i64, - span, - }) - } + record.push( + "group", + if let Some(group) = users::get_group_by_gid(md.gid()) { + Value::string(group.name, span) + } else { + Value::int(md.gid() as i64, span) + }, + ); } } } - cols.push("size".to_string()); - if let Some(md) = metadata { - let zero_sized = file_type == "pipe" - || file_type == "socket" - || file_type == "char device" - || file_type == "block device"; + record.push( + "size", + if let Some(md) = metadata { + let zero_sized = file_type == "pipe" + || file_type == "socket" + || file_type == "char device" + || file_type == "block device"; - if md.is_dir() { - if du { - let params = DirBuilder::new(Span::new(0, 2), None, false, None, false); - let dir_size = DirInfo::new(filename, ¶ms, None, ctrl_c).get_size(); + if md.is_dir() { + if du { + let params = DirBuilder::new(Span::new(0, 2), None, false, None, false); + let dir_size = DirInfo::new(filename, ¶ms, None, ctrl_c).get_size(); - vals.push(Value::Filesize { - val: dir_size as i64, - span, - }); - } else { - let dir_size: u64 = md.len(); + Value::filesize(dir_size as i64, span) + } else { + let dir_size: u64 = md.len(); - vals.push(Value::Filesize { - val: dir_size as i64, - span, - }); - }; - } else if md.is_file() { - vals.push(Value::Filesize { - val: md.len() as i64, - span, - }); - } else if md.file_type().is_symlink() { - if let Ok(symlink_md) = filename.symlink_metadata() { - vals.push(Value::Filesize { - val: symlink_md.len() as i64, - span, - }); - } else { - vals.push(Value::nothing(span)); - } - } else { - let value = if zero_sized { - Value::Filesize { val: 0, span } + Value::filesize(dir_size as i64, span) + } + } else if md.is_file() { + Value::filesize(md.len() as i64, span) + } else if md.file_type().is_symlink() { + if let Ok(symlink_md) = filename.symlink_metadata() { + Value::filesize(symlink_md.len() as i64, span) + } else { + Value::nothing(span) + } + } else if zero_sized { + Value::filesize(0, span) } else { Value::nothing(span) - }; - vals.push(value); - } - } else { - vals.push(Value::nothing(span)); - } + } + } else { + Value::nothing(span) + }, + ); if let Some(md) = metadata { if long { - cols.push("created".to_string()); - { + record.push("created", { let mut val = Value::nothing(span); if let Ok(c) = md.created() { if let Some(local) = try_convert_to_local_date_time(c) { - val = Value::Date { - val: local.with_timezone(local.offset()), - span, - }; + val = Value::date(local.with_timezone(local.offset()), span); } } - vals.push(val); - } + val + }); - cols.push("accessed".to_string()); - { + record.push("accessed", { let mut val = Value::nothing(span); if let Ok(a) = md.accessed() { if let Some(local) = try_convert_to_local_date_time(a) { - val = Value::Date { - val: local.with_timezone(local.offset()), - span, - }; + val = Value::date(local.with_timezone(local.offset()), span) } } - vals.push(val); - } + val + }); } - cols.push("modified".to_string()); - { + record.push("modified", { let mut val = Value::nothing(span); if let Ok(m) = md.modified() { if let Some(local) = try_convert_to_local_date_time(m) { - val = Value::Date { - val: local.with_timezone(local.offset()), - span, - }; + val = Value::date(local.with_timezone(local.offset()), span); } } - vals.push(val); - } + val + }) } else { if long { - cols.push("created".to_string()); - vals.push(Value::nothing(span)); - - cols.push("accessed".to_string()); - vals.push(Value::nothing(span)); + record.push("created", Value::nothing(span)); + record.push("accessed", Value::nothing(span)); } - cols.push("modified".to_string()); - vals.push(Value::nothing(span)); + record.push("modified", Value::nothing(span)); } - Ok(Value::Record { cols, vals, span }) + Ok(Value::record(record, span)) } // TODO: can we get away from local times in `ls`? internals might be cleaner if we worked in UTC @@ -702,14 +638,9 @@ mod windows_helper { span: Span, long: bool, ) -> Value { - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); - cols.push("name".into()); - vals.push(Value::String { - val: display_name.to_string(), - span, - }); + record.push("name", Value::string(display_name, span)); let find_data = match find_first_file(filename, span) { Ok(fd) => fd, @@ -722,90 +653,71 @@ mod windows_helper { filename.to_string_lossy() ); log::error!("{e}"); - return Value::Record { cols, vals, span }; + return Value::record(record, span); } }; - cols.push("type".into()); - vals.push(Value::String { - val: get_file_type_windows_fallback(&find_data), - span, - }); + record.push( + "type", + Value::string(get_file_type_windows_fallback(&find_data), span), + ); if long { - cols.push("target".into()); - if is_symlink(&find_data) { - if let Ok(path_to_link) = filename.read_link() { - vals.push(Value::String { - val: path_to_link.to_string_lossy().to_string(), - span, - }); + record.push( + "target", + if is_symlink(&find_data) { + if let Ok(path_to_link) = filename.read_link() { + Value::string(path_to_link.to_string_lossy(), span) + } else { + Value::string("Could not obtain target file's path", span) + } } else { - vals.push(Value::String { - val: "Could not obtain target file's path".to_string(), - span, - }); - } - } else { - vals.push(Value::nothing(span)); - } + Value::nothing(span) + }, + ); - cols.push("readonly".into()); - vals.push(Value::Bool { - val: (find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY.0 != 0), - span, - }); + record.push( + "readonly", + Value::bool( + find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY.0 != 0, + span, + ), + ); } - cols.push("size".to_string()); let file_size = (find_data.nFileSizeHigh as u64) << 32 | find_data.nFileSizeLow as u64; - vals.push(Value::Filesize { - val: file_size as i64, - span, - }); + record.push("size", Value::filesize(file_size as i64, span)); if long { - cols.push("created".to_string()); - { + record.push("created", { let mut val = Value::nothing(span); let seconds_since_unix_epoch = unix_time_from_filetime(&find_data.ftCreationTime); if let Some(local) = unix_time_to_local_date_time(seconds_since_unix_epoch) { - val = Value::Date { - val: local.with_timezone(local.offset()), - span, - }; + val = Value::date(local.with_timezone(local.offset()), span); } - vals.push(val); - } + val + }); - cols.push("accessed".to_string()); - { + record.push("accessed", { let mut val = Value::nothing(span); let seconds_since_unix_epoch = unix_time_from_filetime(&find_data.ftLastAccessTime); if let Some(local) = unix_time_to_local_date_time(seconds_since_unix_epoch) { - val = Value::Date { - val: local.with_timezone(local.offset()), - span, - }; + val = Value::date(local.with_timezone(local.offset()), span); } - vals.push(val); - } + val + }); } - cols.push("modified".to_string()); - { + record.push("modified", { let mut val = Value::nothing(span); let seconds_since_unix_epoch = unix_time_from_filetime(&find_data.ftLastWriteTime); if let Some(local) = unix_time_to_local_date_time(seconds_since_unix_epoch) { - val = Value::Date { - val: local.with_timezone(local.offset()), - span, - }; + val = Value::date(local.with_timezone(local.offset()), span); } - vals.push(val); - } + val + }); - Value::Record { cols, vals, span } + Value::record(record, span) } fn unix_time_from_filetime(ft: &FILETIME) -> i64 { diff --git a/crates/nu-command/src/filters/columns.rs b/crates/nu-command/src/filters/columns.rs index f346ffd33c..fb2fbf83ba 100644 --- a/crates/nu-command/src/filters/columns.rs +++ b/crates/nu-command/src/filters/columns.rs @@ -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) diff --git a/crates/nu-command/src/filters/compact.rs b/crates/nu-command/src/filters/compact.rs index 5d3bb7fccf..2b750ff735 100644 --- a/crates/nu-command/src/filters/compact.rs +++ b/crates/nu-command/src/filters/compact.rs @@ -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(), }), }, diff --git a/crates/nu-command/src/filters/default.rs b/crates/nu-command/src/filters/default.rs index e16c97a5a5..72c4c7a6a2 100644 --- a/crates/nu-command/src/filters/default.rs +++ b/crates/nu-command/src/filters/default.rs @@ -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, }, diff --git a/crates/nu-command/src/filters/drop/column.rs b/crates/nu-command/src/filters/drop/column.rs index 0ad10f140a..9532ca3b27 100644 --- a/crates/nu-command/src/filters/drop/column.rs +++ b/crates/nu-command/src/filters/drop/column.rs @@ -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) -> Vec { 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()], } } diff --git a/crates/nu-command/src/filters/drop/drop_.rs b/crates/nu-command/src/filters/drop/drop_.rs index 6600d58916..9c7a32b573 100644 --- a/crates/nu-command/src/filters/drop/drop_.rs +++ b/crates/nu-command/src/filters/drop/drop_.rs @@ -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(), }), }, diff --git a/crates/nu-command/src/filters/enumerate.rs b/crates/nu-command/src/filters/enumerate.rs index df258ca7cd..2715849444 100644 --- a/crates/nu-command/src/filters/enumerate.rs +++ b/crates/nu-command/src/filters/enumerate.rs @@ -1,8 +1,8 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - Type, Value, + record, Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError, + Signature, Span, Type, Value, }; #[derive(Clone)] @@ -33,21 +33,18 @@ impl Command for Enumerate { example: r#"[a, b, c] | enumerate "#, result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["index".into(), "item".into()], vals: vec![Value::test_int(0), Value::test_string("a")], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["index".into(), "item".into()], vals: vec![Value::test_int(1), Value::test_string("b")], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["index".into(), "item".into()], vals: vec![Value::test_int(2), Value::test_string("c")], - span: Span::test_data(), - }, + }), ], span: Span::test_data(), }), @@ -68,16 +65,14 @@ impl Command for Enumerate { Ok(input .into_iter() .enumerate() - .map(move |(idx, x)| Value::Record { - cols: vec!["index".into(), "item".into()], - vals: vec![ - Value::Int { - val: idx as i64, - span, + .map(move |(idx, x)| { + Value::record( + record! { + "index" => Value::int(idx as i64, span), + "item" => x, }, - x, - ], - span, + span, + ) }) .into_pipeline_data_with_metadata(metadata, ctrlc)) } diff --git a/crates/nu-command/src/filters/filter.rs b/crates/nu-command/src/filters/filter.rs index d5b92f7b9d..a60b30192c 100644 --- a/crates/nu-command/src/filters/filter.rs +++ b/crates/nu-command/src/filters/filter.rs @@ -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(), }), }, diff --git a/crates/nu-command/src/filters/find.rs b/crates/nu-command/src/filters/find.rs index 2880a9e1d6..cba2281ec3 100644 --- a/crates/nu-command/src/filters/find.rs +++ b/crates/nu-command/src/filters/find.rs @@ -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, - cols: &[String], - vals: &[Value], + record: &Record, span: Span, config: &Config, terms: &[Value], @@ -302,14 +303,14 @@ fn highlight_terms_in_record_with_search_columns( highlight_style: Style, ) -> Value { let cols_to_search = if search_cols.is_empty() { - cols.to_vec() + &record.cols } else { - search_cols.to_vec() + search_cols }; let term_strs: Vec<_> = terms.iter().map(|v| v.into_string("", config)).collect(); // iterator of Ok((val_str, term_str)) pairs if the value should be highlighted, otherwise Err(val) - let try_val_highlight = vals.iter().zip(cols).map(|(val, col)| { + let try_val_highlight = record.iter().map(|(col, val)| { let val_str = val.into_string("", config); let predicate = cols_to_search.contains(col); predicate @@ -337,11 +338,13 @@ fn highlight_terms_in_record_with_search_columns( }) .map(|v| v.unwrap_or_else(|v| v)); - Value::Record { - cols: cols.to_vec(), - vals: new_vals.collect(), + Value::record( + Record { + cols: record.cols.clone(), + vals: new_vals.collect(), + }, span, - } + ) } fn contains_ignore_case(string: &str, substring: &str) -> bool { @@ -392,18 +395,15 @@ fn find_with_rest_and_highlight( PipelineData::Value(_, _) => input .map( move |mut x| match &mut x { - Value::Record { cols, vals, span } => { - highlight_terms_in_record_with_search_columns( - &cols_to_search_in_map, - cols, - vals, - *span, - &config, - &terms, - string_style, - highlight_style, - ) - } + Value::Record { val, span } => highlight_terms_in_record_with_search_columns( + &cols_to_search_in_map, + val, + *span, + &config, + &terms, + string_style, + highlight_style, + ), _ => x, }, ctrlc.clone(), @@ -424,18 +424,15 @@ fn find_with_rest_and_highlight( PipelineData::ListStream(stream, meta) => Ok(ListStream::from_stream( stream .map(move |mut x| match &mut x { - Value::Record { cols, vals, span } => { - highlight_terms_in_record_with_search_columns( - &cols_to_search_in_map, - cols, - vals, - *span, - &config, - &terms, - string_style, - highlight_style, - ) - } + Value::Record { val, span } => highlight_terms_in_record_with_search_columns( + &cols_to_search_in_map, + val, + *span, + &config, + &terms, + string_style, + highlight_style, + ), _ => x, }) .filter(move |value| { @@ -535,13 +532,13 @@ fn value_should_be_printed( | Value::List { .. } | Value::CellPath { .. } | Value::CustomValue { .. } => term_contains_value(term, &lower_value, span), - Value::Record { cols, vals, .. } => { - record_matches_term(cols, vals, columns_to_search, filter_config, term, span) + Value::Record { val, .. } => { + record_matches_term(val, columns_to_search, filter_config, term, span) } Value::LazyRecord { val, .. } => match val.collect() { Ok(val) => match val { - Value::Record { cols, vals, .. } => { - record_matches_term(&cols, &vals, columns_to_search, filter_config, term, span) + Value::Record { val, .. } => { + record_matches_term(&val, columns_to_search, filter_config, term, span) } _ => false, }, @@ -567,19 +564,18 @@ fn term_equals_value(term: &Value, value: &Value, span: Span) -> bool { } fn record_matches_term( - cols: &[String], - vals: &[Value], + record: &Record, columns_to_search: &Vec, 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; } diff --git a/crates/nu-command/src/filters/flatten.rs b/crates/nu-command/src/filters/flatten.rs index c7f7fe881e..8b91d542ec 100644 --- a/crates/nu-command/src/filters/flatten.rs +++ b/crates/nu-command/src/filters/flatten.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, + Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -84,26 +84,50 @@ impl Command for Flatten { example: "{ a: b, d: [ 1 2 3 4 ], e: [ 4 3 ] } | flatten d --all", result: Some(Value::List{ vals: vec![ - Value::Record{ + Value::test_record(Record { cols: vec!["a".to_string(), "d".to_string(), "e".to_string()], - vals: vec![Value::test_string("b"), Value::test_int(1), Value::List{vals: vec![Value::test_int(4), Value::test_int(3)], span: Span::test_data()} ], - span: Span::test_data() - }, - Value::Record{ + vals: vec![ + Value::test_string("b"), + Value::test_int(1), + Value::List { + vals: vec![Value::test_int(4), Value::test_int(3)], + span: Span::test_data(), + }, + ], + }), + Value::test_record(Record { cols: vec!["a".to_string(), "d".to_string(), "e".to_string()], - vals: vec![Value::test_string("b"), Value::test_int(2), Value::List{vals: vec![Value::test_int(4), Value::test_int(3)], span: Span::test_data()} ], - span: Span::test_data() - }, - Value::Record{ + vals: vec![ + Value::test_string("b"), + Value::test_int(2), + Value::List { + vals: vec![Value::test_int(4), Value::test_int(3)], + span: Span::test_data(), + }, + ], + }), + Value::test_record(Record { cols: vec!["a".to_string(), "d".to_string(), "e".to_string()], - vals: vec![Value::test_string("b"), Value::test_int(3), Value::List{vals: vec![Value::test_int(4), Value::test_int(3)], span: Span::test_data()} ], - span: Span::test_data() - }, - Value::Record{ + vals: vec![ + Value::test_string("b"), + Value::test_int(3), + Value::List { + vals: vec![Value::test_int(4), Value::test_int(3)], + span: Span::test_data(), + }, + ], + }), + Value::test_record(Record { cols: vec!["a".to_string(), "d".to_string(), "e".to_string()], - vals: vec![Value::test_string("b"), Value::test_int(4), Value::List{vals: vec![Value::test_int(4), Value::test_int(3)], span: Span::test_data()} ], - span: Span::test_data() - } + vals: vec![ + Value::test_string("b"), + Value::test_int(4), + Value::List { + vals: vec![Value::test_int(4), Value::test_int(3)], + span: Span::test_data() + } + ], + }), ], span: Span::test_data(), }), @@ -157,259 +181,208 @@ fn flat_value(columns: &[CellPath], item: &Value, _name_tag: Span, all: bool) -> Err(e) => return vec![Value::Error { error: Box::new(e) }], }; - let res = { - if item.as_record().is_ok() { - let mut out = IndexMap::::new(); - let mut inner_table = None; + if item.as_record().is_ok() { + let mut out = IndexMap::::new(); + let mut inner_table = None; - let records = match item { - Value::Record { - cols, - vals, - span: _, - } => (cols, vals), - // Propagate errors by explicitly matching them before the final case. - Value::Error { .. } => return vec![item.clone()], - other => { - return vec![Value::Error { - error: Box::new(ShellError::OnlySupportsThisInputType { - exp_input_type: "record".into(), - wrong_type: other.get_type().to_string(), - dst_span: _name_tag, - src_span: other.expect_span(), - }), - }]; - } - }; + let record = match item { + Value::Record { val, .. } => val, + // Propagate errors by explicitly matching them before the final case. + Value::Error { .. } => return vec![item.clone()], + other => { + return vec![Value::Error { + error: Box::new(ShellError::OnlySupportsThisInputType { + exp_input_type: "record".into(), + wrong_type: other.get_type().to_string(), + dst_span: _name_tag, + src_span: other.expect_span(), + }), + }]; + } + }; - let s = match item.span() { - Ok(x) => x, - Err(e) => return vec![Value::Error { error: Box::new(e) }], - }; + let s = match item.span() { + Ok(x) => x, + Err(e) => return vec![Value::Error { error: Box::new(e) }], + }; - let records_iterator = { - let cols = records.0; - let vals = records.1; + for (column_index, (column, value)) in record.iter().enumerate() { + let column_requested = columns.iter().find(|c| c.into_string() == *column); + let need_flatten = { columns.is_empty() || column_requested.is_some() }; - let mut pairs = vec![]; - for i in 0..cols.len() { - pairs.push((cols[i].as_str(), &vals[i])); - } - pairs - }; - - for (column_index, (column, value)) in records_iterator.into_iter().enumerate() { - let column_requested = columns.iter().find(|c| c.into_string() == *column); - let need_flatten = { columns.is_empty() || column_requested.is_some() }; - - match value { - Value::Record { - cols, - vals, - span: _, - } => { - if need_flatten { - cols.iter().enumerate().for_each(|(idx, inner_record_col)| { - if out.contains_key(inner_record_col) { - out.insert( - format!("{column}_{inner_record_col}"), - vals[idx].clone(), - ); - } else { - out.insert(inner_record_col.to_string(), vals[idx].clone()); - } - }) - } else if out.contains_key(column) { - out.insert(format!("{column}_{column}"), value.clone()); - } else { - out.insert(column.to_string(), value.clone()); - } - } - Value::List { vals, span } - if all && vals.iter().all(|f| f.as_record().is_ok()) => - { - if need_flatten && inner_table.is_some() { - return vec![Value::Error{ error: Box::new(ShellError::UnsupportedInput( - "can only flatten one inner list at a time. tried flattening more than one column with inner lists... but is flattened already".to_string(), - "value originates from here".into(), - s, - *span - ))} - ]; - } - // it's a table (a list of record, we can flatten inner record) - let mut cs = vec![]; - let mut vs = vec![]; - - for v in vals { - if let Ok(r) = v.as_record() { - cs.push(r.0); - vs.push(r.1) - } - } - - if need_flatten { - let cols = cs.into_iter().map(|f| f.to_vec()); - let vals = vs.into_iter().map(|f| f.to_vec()); - - inner_table = Some(TableInside::FlattenedRows { - columns: cols.collect(), - _span: &s, - values: vals.collect(), - parent_column_name: column, - parent_column_index: column_index, - }); - } else if out.contains_key(column) { - out.insert(format!("{column}_{column}"), value.clone()); - } else { - out.insert(column.to_string(), value.clone()); - } - } - Value::List { vals: values, span } => { - if need_flatten && inner_table.is_some() { - return vec![Value::Error{ error: Box::new(ShellError::UnsupportedInput( - "can only flatten one inner list at a time. tried flattening more than one column with inner lists... but is flattened already".to_string(), - "value originates from here".into(), - s, - *span - ))} - ]; - } - - if !columns.is_empty() { - let cell_path = - column_requested.and_then(|x| match x.members.first() { - Some(PathMember::String { val, span: _, .. }) => Some(val), - _ => None, - }); - - if let Some(r) = cell_path { - inner_table = Some(TableInside::Entries( - r, - &s, - values.iter().collect::>(), - column_index, - )); + match value { + Value::Record { val, .. } => { + if need_flatten { + val.iter().for_each(|(col, val)| { + if out.contains_key(col) { + out.insert(format!("{column}_{col}"), val.clone()); } else { - out.insert(column.to_string(), value.clone()); + out.insert(col.to_string(), val.clone()); } - } else { + }) + } else if out.contains_key(column) { + out.insert(format!("{column}_{column}"), value.clone()); + } else { + out.insert(column.to_string(), value.clone()); + } + } + Value::List { vals, span } if all && vals.iter().all(|f| f.as_record().is_ok()) => { + if need_flatten && inner_table.is_some() { + return vec![Value::Error{ error: Box::new(ShellError::UnsupportedInput( + "can only flatten one inner list at a time. tried flattening more than one column with inner lists... but is flattened already".to_string(), + "value originates from here".into(), + s, + *span + ))} + ]; + } + // it's a table (a list of record, we can flatten inner record) + let mut records = vec![]; + + for v in vals { + if let Ok(r) = v.as_record() { + records.push(r) + } + } + + if need_flatten { + let cols = records.iter().map(|r| r.cols.clone()); + let vals = records.iter().map(|r| r.vals.clone()); + + inner_table = Some(TableInside::FlattenedRows { + columns: cols.collect(), + _span: &s, + values: vals.collect(), + parent_column_name: column, + parent_column_index: column_index, + }); + } else if out.contains_key(column) { + out.insert(format!("{column}_{column}"), value.clone()); + } else { + out.insert(column.to_string(), value.clone()); + } + } + Value::List { vals: values, span } => { + if need_flatten && inner_table.is_some() { + return vec![Value::Error{ error: Box::new(ShellError::UnsupportedInput( + "can only flatten one inner list at a time. tried flattening more than one column with inner lists... but is flattened already".to_string(), + "value originates from here".into(), + s, + *span + ))} + ]; + } + + if !columns.is_empty() { + let cell_path = column_requested.and_then(|x| match x.members.first() { + Some(PathMember::String { val, span: _, .. }) => Some(val), + _ => None, + }); + + if let Some(r) = cell_path { inner_table = Some(TableInside::Entries( - column, + r, &s, values.iter().collect::>(), column_index, )); + } else { + out.insert(column.to_string(), value.clone()); } + } else { + inner_table = Some(TableInside::Entries( + column, + &s, + values.iter().collect::>(), + column_index, + )); } - _ => { - out.insert(column.to_string(), value.clone()); - } + } + _ => { + out.insert(column.to_string(), value.clone()); } } + } - let mut expanded = vec![]; - match inner_table { - Some(TableInside::Entries(column, _, entries, parent_column_index)) => { - for entry in entries { - let base = out.clone(); - let (mut record_cols, mut record_vals) = (vec![], vec![]); - let mut index = 0; - for (col, val) in base.into_iter() { - // meet the flattened column, push them to result record first - // this can avoid output column order changed. - if index == parent_column_index { - record_cols.push(column.to_string()); - record_vals.push(entry.clone()); - } - record_cols.push(col); - record_vals.push(val); - index += 1; - } - // the flattened column may be the last column in the original table. + let mut expanded = vec![]; + match inner_table { + Some(TableInside::Entries(column, _, entries, parent_column_index)) => { + for entry in entries { + let base = out.clone(); + let mut record = Record::new(); + let mut index = 0; + for (col, val) in base.into_iter() { + // meet the flattened column, push them to result record first + // this can avoid output column order changed. if index == parent_column_index { - record_cols.push(column.to_string()); - record_vals.push(entry.clone()); + record.push(column, entry.clone()); } - let record = Value::Record { - cols: record_cols, - vals: record_vals, - span: tag, - }; - expanded.push(record); + record.push(col, val); + index += 1; } + // the flattened column may be the last column in the original table. + if index == parent_column_index { + record.push(column, entry.clone()); + } + expanded.push(Value::record(record, tag)); } - Some(TableInside::FlattenedRows { - columns, - _span, - values, - parent_column_name, - parent_column_index, - }) => { - for (inner_cols, inner_vals) in columns.into_iter().zip(values) { - let base = out.clone(); - let (mut record_cols, mut record_vals) = (vec![], vec![]); - let mut index = 0; + } + Some(TableInside::FlattenedRows { + columns, + _span, + values, + parent_column_name, + parent_column_index, + }) => { + for (inner_cols, inner_vals) in columns.into_iter().zip(values) { + let base = out.clone(); + let mut record = Record::new(); + let mut index = 0; - for (base_col, base_val) in base.into_iter() { - // meet the flattened column, push them to result record first - // this can avoid output column order changed. - if index == parent_column_index { - for (col, val) in inner_cols.iter().zip(inner_vals.iter()) { - if record_cols.contains(col) { - record_cols.push(format!("{parent_column_name}_{col}")); - } else { - record_cols.push(col.to_string()); - } - record_vals.push(val.clone()); - } - } - - record_cols.push(base_col); - record_vals.push(base_val); - index += 1; - } - - // the flattened column may be the last column in the original table. + for (base_col, base_val) in base.into_iter() { + // meet the flattened column, push them to result record first + // this can avoid output column order changed. if index == parent_column_index { for (col, val) in inner_cols.iter().zip(inner_vals.iter()) { - if record_cols.contains(col) { - record_cols.push(format!("{parent_column_name}_{col}")); + if record.cols.contains(col) { + record.push(format!("{parent_column_name}_{col}"), val.clone()); } else { - record_cols.push(col.to_string()); - } - record_vals.push(val.clone()); + record.push(col, val.clone()); + }; } } - let record = Value::Record { - cols: record_cols, - vals: record_vals, - span: tag, - }; - expanded.push(record); + + record.push(base_col, base_val); + index += 1; } - } - None => { - let record = Value::Record { - cols: out.keys().map(|f| f.to_string()).collect::>(), - vals: out.values().cloned().collect(), - span: tag, - }; - expanded.push(record); + + // the flattened column may be the last column in the original table. + if index == parent_column_index { + for (col, val) in inner_cols.iter().zip(inner_vals.iter()) { + if record.cols.contains(col) { + record.push(format!("{parent_column_name}_{col}"), val.clone()); + } else { + record.push(col, val.clone()); + } + } + } + expanded.push(Value::record(record, tag)); } } - expanded - } else if item.as_list().is_ok() { - if let Value::List { vals, span: _ } = item { - vals.to_vec() - } else { - vec![] + None => { + expanded.push(Value::record(out.into_iter().collect(), tag)); } - } else { - vec![item.clone()] } - }; - res + expanded + } else if item.as_list().is_ok() { + if let Value::List { vals, span: _ } = item { + vals.to_vec() + } else { + vec![] + } + } else { + vec![item.clone()] + } } #[cfg(test)] diff --git a/crates/nu-command/src/filters/group_by.rs b/crates/nu-command/src/filters/group_by.rs index 40317815f3..1ca15b2daf 100644 --- a/crates/nu-command/src/filters/group_by.rs +++ b/crates/nu-command/src/filters/group_by.rs @@ -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, span: Span) -> Result { @@ -197,15 +193,13 @@ pub fn group_no_grouper(values: Vec, span: Span) -> Result Result { 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, Vec), 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)| { diff --git a/crates/nu-command/src/filters/insert.rs b/crates/nu-command/src/filters/insert.rs index 14c4a65cad..a52b692ef5 100644 --- a/crates/nu-command/src/filters/insert.rs +++ b/crates/nu-command/src/filters/insert.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember}; use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, - ShellError, Signature, Span, SyntaxShape, Type, Value, + Record, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -60,51 +60,56 @@ impl Command for Insert { vec![Example { description: "Insert a new entry into a single record", example: "{'name': 'nu', 'stars': 5} | insert alias 'Nushell'", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["name".into(), "stars".into(), "alias".into()], vals: vec![ Value::test_string("nu"), Value::test_int(5), Value::test_string("Nushell"), ], - span: Span::test_data(), - }), + })), }, Example { description: "Insert a new column into a table, populating all rows", example: "[[project, lang]; ['Nushell', 'Rust']] | insert type 'shell'", - result: Some(Value::List { vals: vec![Value::Record { cols: vec!["project".into(), "lang".into(), "type".into()], - vals: vec![Value::test_string("Nushell"), Value::test_string("Rust"), Value::test_string("shell")], span: Span::test_data()}], span: Span::test_data()}), + result: Some(Value::List { + vals: vec![Value::test_record(Record { + cols: vec!["project".into(), "lang".into(), "type".into()], + vals: vec![Value::test_string("Nushell"), Value::test_string("Rust"), Value::test_string("shell")], + })], + span: Span::test_data(), + }), }, Example { description: "Insert a column with values equal to their row index, plus the value of 'foo' in each row", example: "[[foo]; [7] [8] [9]] | enumerate | insert bar {|e| $e.item.foo + $e.index } | flatten", result: Some(Value::List { - vals: vec![Value::Record { - cols: vec!["index".into(), "foo".into(), "bar".into()], - vals: vec![ - Value::test_int(0), - Value::test_int(7), - Value::test_int(7), - ], - span: Span::test_data(), - }, Value::Record { - cols: vec!["index".into(),"foo".into(), "bar".into()], - vals: vec![ - Value::test_int(1), - Value::test_int(8), - Value::test_int(9), - ], - span: Span::test_data(), - }, Value::Record { - cols: vec!["index".into(), "foo".into(), "bar".into()], - vals: vec![ - Value::test_int(2), - Value::test_int(9), - Value::test_int(11), - ], - span: Span::test_data(), - }], + vals: vec![ + Value::test_record(Record { + cols: vec!["index".into(), "foo".into(), "bar".into()], + vals: vec![ + Value::test_int(0), + Value::test_int(7), + Value::test_int(7), + ], + }), + Value::test_record(Record { + cols: vec!["index".into(),"foo".into(), "bar".into()], + vals: vec![ + Value::test_int(1), + Value::test_int(8), + Value::test_int(9), + ], + }), + Value::test_record(Record { + cols: vec!["index".into(), "foo".into(), "bar".into()], + vals: vec![ + Value::test_int(2), + Value::test_int(9), + Value::test_int(11), + ], + }), + ], span: Span::test_data(), }), }] diff --git a/crates/nu-command/src/filters/items.rs b/crates/nu-command/src/filters/items.rs index a0f90465b6..3e0a9337d7 100644 --- a/crates/nu-command/src/filters/items.rs +++ b/crates/nu-command/src/filters/items.rs @@ -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 diff --git a/crates/nu-command/src/filters/join.rs b/crates/nu-command/src/filters/join.rs index 48ea524cd6..d7643e5680 100644 --- a/crates/nu-command/src/filters/join.rs +++ b/crates/nu-command/src/filters/join.rs @@ -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, &'a Vec)>; - const EMPTY_COL_NAMES: &Vec = &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, this: &Vec, this_join_key: &str, - other: HashMap, - other_keys: &Vec, + other: HashMap>, + 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 { 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> { - let mut map = HashMap::::with_capacity(cap); +) -> HashMap> { + let mut map = HashMap::>::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, &Vec), - right: (&Vec, &Vec), - shared_key: Option<&str>, -) -> (Vec, Vec) { - 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)] diff --git a/crates/nu-command/src/filters/merge.rs b/crates/nu-command/src/filters/merge.rs index ea7d0092b5..06a18985cd 100644 --- a/crates/nu-command/src/filters/merge.rs +++ b/crates/nu-command/src/filters/merge.rs @@ -2,8 +2,8 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, SyntaxShape, Type, Value, + Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Record, + ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -48,18 +48,18 @@ repeating this process with row 1, and so on."# description: "Add an 'index' column to the input table", result: Some(Value::List { vals: vec![ - Value::test_record( - vec!["name", "index"], - vec![Value::test_string("a"), Value::test_int(1)], - ), - Value::test_record( - vec!["name", "index"], - vec![Value::test_string("b"), Value::test_int(2)], - ), - Value::test_record( - vec!["name", "index"], - vec![Value::test_string("c"), Value::test_int(3)], - ), + Value::test_record(Record { + cols: vec!["name".to_string(), "index".to_string()], + vals: vec![Value::test_string("a"), Value::test_int(1)], + }), + Value::test_record(Record { + cols: vec!["name".to_string(), "index".to_string()], + vals: vec![Value::test_string("b"), Value::test_int(2)], + }), + Value::test_record(Record { + cols: vec!["name".to_string(), "index".to_string()], + vals: vec![Value::test_string("c"), Value::test_int(3)], + }), ], span: Span::test_data(), }), @@ -67,20 +67,19 @@ repeating this process with row 1, and so on."# Example { example: "{a: 1, b: 2} | merge {c: 3}", description: "Merge two records", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string(), "c".to_string()], vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)], - span: Span::test_data(), - }), + })), }, Example { example: "[{columnA: A0 columnB: B0}] | merge [{columnA: 'A0*'}]", description: "Merge two tables, overwriting overlapping columns", result: Some(Value::List { - vals: vec![Value::test_record( - vec!["columnA", "columnB"], - vec![Value::test_string("A0*"), Value::test_string("B0")], - )], + vals: vec![Value::test_record(Record { + cols: vec!["columnA".to_string(), "columnB".to_string()], + vals: vec![Value::test_string("A0*"), Value::test_string("B0")], + })], span: Span::test_data(), }), }, @@ -112,24 +111,12 @@ repeating this process with row 1, and so on."# input .into_iter() .map(move |inp| match (inp.as_record(), table_iter.next()) { - (Ok((inp_cols, inp_vals)), Some(to_merge)) => { - match to_merge.as_record() { - Ok((to_merge_cols, to_merge_vals)) => { - let (cols, vals) = do_merge( - (inp_cols.to_vec(), inp_vals.to_vec()), - (to_merge_cols.to_vec(), to_merge_vals.to_vec()), - ); - Value::Record { - cols, - vals, - span: call.head, - } - } - Err(error) => Value::Error { - error: Box::new(error), - }, - } - } + (Ok(inp), Some(to_merge)) => match to_merge.as_record() { + Ok(to_merge) => Value::record(do_merge(inp, to_merge), call.head), + Err(error) => Value::Error { + error: Box::new(error), + }, + }, (_, None) => inp, (Err(error), _) => Value::Error { error: Box::new(error), @@ -144,31 +131,9 @@ repeating this process with row 1, and so on."# } // record ( - PipelineData::Value( - Value::Record { - cols: inp_cols, - vals: inp_vals, - .. - }, - .., - ), - Value::Record { - cols: to_merge_cols, - vals: to_merge_vals, - .. - }, - ) => { - let (cols, vals) = do_merge( - (inp_cols.to_vec(), inp_vals.to_vec()), - (to_merge_cols.to_vec(), to_merge_vals.to_vec()), - ); - Ok(Value::Record { - cols, - vals, - span: call.head, - } - .into_pipeline_data()) - } + PipelineData::Value(Value::Record { val: inp, .. }, ..), + Value::Record { val: to_merge, .. }, + ) => Ok(Value::record(do_merge(inp, &to_merge), call.head).into_pipeline_data()), (PipelineData::Value(val, ..), ..) => { // Only point the "value originates here" arrow at the merge value // if it was generated from a block. Otherwise, point at the pipeline value. -Leon 2022-10-27 @@ -194,27 +159,22 @@ repeating this process with row 1, and so on."# } } -fn do_merge( - input_record: (Vec, Vec), - to_merge_record: (Vec, Vec), -) -> (Vec, Vec) { - 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)] diff --git a/crates/nu-command/src/filters/move_.rs b/crates/nu-command/src/filters/move_.rs index cb1d5ce498..30593f91cd 100644 --- a/crates/nu-command/src/filters/move_.rs +++ b/crates/nu-command/src/filters/move_.rs @@ -2,8 +2,8 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, Spanned, SyntaxShape, Type, Value, + Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Record, + ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; #[derive(Clone, Debug)] @@ -54,18 +54,18 @@ impl Command for Move { result: Some(Value::List { vals: vec![ - Value::test_record( - vec!["index", "name", "value"], - vec![Value::test_int(1), Value::test_string("foo"), Value::test_string("a")], - ), - Value::test_record( - vec!["index", "name", "value"], - vec![Value::test_int(2), Value::test_string("bar"), Value::test_string("b")], - ), - Value::test_record( - vec!["index", "name", "value"], - vec![Value::test_int(3), Value::test_string("baz"), Value::test_string("c")], - ), + Value::test_record(Record { + cols: vec!["index".to_string(), "name".to_string(), "value".to_string()], + vals: vec![Value::test_int(1), Value::test_string("foo"), Value::test_string("a")], + }), + Value::test_record(Record { + cols: vec!["index".to_string(), "name".to_string(), "value".to_string()], + vals: vec![Value::test_int(2), Value::test_string("bar"), Value::test_string("b")], + }), + Value::test_record(Record { + cols: vec!["index".to_string(), "name".to_string(), "value".to_string()], + vals: vec![Value::test_int(3), Value::test_string("baz"), Value::test_string("c")], + }), ], span: Span::test_data(), }) @@ -76,18 +76,18 @@ impl Command for Move { result: Some(Value::List { vals: vec![ - Value::test_record( - vec!["index", "value", "name"], - vec![Value::test_int(1), Value::test_string("a"), Value::test_string("foo")], - ), - Value::test_record( - vec!["index", "value", "name"], - vec![Value::test_int(2), Value::test_string("b"), Value::test_string("bar")], - ), - Value::test_record( - vec!["index", "value", "name"], - vec![Value::test_int(3), Value::test_string("c"), Value::test_string("baz")], - ), + Value::test_record(Record { + cols: vec!["index".to_string(), "value".to_string(), "name".to_string()], + vals: vec![Value::test_int(1), Value::test_string("a"), Value::test_string("foo")], + }), + Value::test_record(Record { + cols: vec!["index".to_string(), "value".to_string(), "name".to_string()], + vals: vec![Value::test_int(2), Value::test_string("b"), Value::test_string("bar")], + }), + Value::test_record(Record { + cols: vec!["index".to_string(), "value".to_string(), "name".to_string()], + vals: vec![Value::test_int(3), Value::test_string("c"), Value::test_string("baz")], + }), ], span: Span::test_data(), }) @@ -95,10 +95,10 @@ impl Command for Move { Example { example: "{ name: foo, value: a, index: 1 } | move name --before index", description: "Move columns of a record", - result: Some(Value::test_record( - vec!["value", "name", "index"], - vec![Value::test_string("a"), Value::test_string("foo"), Value::test_int(1)], - )) + result: Some(Value::test_record(Record { + cols: vec!["value".to_string(), "name".to_string(), "index".to_string()], + vals: vec![Value::test_string("a"), Value::test_string("foo"), Value::test_int(1)], + })) }, ] } @@ -150,18 +150,14 @@ impl Command for Move { match input { PipelineData::Value(Value::List { .. }, ..) | PipelineData::ListStream { .. } => { let res = input.into_iter().map(move |x| match x.as_record() { - Ok((inp_cols, inp_vals)) => match move_record_columns( - inp_cols, - inp_vals, - &columns, - &before_or_after, - call.head, - ) { - Ok(val) => val, - Err(error) => Value::Error { - error: Box::new(error), - }, - }, + Ok(record) => { + match move_record_columns(record, &columns, &before_or_after, call.head) { + Ok(val) => val, + Err(error) => Value::Error { + error: Box::new(error), + }, + } + } Err(error) => Value::Error { error: Box::new(error), }, @@ -173,21 +169,12 @@ impl Command for Move { Ok(res.into_pipeline_data(ctrlc)) } } - PipelineData::Value( - Value::Record { - cols: inp_cols, - vals: inp_vals, - .. - }, - .., - ) => Ok(move_record_columns( - &inp_cols, - &inp_vals, - &columns, - &before_or_after, - call.head, - )? - .into_pipeline_data()), + PipelineData::Value(Value::Record { val, .. }, ..) => { + Ok( + move_record_columns(&val, &columns, &before_or_after, call.head)? + .into_pipeline_data(), + ) + } _ => Err(ShellError::PipelineMismatch { exp_input_type: "record or table".to_string(), dst_span: call.head, @@ -199,8 +186,7 @@ impl Command for Move { // Move columns within a record fn move_record_columns( - inp_cols: &[String], - inp_vals: &[Value], + record: &Record, columns: &[Value], before_or_after: &Spanned, 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 = Vec::with_capacity(inp_cols.len()); - let mut out_vals: Vec = 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)] diff --git a/crates/nu-command/src/filters/reject.rs b/crates/nu-command/src/filters/reject.rs index 1ef3c6994e..a86bf91442 100644 --- a/crates/nu-command/src/filters/reject.rs +++ b/crates/nu-command/src/filters/reject.rs @@ -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(), - }), + })], + })), }, ] } diff --git a/crates/nu-command/src/filters/rename.rs b/crates/nu-command/src/filters/rename.rs index 0e65789f34..546ca55e9d 100644 --- a/crates/nu-command/src/filters/rename.rs +++ b/crates/nu-command/src/filters/rename.rs @@ -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(), diff --git a/crates/nu-command/src/filters/reverse.rs b/crates/nu-command/src/filters/reverse.rs index b8fad219c5..6a75ec569f 100644 --- a/crates/nu-command/src/filters/reverse.rs +++ b/crates/nu-command/src/filters/reverse.rs @@ -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(), }), diff --git a/crates/nu-command/src/filters/select.rs b/crates/nu-command/src/filters/select.rs index a3eb99ef9f..edd40b2d0b 100644 --- a/crates/nu-command/src/filters/select.rs +++ b/crates/nu-command/src/filters/select.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, - PipelineIterator, ShellError, Signature, Span, SyntaxShape, Type, Value, + PipelineIterator, Record, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use std::collections::HashSet; @@ -152,14 +152,20 @@ produce a table, a list will produce a list, and a record will produce a record. description: "Select a column in a table", example: "[{a: a b: b}] | select a", result: Some(Value::List { - vals: vec![Value::test_record(vec!["a"], vec![Value::test_string("a")])], + vals: vec![Value::test_record(Record { + cols: vec!["a".to_string()], + vals: vec![Value::test_string("a")] + })], span: Span::test_data(), }), }, Example { description: "Select a field in a record", example: "{a: a b: b} | select a", - result: Some(Value::test_record(vec!["a"], vec![Value::test_string("a")])), + result: Some(Value::test_record(Record { + cols: vec!["a".to_string()], + vals: vec![Value::test_string("a")] + })), }, Example { description: "Select just the `name` column", @@ -256,14 +262,12 @@ fn select( let mut columns_with_value = Vec::new(); for input_val in input_vals { if !columns.is_empty() { - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); for path in &columns { //FIXME: improve implementation to not clone match input_val.clone().follow_cell_path(&path.members, false) { Ok(fetcher) => { - cols.push(path.into_string().replace('.', "_")); - vals.push(fetcher); + record.push(path.into_string().replace('.', "_"), fetcher); if !columns_with_value.contains(&path) { columns_with_value.push(path); } @@ -274,7 +278,7 @@ fn select( } } - output.push(Value::Record { cols, vals, span }) + output.push(Value::record(record, span)) } else { output.push(input_val) } @@ -290,23 +294,17 @@ fn select( for x in stream { if !columns.is_empty() { - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); for path in &columns { //FIXME: improve implementation to not clone match x.clone().follow_cell_path(&path.members, false) { Ok(value) => { - cols.push(path.into_string().replace('.', "_")); - vals.push(value); + record.push(path.into_string().replace('.', "_"), value); } Err(e) => return Err(e), } } - values.push(Value::Record { - cols, - vals, - span: call_span, - }); + values.push(Value::record(record, call_span)); } else { values.push(x); } @@ -318,27 +316,21 @@ fn select( } PipelineData::Value(v, metadata, ..) => { if !columns.is_empty() { - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); for cell_path in columns { // FIXME: remove clone match v.clone().follow_cell_path(&cell_path.members, false) { Ok(result) => { - cols.push(cell_path.into_string().replace('.', "_")); - vals.push(result); + record.push(cell_path.into_string().replace('.', "_"), result); } Err(e) => return Err(e), } } - Ok(Value::Record { - cols, - vals, - span: call_span, - } - .into_pipeline_data() - .set_metadata(metadata)) + Ok(Value::record(record, call_span) + .into_pipeline_data() + .set_metadata(metadata)) } else { Ok(v.into_pipeline_data().set_metadata(metadata)) } diff --git a/crates/nu-command/src/filters/skip/skip_.rs b/crates/nu-command/src/filters/skip/skip_.rs index 1274f96d5b..f44d3d5a83 100644 --- a/crates/nu-command/src/filters/skip/skip_.rs +++ b/crates/nu-command/src/filters/skip/skip_.rs @@ -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(), }), }, diff --git a/crates/nu-command/src/filters/skip/skip_until.rs b/crates/nu-command/src/filters/skip/skip_until.rs index d0eea13caf..1662809bb3 100644 --- a/crates/nu-command/src/filters/skip/skip_until.rs +++ b/crates/nu-command/src/filters/skip/skip_until.rs @@ -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(), }), diff --git a/crates/nu-command/src/filters/skip/skip_while.rs b/crates/nu-command/src/filters/skip/skip_while.rs index 1512fa0508..e5f78091da 100644 --- a/crates/nu-command/src/filters/skip/skip_while.rs +++ b/crates/nu-command/src/filters/skip/skip_while.rs @@ -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(), }), diff --git a/crates/nu-command/src/filters/sort.rs b/crates/nu-command/src/filters/sort.rs index a6a3944e97..a8775f99eb 100644 --- a/crates/nu-command/src/filters/sort.rs +++ b/crates/nu-command/src/filters/sort.rs @@ -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, - vals: Vec, + 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)); } _ => { diff --git a/crates/nu-command/src/filters/sort_by.rs b/crates/nu-command/src/filters/sort_by.rs index 7e1b048300..a53375f728 100644 --- a/crates/nu-command/src/filters/sort_by.rs +++ b/crates/nu-command/src/filters/sort_by.rs @@ -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(), }), diff --git a/crates/nu-command/src/filters/split_by.rs b/crates/nu-command/src/filters/split_by.rs index af3175e3d8..a62e26aa5f 100644 --- a/crates/nu-command/src/filters/split_by.rs +++ b/crates/nu-command/src/filters/split_by.rs @@ -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 { 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 = - 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)] diff --git a/crates/nu-command/src/filters/take/take_.rs b/crates/nu-command/src/filters/take/take_.rs index 1c75b40e38..53f066cb77 100644 --- a/crates/nu-command/src/filters/take/take_.rs +++ b/crates/nu-command/src/filters/take/take_.rs @@ -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(), }), diff --git a/crates/nu-command/src/filters/take/take_until.rs b/crates/nu-command/src/filters/take/take_until.rs index 1f9a94bf5e..cf3be0a6d3 100644 --- a/crates/nu-command/src/filters/take/take_until.rs +++ b/crates/nu-command/src/filters/take/take_until.rs @@ -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(), }), diff --git a/crates/nu-command/src/filters/take/take_while.rs b/crates/nu-command/src/filters/take/take_while.rs index 7fd0d9fbdb..b33374f09f 100644 --- a/crates/nu-command/src/filters/take/take_while.rs +++ b/crates/nu-command/src/filters/take/take_while.rs @@ -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(), }), diff --git a/crates/nu-command/src/filters/transpose.rs b/crates/nu-command/src/filters/transpose.rs index 0116400ea0..822ae61879 100644 --- a/crates/nu-command/src/filters/transpose.rs +++ b/crates/nu-command/src/filters/transpose.rs @@ -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::>(); if result_data.len() == 1 && args.as_record { diff --git a/crates/nu-command/src/filters/uniq.rs b/crates/nu-command/src/filters/uniq.rs index 7b866e1de5..ac37b9b7b3 100644 --- a/crates/nu-command/src/filters/uniq.rs +++ b/crates/nu-command/src/filters/uniq.rs @@ -3,8 +3,8 @@ use itertools::Itertools; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, PipelineMetadata, ShellError, Signature, - Span, Type, Value, + record, Category, Example, IntoPipelineData, PipelineData, PipelineMetadata, Record, + ShellError, Signature, Span, Type, Value, }; use std::collections::hash_map::IntoIter; use std::collections::HashMap; @@ -122,16 +122,14 @@ impl Command for Uniq { example: "[1 2 2] | uniq -c", result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["value".to_string(), "count".to_string()], vals: vec![Value::test_int(1), Value::test_int(1)], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["value".to_string(), "count".to_string()], vals: vec![Value::test_int(2), Value::test_int(2)], - span: Span::test_data(), - }, + }), ], span: Span::test_data(), }), @@ -193,32 +191,25 @@ fn clone_to_lowercase(value: &Value) -> Value { span: *span, }, Value::List { vals: vec, span } => Value::List { - vals: vec - .clone() - .into_iter() - .map(|v| clone_to_lowercase(&v)) - .collect(), - span: *span, - }, - Value::Record { cols, vals, span } => Value::Record { - cols: cols.clone(), - vals: vals - .clone() - .into_iter() - .map(|v| clone_to_lowercase(&v)) - .collect(), + vals: vec.iter().map(clone_to_lowercase).collect(), span: *span, }, + Value::Record { val: record, span } => Value::record( + Record { + cols: record.cols.clone(), + vals: record.vals.iter().map(clone_to_lowercase).collect(), + }, + *span, + ), other => other.clone(), } } fn sort_attributes(val: Value) -> Value { match val { - Value::Record { cols, vals, span } => { - let sorted = cols + Value::Record { val, span } => { + let sorted = val .into_iter() - .zip(vals) .sorted_by(|a, b| a.0.cmp(&b.0)) .collect_vec(); @@ -228,11 +219,13 @@ fn sort_attributes(val: Value) -> Value { .map(|a| sort_attributes(a.1)) .collect_vec(); - Value::Record { - cols: sorted_cols, - vals: sorted_vals, + Value::record( + Record { + cols: sorted_cols, + vals: sorted_vals, + }, span, - } + ) } Value::List { vals, span } => Value::List { vals: vals.into_iter().map(sort_attributes).collect_vec(), @@ -250,10 +243,14 @@ fn generate_key(item: &ValueCounter) -> Result { fn generate_results_with_count(head: Span, uniq_values: Vec) -> Vec { 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() } diff --git a/crates/nu-command/src/filters/uniq_by.rs b/crates/nu-command/src/filters/uniq_by.rs index 16bbd3b497..ea86a1261e 100644 --- a/crates/nu-command/src/filters/uniq_by.rs +++ b/crates/nu-command/src/filters/uniq_by.rs @@ -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, columns: &Vec, 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, columns: &Vec, 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, diff --git a/crates/nu-command/src/filters/update.rs b/crates/nu-command/src/filters/update.rs index 7743252f59..0e7a2137a4 100644 --- a/crates/nu-command/src/filters/update.rs +++ b/crates/nu-command/src/filters/update.rs @@ -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(), + }), } ] } diff --git a/crates/nu-command/src/filters/upsert.rs b/crates/nu-command/src/filters/upsert.rs index cbabfe2807..912d64be9e 100644 --- a/crates/nu-command/src/filters/upsert.rs +++ b/crates/nu-command/src/filters/upsert.rs @@ -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", diff --git a/crates/nu-command/src/filters/values.rs b/crates/nu-command/src/filters/values.rs index 7225290daf..26608e9f15 100644 --- a/crates/nu-command/src/filters/values.rs +++ b/crates/nu-command/src/filters/values.rs @@ -110,8 +110,8 @@ pub fn get_values<'a>( for item in input { match item { - Value::Record { cols, vals, .. } => { - for (k, v) in cols.iter().zip(vals.iter()) { + Value::Record { val, .. } => { + for (k, v) in val { if let Some(vec) = output.get_mut(k) { vec.push(v.clone()); } else { @@ -172,8 +172,8 @@ fn values( Err(err) => Err(err), } } - PipelineData::Value(Value::Record { vals, .. }, ..) => { - Ok(vals.into_pipeline_data(ctrlc).set_metadata(metadata)) + PipelineData::Value(Value::Record { val, .. }, ..) => { + Ok(val.vals.into_pipeline_data(ctrlc).set_metadata(metadata)) } // Propagate errors PipelineData::Value(Value::Error { error }, ..) => Err(*error), diff --git a/crates/nu-command/src/filters/where_.rs b/crates/nu-command/src/filters/where_.rs index e8d9b0de7b..f3493dd075 100644 --- a/crates/nu-command/src/filters/where_.rs +++ b/crates/nu-command/src/filters/where_.rs @@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, SyntaxShape, Type, Value, + Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Record, + ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -114,11 +114,10 @@ not supported."# description: "Filter rows of a table according to a condition", example: "[{a: 1} {a: 2}] | where a > 1", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["a".to_string()], vals: vec![Value::test_int(2)], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }, diff --git a/crates/nu-command/src/filters/wrap.rs b/crates/nu-command/src/filters/wrap.rs index 64f5be8df4..721c4bc127 100644 --- a/crates/nu-command/src/filters/wrap.rs +++ b/crates/nu-command/src/filters/wrap.rs @@ -2,8 +2,8 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, SyntaxShape, Type, Value, + record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, + Record, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -47,27 +47,18 @@ impl Command for Wrap { | PipelineData::Value(Value::List { .. }, ..) | PipelineData::ListStream { .. } => Ok(input .into_iter() - .map(move |x| Value::Record { - cols: vec![name.clone()], - vals: vec![x], - span, - }) + .map(move |x| Value::record(record! { name.clone() => x }, span)) .into_pipeline_data(engine_state.ctrlc.clone()) .set_metadata(metadata)), - PipelineData::ExternalStream { .. } => Ok(Value::Record { - cols: vec![name], - vals: vec![input.into_value(call.head)], + PipelineData::ExternalStream { .. } => Ok(Value::record( + record! { name => input.into_value(call.head) }, span, - } - .into_pipeline_data() - .set_metadata(metadata)), - PipelineData::Value(input, ..) => Ok(Value::Record { - cols: vec![name], - vals: vec![input], - span, - } + ) .into_pipeline_data() .set_metadata(metadata)), + PipelineData::Value(input, ..) => Ok(Value::record(record! { name => input }, span) + .into_pipeline_data() + .set_metadata(metadata)), } } @@ -78,21 +69,18 @@ impl Command for Wrap { example: "[1 2 3] | wrap num", result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["num".into()], vals: vec![Value::test_int(1)], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["num".into()], vals: vec![Value::test_int(2)], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["num".into()], vals: vec![Value::test_int(3)], - span: Span::test_data(), - }, + }), ], span: Span::test_data(), }), @@ -102,21 +90,18 @@ impl Command for Wrap { example: "1..3 | wrap num", result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["num".into()], vals: vec![Value::test_int(1)], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["num".into()], vals: vec![Value::test_int(2)], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["num".into()], vals: vec![Value::test_int(3)], - span: Span::test_data(), - }, + }), ], span: Span::test_data(), }), diff --git a/crates/nu-command/src/formats/from/csv.rs b/crates/nu-command/src/formats/from/csv.rs index ab1b11cb95..74e4bcb160 100644 --- a/crates/nu-command/src/formats/from/csv.rs +++ b/crates/nu-command/src/formats/from/csv.rs @@ -4,7 +4,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, + Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -82,14 +82,13 @@ impl Command for FromCsv { description: "Convert comma-separated data to a table", example: "\"ColA,ColB\n1,2\" | from csv", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["ColA".to_string(), "ColB".to_string()], vals: vec![ Value::test_int(1), Value::test_int(2), ], - span: Span::test_data(), - }], + })], span: Span::test_data(), }) }, diff --git a/crates/nu-command/src/formats/from/delimited.rs b/crates/nu-command/src/formats/from/delimited.rs index e191d9218c..ed100cc0c5 100644 --- a/crates/nu-command/src/formats/from/delimited.rs +++ b/crates/nu-command/src/formats/from/delimited.rs @@ -1,5 +1,5 @@ use csv::{ReaderBuilder, Trim}; -use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Value}; +use nu_protocol::{IntoPipelineData, PipelineData, Record, ShellError, Span, Value}; fn from_delimited_string_to_value( DelimitedReaderConfig { @@ -56,11 +56,13 @@ fn from_delimited_string_to_value( }); } } - rows.push(Value::Record { - cols: headers.clone(), - vals: output_row, + rows.push(Value::record( + Record { + cols: headers.clone(), + vals: output_row, + }, span, - }); + )); } Ok(Value::List { vals: rows, span }) diff --git a/crates/nu-command/src/formats/from/json.rs b/crates/nu-command/src/formats/from/json.rs index 6a5f44f6bb..6ec900399f 100644 --- a/crates/nu-command/src/formats/from/json.rs +++ b/crates/nu-command/src/formats/from/json.rs @@ -1,8 +1,8 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, Type, Value, + Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Record, + ShellError, Signature, Span, Type, Value, }; #[derive(Clone)] @@ -29,16 +29,15 @@ impl Command for FromJson { Example { example: r#"'{ "a": 1 }' | from json"#, description: "Converts json formatted string to table", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string()], vals: vec![Value::test_int(1)], - span: Span::test_data(), - }), + })), }, Example { example: r#"'{ "a": 1, "b": [1, 2] }' | from json"#, description: "Converts json formatted string to table", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![ Value::test_int(1), @@ -47,8 +46,7 @@ impl Command for FromJson { span: Span::test_data(), }, ], - span: Span::test_data(), - }), + })), }, ] } @@ -107,17 +105,12 @@ fn convert_nujson_to_value(value: &nu_json::Value, span: Span) -> Value { nu_json::Value::F64(f) => Value::Float { val: *f, span }, nu_json::Value::I64(i) => Value::Int { val: *i, span }, nu_json::Value::Null => Value::Nothing { span }, - nu_json::Value::Object(k) => { - let mut cols = vec![]; - let mut vals = vec![]; - - for item in k { - cols.push(item.0.clone()); - vals.push(convert_nujson_to_value(item.1, span)); - } - - Value::Record { cols, vals, span } - } + nu_json::Value::Object(k) => Value::record( + k.iter() + .map(|(k, v)| (k.clone(), convert_nujson_to_value(v, span))) + .collect(), + span, + ), nu_json::Value::U64(u) => { if *u > i64::MAX as u64 { Value::Error { diff --git a/crates/nu-command/src/formats/from/nuon.rs b/crates/nu-command/src/formats/from/nuon.rs index ac6d59ec8a..24be4721f2 100644 --- a/crates/nu-command/src/formats/from/nuon.rs +++ b/crates/nu-command/src/formats/from/nuon.rs @@ -1,8 +1,8 @@ use nu_protocol::ast::{Call, Expr, Expression, PipelineElement}; use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, Range, ShellError, Signature, Span, Type, - Unit, Value, + Category, Example, IntoPipelineData, PipelineData, Range, Record, ShellError, Signature, Span, + Type, Unit, Value, }; #[derive(Clone)] pub struct FromNuon; @@ -27,16 +27,15 @@ impl Command for FromNuon { Example { example: "'{ a:1 }' | from nuon", description: "Converts nuon formatted string to table", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string()], vals: vec![Value::test_int(1)], - span: Span::test_data(), - }), + })), }, Example { example: "'{ a:1, b: [1, 2] }' | from nuon", description: "Converts nuon formatted string to table", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![ Value::test_int(1), @@ -45,8 +44,7 @@ impl Command for FromNuon { span: Span::test_data(), }, ], - span: Span::test_data(), - }), + })), }, ] } @@ -323,8 +321,7 @@ fn convert_to_value( }) } Expr::Record(key_vals) => { - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); for (key, val) in key_vals { let key_str = match key.expr { @@ -341,11 +338,10 @@ fn convert_to_value( let value = convert_to_value(val, span, original_text)?; - cols.push(key_str); - vals.push(value); + record.push(key_str, value); } - Ok(Value::Record { cols, vals, span }) + Ok(Value::record(record, span)) } Expr::RowCondition(..) => Err(ShellError::OutsideSpannedLabeledError( original_text.to_string(), @@ -409,11 +405,13 @@ fn convert_to_value( )); } - output.push(Value::Record { - cols: cols.clone(), - vals, + output.push(Value::record( + Record { + cols: cols.clone(), + vals, + }, span, - }); + )); } Ok(Value::List { vals: output, span }) diff --git a/crates/nu-command/src/formats/from/ods.rs b/crates/nu-command/src/formats/from/ods.rs index 41a93a86f5..8e6bbb8e7e 100644 --- a/crates/nu-command/src/formats/from/ods.rs +++ b/crates/nu-command/src/formats/from/ods.rs @@ -133,41 +133,29 @@ fn from_ods( sheet_names.retain(|e| sel_sheets.contains(e)); } - for sheet_name in &sheet_names { + for sheet_name in sheet_names { let mut sheet_output = vec![]; - if let Some(Ok(current_sheet)) = ods.worksheet_range(sheet_name) { + if let Some(Ok(current_sheet)) = ods.worksheet_range(&sheet_name) { for row in current_sheet.rows() { - let mut row_output = IndexMap::new(); - for (i, cell) in row.iter().enumerate() { - let value = match cell { - DataType::Empty => Value::nothing(head), - DataType::String(s) => Value::string(s, head), - DataType::Float(f) => Value::float(*f, head), - DataType::Int(i) => Value::int(*i, head), - DataType::Bool(b) => Value::bool(*b, head), - _ => Value::nothing(head), - }; + let record = row + .iter() + .enumerate() + .map(|(i, cell)| { + let value = match cell { + DataType::Empty => Value::nothing(head), + DataType::String(s) => Value::string(s, head), + DataType::Float(f) => Value::float(*f, head), + DataType::Int(i) => Value::int(*i, head), + DataType::Bool(b) => Value::bool(*b, head), + _ => Value::nothing(head), + }; - row_output.insert(format!("column{i}"), value); - } + (format!("column{i}"), value) + }) + .collect(); - let (cols, vals) = - row_output - .into_iter() - .fold((vec![], vec![]), |mut acc, (k, v)| { - acc.0.push(k); - acc.1.push(v); - acc - }); - - let record = Value::Record { - cols, - vals, - span: head, - }; - - sheet_output.push(record); + sheet_output.push(Value::record(record, head)); } dict.insert( @@ -187,19 +175,10 @@ fn from_ods( } } - let (cols, vals) = dict.into_iter().fold((vec![], vec![]), |mut acc, (k, v)| { - acc.0.push(k.clone()); - acc.1.push(v); - acc - }); - - let record = Value::Record { - cols, - vals, - span: head, - }; - - Ok(PipelineData::Value(record, None)) + Ok(PipelineData::Value( + Value::record(dict.into_iter().collect(), head), + None, + )) } #[cfg(test)] diff --git a/crates/nu-command/src/formats/from/ssv.rs b/crates/nu-command/src/formats/from/ssv.rs index 2b9490a796..9abea4e5bc 100644 --- a/crates/nu-command/src/formats/from/ssv.rs +++ b/crates/nu-command/src/formats/from/ssv.rs @@ -3,8 +3,8 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Type, Value, + Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span, + Spanned, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -44,13 +44,32 @@ impl Command for FromSsv { example: r#"'FOO BAR 1 2' | from ssv"#, description: "Converts ssv formatted string to table", - result: Some(Value::List { vals: vec![Value::Record { cols: vec!["FOO".to_string(), "BAR".to_string()], vals: vec![Value::test_string("1"), Value::test_string("2")], span: Span::test_data() }], span: Span::test_data() }), + result: Some(Value::List { + vals: vec![Value::test_record(Record { + cols: vec!["FOO".to_string(), "BAR".to_string()], + vals: vec![Value::test_string("1"), Value::test_string("2")], + })], + span: Span::test_data(), + }), }, Example { example: r#"'FOO BAR 1 2' | from ssv -n"#, description: "Converts ssv formatted string to table but not treating the first row as column names", result: Some( - Value::List { vals: vec![Value::Record { cols: vec!["column1".to_string(), "column2".to_string()], vals: vec![Value::test_string("FOO"), Value::test_string("BAR")], span: Span::test_data() }, Value::Record { cols: vec!["column1".to_string(), "column2".to_string()], vals: vec![Value::test_string("1"), Value::test_string("2")], span: Span::test_data() }], span: Span::test_data() }), + Value::List { + vals: vec![ + Value::test_record(Record { + cols: vec!["column1".to_string(), "column2".to_string()], + vals: vec![Value::test_string("FOO"), Value::test_string("BAR")], + }), + Value::test_record(Record { + cols: vec!["column1".to_string(), "column2".to_string()], + vals: vec![Value::test_string("1"), Value::test_string("2")], + }), + ], + span: Span::test_data(), + } + ), }] } @@ -251,19 +270,13 @@ fn from_ssv_string_to_value( span: Span, ) -> Value { let rows = string_to_table(s, noheaders, aligned_columns, split_at) - .iter() + .into_iter() .map(|row| { let mut dict = IndexMap::new(); for (col, entry) in row { - dict.insert( - col.to_string(), - Value::String { - val: entry.to_string(), - span, - }, - ); + dict.insert(col, Value::string(entry, span)); } - Value::from(Spanned { item: dict, span }) + Value::record(dict.into_iter().collect(), span) }) .collect(); diff --git a/crates/nu-command/src/formats/from/toml.rs b/crates/nu-command/src/formats/from/toml.rs index 0fc5b7c2a2..4a289b68b0 100644 --- a/crates/nu-command/src/formats/from/toml.rs +++ b/crates/nu-command/src/formats/from/toml.rs @@ -1,7 +1,8 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value, + Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span, Type, + Value, }; #[derive(Clone)] @@ -27,17 +28,16 @@ impl Command for FromToml { Example { example: "'a = 1' | from toml", description: "Converts toml formatted string to record", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string()], vals: vec![Value::test_int(1)], - span: Span::test_data(), - }), + })), }, Example { example: "'a = 1 b = [1, 2]' | from toml", description: "Converts toml formatted string to record", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![ Value::test_int(1), @@ -46,8 +46,7 @@ b = [1, 2]' | from toml", span: Span::test_data(), }, ], - span: Span::test_data(), - }), + })), }, ] } @@ -79,17 +78,12 @@ fn convert_toml_to_value(value: &toml::Value, span: Span) -> Value { toml::Value::Boolean(b) => Value::Bool { val: *b, span }, toml::Value::Float(f) => Value::Float { val: *f, span }, toml::Value::Integer(i) => Value::Int { val: *i, span }, - toml::Value::Table(k) => { - let mut cols = vec![]; - let mut vals = vec![]; - - for item in k { - cols.push(item.0.clone()); - vals.push(convert_toml_to_value(item.1, span)); - } - - Value::Record { cols, vals, span } - } + toml::Value::Table(k) => Value::record( + k.iter() + .map(|(k, v)| (k.clone(), convert_toml_to_value(v, span))) + .collect(), + span, + ), toml::Value::String(s) => Value::String { val: s.clone(), span, diff --git a/crates/nu-command/src/formats/from/tsv.rs b/crates/nu-command/src/formats/from/tsv.rs index 1b77300cff..f649a5c717 100644 --- a/crates/nu-command/src/formats/from/tsv.rs +++ b/crates/nu-command/src/formats/from/tsv.rs @@ -4,7 +4,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, + Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -76,14 +76,13 @@ impl Command for FromTsv { description: "Convert tab-separated data to a table", example: "\"ColA\tColB\n1\t2\" | from tsv", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["ColA".to_string(), "ColB".to_string()], vals: vec![ Value::test_int(1), Value::test_int(2), ], - span: Span::test_data(), - }], + })], span: Span::test_data(), }) }, diff --git a/crates/nu-command/src/formats/from/xlsx.rs b/crates/nu-command/src/formats/from/xlsx.rs index 5074b25653..317cca198f 100644 --- a/crates/nu-command/src/formats/from/xlsx.rs +++ b/crates/nu-command/src/formats/from/xlsx.rs @@ -132,41 +132,29 @@ fn from_xlsx( sheet_names.retain(|e| sel_sheets.contains(e)); } - for sheet_name in &sheet_names { + for sheet_name in sheet_names { let mut sheet_output = vec![]; - if let Some(Ok(current_sheet)) = xlsx.worksheet_range(sheet_name) { + if let Some(Ok(current_sheet)) = xlsx.worksheet_range(&sheet_name) { for row in current_sheet.rows() { - let mut row_output = IndexMap::new(); - for (i, cell) in row.iter().enumerate() { - let value = match cell { - DataType::Empty => Value::nothing(head), - DataType::String(s) => Value::string(s, head), - DataType::Float(f) => Value::float(*f, head), - DataType::Int(i) => Value::int(*i, head), - DataType::Bool(b) => Value::bool(*b, head), - _ => Value::nothing(head), - }; + let record = row + .iter() + .enumerate() + .map(|(i, cell)| { + let value = match cell { + DataType::Empty => Value::nothing(head), + DataType::String(s) => Value::string(s, head), + DataType::Float(f) => Value::float(*f, head), + DataType::Int(i) => Value::int(*i, head), + DataType::Bool(b) => Value::bool(*b, head), + _ => Value::nothing(head), + }; - row_output.insert(format!("column{i}"), value); - } + (format!("column{i}"), value) + }) + .collect(); - let (cols, vals) = - row_output - .into_iter() - .fold((vec![], vec![]), |mut acc, (k, v)| { - acc.0.push(k); - acc.1.push(v); - acc - }); - - let record = Value::Record { - cols, - vals, - span: head, - }; - - sheet_output.push(record); + sheet_output.push(Value::record(record, head)); } dict.insert( @@ -186,19 +174,10 @@ fn from_xlsx( } } - let (cols, vals) = dict.into_iter().fold((vec![], vec![]), |mut acc, (k, v)| { - acc.0.push(k.clone()); - acc.1.push(v); - acc - }); - - let record = Value::Record { - cols, - vals, - span: head, - }; - - Ok(PipelineData::Value(record, None)) + Ok(PipelineData::Value( + Value::record(dict.into_iter().collect(), head), + None, + )) } #[cfg(test)] diff --git a/crates/nu-command/src/formats/from/xml.rs b/crates/nu-command/src/formats/from/xml.rs index f1b0b0bf86..fc7fd93579 100644 --- a/crates/nu-command/src/formats/from/xml.rs +++ b/crates/nu-command/src/formats/from/xml.rs @@ -3,7 +3,7 @@ use indexmap::map::IndexMap; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, Type, + Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span, Type, Value, }; use roxmltree::NodeType; @@ -69,38 +69,46 @@ string. This way content of every tag is always a table and is easier to parse"# Event ' | from xml"#, description: "Converts xml formatted string to record", - result: Some(Value::test_record( - vec![COLUMN_TAG_NAME, COLUMN_ATTRS_NAME, COLUMN_CONTENT_NAME], - vec![ + result: Some(Value::test_record(Record { + cols: vec![ + COLUMN_TAG_NAME.to_string(), + COLUMN_ATTRS_NAME.to_string(), + COLUMN_CONTENT_NAME.to_string(), + ], + vals: vec![ Value::test_string("note"), - Value::test_record(Vec::<&str>::new(), vec![]), + Value::test_record(Record::new()), Value::list( - vec![Value::test_record( - vec![COLUMN_TAG_NAME, COLUMN_ATTRS_NAME, COLUMN_CONTENT_NAME], - vec![ + vec![Value::test_record(Record { + cols: vec![ + COLUMN_TAG_NAME.to_string(), + COLUMN_ATTRS_NAME.to_string(), + COLUMN_CONTENT_NAME.to_string(), + ], + vals: vec![ Value::test_string("remember"), - Value::test_record(Vec::<&str>::new(), vec![]), + Value::test_record(Record::new()), Value::list( - vec![Value::test_record( - vec![ - COLUMN_TAG_NAME, - COLUMN_ATTRS_NAME, - COLUMN_CONTENT_NAME, + vec![Value::test_record(Record { + cols: vec![ + COLUMN_TAG_NAME.to_string(), + COLUMN_ATTRS_NAME.to_string(), + COLUMN_CONTENT_NAME.to_string(), ], - vec![ + vals: vec![ Value::test_nothing(), Value::test_nothing(), Value::test_string("Event"), ], - )], + })], Span::test_data(), ), ], - )], + })], Span::test_data(), ), ], - )), + })), }] } } @@ -116,20 +124,7 @@ fn from_attributes_to_value(attributes: &[roxmltree::Attribute], info: &ParsingI for a in attributes { collected.insert(String::from(a.name()), Value::string(a.value(), info.span)); } - - let (cols, vals) = collected - .into_iter() - .fold((vec![], vec![]), |mut acc, (k, v)| { - acc.0.push(k); - acc.1.push(v); - acc - }); - - Value::Record { - cols, - vals, - span: info.span, - } + Value::record(collected.into_iter().collect(), info.span) } fn element_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> Value { @@ -151,7 +146,7 @@ fn element_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> Value { node.insert(String::from(COLUMN_ATTRS_NAME), attributes); node.insert(String::from(COLUMN_CONTENT_NAME), content); - Value::from(Spanned { item: node, span }) + Value::record(node.into_iter().collect(), span) } fn text_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> Option { @@ -168,9 +163,7 @@ fn text_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> Option { node.insert(String::from(COLUMN_ATTRS_NAME), Value::nothing(span)); node.insert(String::from(COLUMN_CONTENT_NAME), content); - let result = Value::from(Spanned { item: node, span }); - - Some(result) + Some(Value::record(node.into_iter().collect(), span)) } } @@ -188,9 +181,7 @@ fn comment_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> Option { node.insert(String::from(COLUMN_ATTRS_NAME), Value::nothing(span)); node.insert(String::from(COLUMN_CONTENT_NAME), content); - let result = Value::from(Spanned { item: node, span }); - - Some(result) + Some(Value::record(node.into_iter().collect(), span)) } else { None } @@ -213,9 +204,7 @@ fn processing_instruction_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> O node.insert(String::from(COLUMN_ATTRS_NAME), Value::nothing(span)); node.insert(String::from(COLUMN_CONTENT_NAME), content); - let result = Value::from(Spanned { item: node, span }); - - Some(result) + Some(Value::record(node.into_iter().collect(), span)) } else { None } @@ -332,20 +321,18 @@ mod tests { use indexmap::indexmap; use indexmap::IndexMap; - use nu_protocol::{Spanned, Value}; fn string(input: impl Into) -> Value { Value::test_string(input) } fn attributes(entries: IndexMap<&str, &str>) -> Value { - Value::from(Spanned { - item: entries + Value::test_record( + entries .into_iter() .map(|(k, v)| (k.into(), string(v))) - .collect::>(), - span: Span::test_data(), - }) + .collect(), + ) } fn table(list: &[Value]) -> Value { @@ -360,24 +347,28 @@ mod tests { attrs: IndexMap<&str, &str>, content: &[Value], ) -> Value { - Value::from(Spanned { - item: indexmap! { - COLUMN_TAG_NAME.into() => string(tag), - COLUMN_ATTRS_NAME.into() => attributes(attrs), - COLUMN_CONTENT_NAME.into() => table(content), - }, - span: Span::test_data(), + Value::test_record(Record { + cols: vec![ + COLUMN_TAG_NAME.into(), + COLUMN_ATTRS_NAME.into(), + COLUMN_CONTENT_NAME.into(), + ], + vals: vec![string(tag), attributes(attrs), table(content)], }) } fn content_string(value: impl Into) -> Value { - Value::from(Spanned { - item: indexmap! { - COLUMN_TAG_NAME.into() => Value::nothing(Span::test_data()), - COLUMN_ATTRS_NAME.into() => Value::nothing(Span::test_data()), - COLUMN_CONTENT_NAME.into() => string(value), - }, - span: Span::test_data(), + Value::test_record(Record { + cols: vec![ + COLUMN_TAG_NAME.into(), + COLUMN_ATTRS_NAME.into(), + COLUMN_CONTENT_NAME.into(), + ], + vals: vec![ + Value::nothing(Span::test_data()), + Value::nothing(Span::test_data()), + string(value), + ], }) } diff --git a/crates/nu-command/src/formats/from/yaml.rs b/crates/nu-command/src/formats/from/yaml.rs index b20d03f647..d8c80f7232 100644 --- a/crates/nu-command/src/formats/from/yaml.rs +++ b/crates/nu-command/src/formats/from/yaml.rs @@ -3,7 +3,7 @@ use itertools::Itertools; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, Type, + Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span, Type, Value, }; use serde::de::Deserialize; @@ -112,11 +112,8 @@ fn convert_yaml_value_to_nu_value( } } serde_yaml::Value::Mapping(t) => { - let mut collected = Spanned { - // Using an IndexMap ensures consistent ordering - item: IndexMap::new(), - span, - }; + // Using an IndexMap ensures consistent ordering + let mut collected = IndexMap::new(); for (k, v) in t { // A ShellError that we re-use multiple times in the Mapping scenario @@ -128,19 +125,19 @@ fn convert_yaml_value_to_nu_value( ); match (k, v) { (serde_yaml::Value::Number(k), _) => { - collected.item.insert( + collected.insert( k.to_string(), convert_yaml_value_to_nu_value(v, span, val_span)?, ); } (serde_yaml::Value::Bool(k), _) => { - collected.item.insert( + collected.insert( k.to_string(), convert_yaml_value_to_nu_value(v, span, val_span)?, ); } (serde_yaml::Value::String(k), _) => { - collected.item.insert( + collected.insert( k.clone(), convert_yaml_value_to_nu_value(v, span, val_span)?, ); @@ -173,7 +170,7 @@ fn convert_yaml_value_to_nu_value( } } - Value::from(collected) + Value::record(collected.into_iter().collect(), span) } serde_yaml::Value::Tagged(t) => { let tag = &t.tag; @@ -238,30 +235,27 @@ pub fn get_examples() -> Vec> { Example { example: "'a: 1' | from yaml", description: "Converts yaml formatted string to table", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string()], vals: vec![Value::test_int(1)], - span: Span::test_data(), - }), + })), }, Example { example: "'[ a: 1, b: [1, 2] ]' | from yaml", description: "Converts yaml formatted string to table", result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["a".to_string()], vals: vec![Value::test_int(1)], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["b".to_string()], vals: vec![Value::List { vals: vec![Value::test_int(1), Value::test_int(2)], span: Span::test_data(), }], - span: Span::test_data(), - }, + }), ], span: Span::test_data(), }), @@ -294,20 +288,18 @@ mod test { TestCase { description: "Double Curly Braces With Quotes", input: r#"value: "{{ something }}""#, - expected: Ok(Value::Record { + expected: Ok(Value::test_record(Record { cols: vec!["value".to_string()], vals: vec![Value::test_string("{{ something }}")], - span: Span::test_data(), - }), + })), }, TestCase { description: "Double Curly Braces Without Quotes", input: r#"value: {{ something }}"#, - expected: Ok(Value::Record { + expected: Ok(Value::test_record(Record { cols: vec!["value".to_string()], vals: vec![Value::test_string("{{ something }}")], - span: Span::test_data(), - }), + })), }, ]; let config = Config::default(); @@ -359,16 +351,14 @@ mod test { let expected: Result = Ok(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_string("b"), Value::test_string("c")], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_string("g"), Value::test_string("h")], - span: Span::test_data(), - }, + }), ], span: Span::test_data(), }); @@ -388,15 +378,15 @@ mod test { let actual_record = actual_vals[jj].as_record().unwrap(); let expected_record = expected_vals[jj].as_record().unwrap(); - let actual_columns = actual_record.0; - let expected_columns = expected_record.0; + let actual_columns = &actual_record.cols; + let expected_columns = &expected_record.cols; assert_eq!( expected_columns, actual_columns, "record {jj}, iteration {ii}" ); - let actual_vals = actual_record.1; - let expected_vals = expected_record.1; + let actual_vals = &actual_record.vals; + let expected_vals = &expected_record.vals; assert_eq!(expected_vals, actual_vals, "record {jj}, iteration {ii}") } } @@ -412,43 +402,38 @@ mod test { let test_cases: Vec = vec![ TestCase { input: "Key: !Value ${TEST}-Test-role", - expected: Ok(Value::Record { + expected: Ok(Value::test_record(Record { cols: vec!["Key".to_string()], vals: vec![Value::test_string("!Value ${TEST}-Test-role")], - span: Span::test_data(), - }), + })), }, TestCase { input: "Key: !Value test-${TEST}", - expected: Ok(Value::Record { + expected: Ok(Value::test_record(Record { cols: vec!["Key".to_string()], vals: vec![Value::test_string("!Value test-${TEST}")], - span: Span::test_data(), - }), + })), }, TestCase { input: "Key: !Value", - expected: Ok(Value::Record { + expected: Ok(Value::test_record(Record { cols: vec!["Key".to_string()], vals: vec![Value::test_string("!Value")], - span: Span::test_data(), - }), + })), }, TestCase { input: "Key: !True", - expected: Ok(Value::Record { + expected: Ok(Value::test_record(Record { cols: vec!["Key".to_string()], vals: vec![Value::test_string("!True")], - span: Span::test_data(), - }), + })), }, TestCase { input: "Key: !123", - expected: Ok(Value::Record { + expected: Ok(Value::test_record(Record { cols: vec!["Key".to_string()], vals: vec![Value::test_string("!123")], - span: Span::test_data(), - }), + })), }, ]; diff --git a/crates/nu-command/src/formats/to/delimited.rs b/crates/nu-command/src/formats/to/delimited.rs index c15b684d2a..5508773c17 100644 --- a/crates/nu-command/src/formats/to/delimited.rs +++ b/crates/nu-command/src/formats/to/delimited.rs @@ -1,6 +1,6 @@ use csv::{Writer, WriterBuilder}; use nu_cmd_base::formats::to::delimited::merge_descriptors; -use nu_protocol::{Config, IntoPipelineData, PipelineData, ShellError, Span, Value}; +use nu_protocol::{Config, IntoPipelineData, PipelineData, Record, ShellError, Span, Value}; use std::collections::VecDeque; use std::error::Error; @@ -11,9 +11,7 @@ fn from_value_to_delimited_string( head: Span, ) -> Result { match value { - Value::Record { cols, vals, span } => { - record_to_delimited(cols, vals, *span, separator, config, head) - } + Value::Record { val, span } => record_to_delimited(val, *span, separator, config, head), Value::List { vals, span } => table_to_delimited(vals, *span, separator, config, head), // Propagate errors by explicitly matching them before the final case. Value::Error { error } => Err(*error.clone()), @@ -22,8 +20,7 @@ fn from_value_to_delimited_string( } fn record_to_delimited( - cols: &[String], - vals: &[Value], + record: &Record, span: Span, separator: char, config: &Config, @@ -35,7 +32,7 @@ fn record_to_delimited( let mut fields: VecDeque = VecDeque::new(); let mut values: VecDeque = VecDeque::new(); - for (k, v) in cols.iter().zip(vals.iter()) { + for (k, v) in record { fields.push_back(k.clone()); values.push_back(to_string_tagged_value(v, config, head, span)?); diff --git a/crates/nu-command/src/formats/to/json.rs b/crates/nu-command/src/formats/to/json.rs index 54f4cac74f..518f0133b0 100644 --- a/crates/nu-command/src/formats/to/json.rs +++ b/crates/nu-command/src/formats/to/json.rs @@ -134,9 +134,9 @@ pub fn value_to_json_value(v: &Value) -> Result { Value::Binary { val, .. } => { nu_json::Value::Array(val.iter().map(|x| nu_json::Value::U64(*x as u64)).collect()) } - Value::Record { cols, vals, .. } => { + Value::Record { val, .. } => { let mut m = nu_json::Map::new(); - for (k, v) in cols.iter().zip(vals) { + for (k, v) in val { m.insert(k.clone(), value_to_json_value(v)?); } nu_json::Value::Object(m) diff --git a/crates/nu-command/src/formats/to/md.rs b/crates/nu-command/src/formats/to/md.rs index 75fec9615d..192650deba 100644 --- a/crates/nu-command/src/formats/to/md.rs +++ b/crates/nu-command/src/formats/to/md.rs @@ -105,10 +105,7 @@ fn to_md( } fn fragment(input: Value, pretty: bool, config: &Config) -> String { - let headers = match input { - Value::Record { ref cols, .. } => cols.to_owned(), - _ => vec![], - }; + let headers = input.columns(); let mut out = String::new(); if headers.len() == 1 { @@ -207,9 +204,12 @@ pub fn group_by(values: PipelineData, head: Span, config: &Config) -> (PipelineD let mut lists = IndexMap::new(); let mut single_list = false; for val in values { - if let Value::Record { ref cols, .. } = val { + if let Value::Record { + val: ref record, .. + } = val + { lists - .entry(cols.concat()) + .entry(record.cols.concat()) .and_modify(|v: &mut Vec| v.push(val.clone())) .or_insert_with(|| vec![val.clone()]); } else { @@ -329,7 +329,7 @@ fn get_padded_string(text: String, desired_length: usize, padding_character: cha #[cfg(test)] mod tests { use super::*; - use nu_protocol::{Config, IntoPipelineData, Span, Value}; + use nu_protocol::{Config, IntoPipelineData, Record, Span, Value}; fn one(string: &str) -> String { string @@ -351,44 +351,40 @@ mod tests { #[test] fn render_h1() { - let value = Value::Record { + let value = Value::test_record(Record { cols: vec!["H1".to_string()], vals: vec![Value::test_string("Ecuador")], - span: Span::test_data(), - }; + }); assert_eq!(fragment(value, false, &Config::default()), "# Ecuador\n"); } #[test] fn render_h2() { - let value = Value::Record { + let value = Value::test_record(Record { cols: vec!["H2".to_string()], vals: vec![Value::test_string("Ecuador")], - span: Span::test_data(), - }; + }); assert_eq!(fragment(value, false, &Config::default()), "## Ecuador\n"); } #[test] fn render_h3() { - let value = Value::Record { + let value = Value::test_record(Record { cols: vec!["H3".to_string()], vals: vec![Value::test_string("Ecuador")], - span: Span::test_data(), - }; + }); assert_eq!(fragment(value, false, &Config::default()), "### Ecuador\n"); } #[test] fn render_blockquote() { - let value = Value::Record { + let value = Value::test_record(Record { cols: vec!["BLOCKQUOTE".to_string()], vals: vec![Value::test_string("Ecuador")], - span: Span::test_data(), - }; + }); assert_eq!(fragment(value, false, &Config::default()), "> Ecuador\n"); } @@ -397,21 +393,18 @@ mod tests { fn render_table() { let value = Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["country".to_string()], vals: vec![Value::test_string("Ecuador")], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["country".to_string()], vals: vec![Value::test_string("New Zealand")], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["country".to_string()], vals: vec![Value::test_string("USA")], - span: Span::test_data(), - }, + }), ], span: Span::test_data(), }; diff --git a/crates/nu-command/src/formats/to/nuon.rs b/crates/nu-command/src/formats/to/nuon.rs index c087264697..306349b6e8 100644 --- a/crates/nu-command/src/formats/to/nuon.rs +++ b/crates/nu-command/src/formats/to/nuon.rs @@ -209,8 +209,8 @@ pub fn value_to_string( for val in vals { let mut row = vec![]; - if let Value::Record { vals, .. } = val { - for val in vals { + if let Value::Record { val, .. } = val { + for val in &val.vals { row.push(value_to_string_without_quotes( val, span, @@ -259,9 +259,9 @@ pub fn value_to_string( }, value_to_string(&val.to, span, depth + 1, indent)? )), - Value::Record { cols, vals, .. } => { + Value::Record { val, .. } => { let mut collection = vec![]; - for (col, val) in cols.iter().zip(vals) { + for (col, val) in val { collection.push(if needs_quotes(col) { format!( "{idt_po}\"{}\": {}", diff --git a/crates/nu-command/src/formats/to/text.rs b/crates/nu-command/src/formats/to/text.rs index 41c14f052b..8db436a84e 100644 --- a/crates/nu-command/src/formats/to/text.rs +++ b/crates/nu-command/src/formats/to/text.rs @@ -132,14 +132,13 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String { } Value::String { val, .. } => val, Value::List { vals: val, .. } => val - .iter() - .map(|x| local_into_string(x.clone(), ", ", config)) + .into_iter() + .map(|x| local_into_string(x, ", ", config)) .collect::>() .join(separator), - Value::Record { cols, vals, .. } => cols - .iter() - .zip(vals.iter()) - .map(|(x, y)| format!("{}: {}", x, local_into_string(y.clone(), ", ", config))) + Value::Record { val, .. } => val + .into_iter() + .map(|(x, y)| format!("{}: {}", x, local_into_string(y, ", ", config))) .collect::>() .join(separator), Value::LazyRecord { val, .. } => match val.collect() { diff --git a/crates/nu-command/src/formats/to/toml.rs b/crates/nu-command/src/formats/to/toml.rs index 634c345c10..aa2276779b 100644 --- a/crates/nu-command/src/formats/to/toml.rs +++ b/crates/nu-command/src/formats/to/toml.rs @@ -54,9 +54,9 @@ fn helper(engine_state: &EngineState, v: &Value) -> Result toml::Value::String("".to_string()), Value::Float { val, .. } => toml::Value::Float(*val), Value::String { val, .. } => toml::Value::String(val.clone()), - Value::Record { cols, vals, .. } => { + Value::Record { val, .. } => { let mut m = toml::map::Map::new(); - for (k, v) in cols.iter().zip(vals.iter()) { + for (k, v) in val { m.insert(k.clone(), helper(engine_state, v)?); } toml::Value::Table(m) @@ -172,7 +172,6 @@ fn to_toml( #[cfg(test)] mod tests { use super::*; - use nu_protocol::Spanned; #[test] fn test_examples() { @@ -201,10 +200,7 @@ mod tests { ); let tv = value_to_toml_value( &engine_state, - &Value::from(Spanned { - item: m, - span: Span::test_data(), - }), + &Value::record(m.into_iter().collect(), Span::test_data()), Span::test_data(), ) .expect("Expected Ok from valid TOML dictionary"); diff --git a/crates/nu-command/src/formats/to/xml.rs b/crates/nu-command/src/formats/to/xml.rs index aaa2c63e25..796d4a1f61 100644 --- a/crates/nu-command/src/formats/to/xml.rs +++ b/crates/nu-command/src/formats/to/xml.rs @@ -4,8 +4,8 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Type, Value, + Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span, + Spanned, SyntaxShape, Type, Value, }; use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; use std::io::Cursor; @@ -208,9 +208,9 @@ fn to_tag_like( // Allow tag to have no attributes or content for short hand input // alternatives like {tag: a attributes: {} content: []}, {tag: a attribbutes: null // content: null}, {tag: a}. See to_xml_entry for more - let (attr_cols, attr_values) = match attrs { - Value::Record { cols, vals, .. } => (cols, vals), - Value::Nothing { .. } => (Vec::new(), Vec::new()), + let attrs = match attrs { + Value::Record { val, .. } => val, + Value::Nothing { .. } => Record::new(), _ => { return Err(ShellError::CantConvert { to_type: "XML".into(), @@ -234,15 +234,7 @@ fn to_tag_like( } }; - to_tag( - entry_span, - tag, - tag_span, - attr_cols, - attr_values, - content, - writer, - ) + to_tag(entry_span, tag, tag_span, attrs, content, writer) } } @@ -305,8 +297,7 @@ fn to_tag( entry_span: Span, tag: String, tag_span: Span, - attr_cols: Vec, - attr_vals: Vec, + attrs: Record, children: Vec, writer: &mut quick_xml::Writer, ) -> Result<(), ShellError> { @@ -322,7 +313,7 @@ fn to_tag( }); } - let attributes = parse_attributes(attr_cols, attr_vals)?; + let attributes = parse_attributes(attrs)?; let mut open_tag_event = BytesStart::new(tag.clone()); add_attributes(&mut open_tag_event, &attributes); @@ -350,12 +341,9 @@ fn to_tag( }) } -fn parse_attributes( - cols: Vec, - vals: Vec, -) -> Result, ShellError> { +fn parse_attributes(attrs: Record) -> Result, ShellError> { let mut h = IndexMap::new(); - for (k, v) in cols.into_iter().zip(vals) { + for (k, v) in attrs { if let Value::String { val, .. } = v { h.insert(k, val); } else { diff --git a/crates/nu-command/src/formats/to/yaml.rs b/crates/nu-command/src/formats/to/yaml.rs index 648b1e7aba..a3f23d7209 100644 --- a/crates/nu-command/src/formats/to/yaml.rs +++ b/crates/nu-command/src/formats/to/yaml.rs @@ -53,9 +53,9 @@ pub fn value_to_yaml_value(v: &Value) -> Result { Value::Range { .. } => serde_yaml::Value::Null, Value::Float { val, .. } => serde_yaml::Value::Number(serde_yaml::Number::from(*val)), Value::String { val, .. } => serde_yaml::Value::String(val.clone()), - Value::Record { cols, vals, .. } => { + Value::Record { val, .. } => { let mut m = serde_yaml::Mapping::new(); - for (k, v) in cols.iter().zip(vals.iter()) { + for (k, v) in val { m.insert( serde_yaml::Value::String(k.clone()), value_to_yaml_value(v)?, diff --git a/crates/nu-command/src/generators/cal.rs b/crates/nu-command/src/generators/cal.rs index 7015c48d1a..711de83581 100644 --- a/crates/nu-command/src/generators/cal.rs +++ b/crates/nu-command/src/generators/cal.rs @@ -359,18 +359,7 @@ fn add_month_to_table( day_number += 1; } - let cols: Vec = indexmap.keys().map(|f| f.to_string()).collect(); - let mut vals: Vec = Vec::new(); - for c in &cols { - if let Some(x) = indexmap.get(c) { - vals.push(x.to_owned()) - } - } - calendar_vec_deque.push_back(Value::Record { - cols, - vals, - span: tag, - }) + calendar_vec_deque.push_back(Value::record(indexmap.into_iter().collect(), tag)) } Ok(()) diff --git a/crates/nu-command/src/help/help_.rs b/crates/nu-command/src/help/help_.rs index dd73da69ca..d203e95005 100644 --- a/crates/nu-command/src/help/help_.rs +++ b/crates/nu-command/src/help/help_.rs @@ -148,8 +148,8 @@ pub fn highlight_search_in_table( let mut matches = vec![]; for record in table { - let (cols, mut vals, record_span) = if let Value::Record { cols, vals, span } = record { - (cols, vals, span) + let (mut record, record_span) = if let Value::Record { val, span } = record { + (val, span) } else { return Err(ShellError::NushellFailedSpanned { msg: "Expected record".to_string(), @@ -158,7 +158,7 @@ pub fn highlight_search_in_table( }); }; - let has_match = cols.iter().zip(vals.iter_mut()).try_fold( + let has_match = record.iter_mut().try_fold( false, |acc: bool, (col, val)| -> Result { if !searched_cols.contains(&col.as_str()) { @@ -186,11 +186,7 @@ pub fn highlight_search_in_table( )?; if has_match { - matches.push(Value::Record { - cols, - vals, - span: record_span, - }); + matches.push(Value::record(record, record_span)); } } diff --git a/crates/nu-command/src/help/help_commands.rs b/crates/nu-command/src/help/help_commands.rs index 621ba9e889..482c0f9af0 100644 --- a/crates/nu-command/src/help/help_commands.rs +++ b/crates/nu-command/src/help/help_commands.rs @@ -4,8 +4,8 @@ use nu_engine::{get_full_help, CallExt}; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - span, Category, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, Spanned, SyntaxShape, Type, Value, + record, span, Category, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, + ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; use std::borrow::Borrow; @@ -128,9 +128,6 @@ fn build_help_commands(engine_state: &EngineState, span: Span) -> Vec { let mut found_cmds_vec = Vec::new(); for (_, decl_id) in commands { - let mut cols = vec![]; - let mut vals = vec![]; - let decl = engine_state.get_decl(decl_id); let sig = decl.signature().update_from_command(decl.borrow()); @@ -138,79 +135,46 @@ fn build_help_commands(engine_state: &EngineState, span: Span) -> Vec { let usage = sig.usage; let search_terms = sig.search_terms; - cols.push("name".into()); - vals.push(Value::String { val: key, span }); - - cols.push("category".into()); - vals.push(Value::string(sig.category.to_string(), span)); - - cols.push("command_type".into()); - vals.push(Value::String { - val: format!("{:?}", decl.command_type()).to_lowercase(), - span, - }); - - cols.push("usage".into()); - vals.push(Value::String { val: usage, span }); - - cols.push("params".into()); + let command_type = format!("{:?}", decl.command_type()).to_lowercase(); // Build table of parameters let param_table = { let mut vals = vec![]; for required_param in &sig.required_positional { - vals.push(Value::Record { - cols: vec![ - "name".to_string(), - "type".to_string(), - "required".to_string(), - "description".to_string(), - ], - vals: vec![ - Value::string(&required_param.name, span), - Value::string(required_param.shape.to_string(), span), - Value::bool(true, span), - Value::string(&required_param.desc, span), - ], + vals.push(Value::record( + record! { + "name" => Value::string(&required_param.name, span), + "type" => Value::string(required_param.shape.to_string(), span), + "required" => Value::bool(true, span), + "description" => Value::string(&required_param.desc, span), + }, span, - }); + )); } for optional_param in &sig.optional_positional { - vals.push(Value::Record { - cols: vec![ - "name".to_string(), - "type".to_string(), - "required".to_string(), - "description".to_string(), - ], - vals: vec![ - Value::string(&optional_param.name, span), - Value::string(optional_param.shape.to_string(), span), - Value::bool(false, span), - Value::string(&optional_param.desc, span), - ], + vals.push(Value::record( + record! { + "name" => Value::string(&optional_param.name, span), + "type" => Value::string(optional_param.shape.to_string(), span), + "required" => Value::bool(false, span), + "description" => Value::string(&optional_param.desc, span), + }, span, - }); + )); } if let Some(rest_positional) = &sig.rest_positional { - vals.push(Value::Record { - cols: vec![ - "name".to_string(), - "type".to_string(), - "required".to_string(), - "description".to_string(), - ], - vals: vec![ - Value::string(format!("...{}", rest_positional.name), span), - Value::string(rest_positional.shape.to_string(), span), - Value::bool(false, span), - Value::string(&rest_positional.desc, span), - ], + vals.push(Value::record( + record! { + "name" => Value::string(format!("...{}", rest_positional.name), span), + "type" => Value::string(rest_positional.shape.to_string(), span), + "required" => Value::bool(false, span), + "description" => Value::string(&rest_positional.desc, span), + }, span, - }); + )); } for named_param in &sig.named { @@ -224,68 +188,54 @@ fn build_help_commands(engine_state: &EngineState, span: Span) -> Vec { format!("--{}", named_param.long) }; - vals.push(Value::Record { - cols: vec![ - "name".to_string(), - "type".to_string(), - "required".to_string(), - "description".to_string(), - ], - vals: vec![ - Value::string(name, span), - Value::string( - if let Some(arg) = &named_param.arg { - arg.to_string() - } else { - "switch".to_string() - }, - span, - ), - Value::bool(named_param.required, span), - Value::string(&named_param.desc, span), - ], + let typ = if let Some(arg) = &named_param.arg { + arg.to_string() + } else { + "switch".to_string() + }; + + vals.push(Value::record( + record! { + "name" => Value::string(name, span), + "type" => Value::string(typ, span), + "required" => Value::bool(named_param.required, span), + "description" => Value::string(&named_param.desc, span), + }, span, - }); + )); } Value::List { vals, span } }; - vals.push(param_table); - - cols.push("input_output".into()); // Build the signature input/output table let input_output_table = { let mut vals = vec![]; for (input_type, output_type) in sig.input_output_types { - vals.push(Value::Record { - cols: vec!["input".to_string(), "output".to_string()], - vals: vec![ - Value::String { - val: input_type.to_string(), - span, - }, - Value::String { - val: output_type.to_string(), - span, - }, - ], + vals.push(Value::record( + record! { + "input" => Value::string(input_type.to_string(), span), + "output" => Value::string(output_type.to_string(), span), + }, span, - }); + )); } Value::List { vals, span } }; - vals.push(input_output_table); - cols.push("search_terms".into()); - vals.push(Value::String { - val: search_terms.join(", "), - span, - }); + let record = record! { + "name" => Value::string(key, span), + "category" => Value::string(sig.category.to_string(), span), + "command_type" => Value::string(command_type, span), + "usage" => Value::string(usage, span), + "params" => param_table, + "input_output" => input_output_table, + "search_terms" => Value::string(search_terms.join(", "), span), + }; - found_cmds_vec.push(Value::Record { cols, vals, span }); + found_cmds_vec.push(Value::record(record, span)); } found_cmds_vec diff --git a/crates/nu-command/src/help/help_operators.rs b/crates/nu-command/src/help/help_operators.rs index 44caf5aabc..adea7bff57 100644 --- a/crates/nu-command/src/help/help_operators.rs +++ b/crates/nu-command/src/help/help_operators.rs @@ -1,7 +1,8 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type, Value, + record, Category, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type, + Value, }; #[derive(Clone)] @@ -35,23 +36,16 @@ impl Command for HelpOperators { let mut recs = vec![]; for op in op_info { - let mut cols = vec![]; - let mut vals = vec![]; - cols.push("type".into()); - vals.push(Value::string(op.op_type, head)); - cols.push("operator".into()); - vals.push(Value::string(op.operator, head)); - cols.push("name".into()); - vals.push(Value::string(op.name, head)); - cols.push("description".into()); - vals.push(Value::string(op.description, head)); - cols.push("precedence".into()); - vals.push(Value::int(op.precedence, head)); - recs.push(Value::Record { - cols, - vals, - span: head, - }) + recs.push(Value::record( + record! { + "type" => Value::string(op.op_type, head), + "operator" => Value::string(op.operator, head), + "name" => Value::string(op.name, head), + "description" => Value::string(op.description, head), + "precedence" => Value::int(op.precedence, head), + }, + head, + )); } Ok(recs diff --git a/crates/nu-command/src/hook.rs b/crates/nu-command/src/hook.rs index eebe8d923f..c83a864e23 100644 --- a/crates/nu-command/src/hook.rs +++ b/crates/nu-command/src/hook.rs @@ -14,12 +14,8 @@ pub fn eval_env_change_hook( ) -> Result<(), ShellError> { if let Some(hook) = env_change_hook { match hook { - Value::Record { - cols: env_names, - vals: hook_values, - .. - } => { - for (env_name, hook_value) in env_names.iter().zip(hook_values.iter()) { + Value::Record { val, .. } => { + for (env_name, hook_value) in &val { let before = engine_state .previous_env_vars .get(env_name) diff --git a/crates/nu-command/src/math/max.rs b/crates/nu-command/src/math/max.rs index 93ad2c9f0e..edc7971d75 100644 --- a/crates/nu-command/src/math/max.rs +++ b/crates/nu-command/src/math/max.rs @@ -2,7 +2,9 @@ use crate::math::reducers::{reducer_for, Reduce}; use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; +use nu_protocol::{ + Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value, +}; #[derive(Clone)] pub struct SubCommand; @@ -50,11 +52,10 @@ impl Command for SubCommand { Example { description: "Find the maxima of the columns of a table", example: "[{a: 1 b: 3} {a: 2 b: -1}] | math max", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_int(2), Value::test_int(3)], - span: Span::test_data(), - }), + })), }, ] } diff --git a/crates/nu-command/src/math/median.rs b/crates/nu-command/src/math/median.rs index 3f34e2adeb..99a4639895 100644 --- a/crates/nu-command/src/math/median.rs +++ b/crates/nu-command/src/math/median.rs @@ -4,7 +4,9 @@ use crate::math::avg::average; use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; +use nu_protocol::{ + Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value, +}; #[derive(Clone)] pub struct SubCommand; @@ -54,11 +56,10 @@ impl Command for SubCommand { Example { description: "Compute the medians of the columns of a table", example: "[{a: 1 b: 3} {a: 2 b: -1} {a: -3 b: 5}] | math median", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_int(1), Value::test_int(3)], - span: Span::test_data(), - }), + })), }, ] } diff --git a/crates/nu-command/src/math/min.rs b/crates/nu-command/src/math/min.rs index 6acc3496f6..8326ea6063 100644 --- a/crates/nu-command/src/math/min.rs +++ b/crates/nu-command/src/math/min.rs @@ -2,7 +2,9 @@ use crate::math::reducers::{reducer_for, Reduce}; use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; +use nu_protocol::{ + Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value, +}; #[derive(Clone)] pub struct SubCommand; @@ -50,11 +52,10 @@ impl Command for SubCommand { Example { description: "Compute the minima of the columns of a table", example: "[{a: 1 b: 3} {a: 2 b: -1}] | math min", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_int(1), Value::test_int(-1)], - span: Span::test_data(), - }), + })), }, Example { description: "Find the minimum of a list of arbitrary values (Warning: Weird)", diff --git a/crates/nu-command/src/math/mode.rs b/crates/nu-command/src/math/mode.rs index e4f0a26cc0..efb6df7227 100644 --- a/crates/nu-command/src/math/mode.rs +++ b/crates/nu-command/src/math/mode.rs @@ -1,7 +1,9 @@ use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; +use nu_protocol::{ + Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value, +}; use std::cmp::Ordering; use std::collections::HashMap; @@ -88,7 +90,7 @@ impl Command for SubCommand { Example { description: "Compute the mode(s) of the columns of a table", example: "[{a: 1 b: 3} {a: 2 b: -1} {a: 1 b: 5}] | math mode", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![ Value::List { @@ -100,8 +102,7 @@ impl Command for SubCommand { span: Span::test_data(), }, ], - span: Span::test_data(), - }), + })), }, ] } diff --git a/crates/nu-command/src/math/utils.rs b/crates/nu-command/src/math/utils.rs index 3f6d059e8c..a704b1ddc7 100644 --- a/crates/nu-command/src/math/utils.rs +++ b/crates/nu-command/src/math/utils.rs @@ -1,6 +1,6 @@ use indexmap::map::IndexMap; use nu_protocol::ast::Call; -use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Spanned, Value}; +use nu_protocol::{IntoPipelineData, PipelineData, Record, ShellError, Span, Value}; pub fn run_with_function( call: &Call, @@ -26,8 +26,8 @@ fn helper_for_tables( let mut column_values = IndexMap::new(); for val in values { match val { - Value::Record { cols, vals, .. } => { - for (key, value) in cols.iter().zip(vals.iter()) { + Value::Record { val, .. } => { + for (key, value) in val { column_values .entry(key.clone()) .and_modify(|v: &mut Vec| v.push(value.clone())) @@ -57,10 +57,7 @@ fn helper_for_tables( )); } - Ok(Value::from(Spanned { - item: column_totals, - span: name, - })) + Ok(Value::record(column_totals.into_iter().collect(), name)) } pub fn calculate( @@ -83,15 +80,20 @@ pub fn calculate( ), _ => mf(vals, span, name), }, - PipelineData::Value(Value::Record { vals, cols, span }, ..) => { - let new_vals: Result, ShellError> = - vals.into_iter().map(|val| mf(&[val], span, name)).collect(); + PipelineData::Value(Value::Record { val: record, span }, ..) => { + let new_vals: Result, ShellError> = record + .vals + .into_iter() + .map(|val| mf(&[val], span, name)) + .collect(); match new_vals { - Ok(vec) => Ok(Value::Record { - cols, - vals: vec, + Ok(vec) => Ok(Value::record( + Record { + cols: record.cols, + vals: vec, + }, span, - }), + )), Err(err) => Err(err), } } diff --git a/crates/nu-command/src/network/http/client.rs b/crates/nu-command/src/network/http/client.rs index 7734d68629..bf203f567a 100644 --- a/crates/nu-command/src/network/http/client.rs +++ b/crates/nu-command/src/network/http/client.rs @@ -5,7 +5,7 @@ use base64::{alphabet, Engine}; use nu_protocol::ast::Call; use nu_protocol::engine::{EngineState, Stack}; use nu_protocol::{ - BufferedReader, IntoPipelineData, PipelineData, RawStream, ShellError, Span, Value, + record, BufferedReader, IntoPipelineData, PipelineData, RawStream, ShellError, Span, Value, }; use ureq::{Error, ErrorKind, Request, Response}; @@ -183,12 +183,12 @@ pub fn send_request( let data = value_to_json_value(&body)?; send_cancellable_request(&request_url, Box::new(|| request.send_json(data)), ctrl_c) } - Value::Record { cols, vals, .. } if body_type == BodyType::Form => { - let mut data: Vec<(String, String)> = Vec::with_capacity(cols.len()); + Value::Record { val, .. } if body_type == BodyType::Form => { + let mut data: Vec<(String, String)> = Vec::with_capacity(val.len()); - for (col, val) in cols.iter().zip(vals.iter()) { + for (col, val) in val { let val_string = val.as_string()?; - data.push((col.clone(), val_string)) + data.push((col, val_string)) } let request_fn = move || { @@ -296,8 +296,8 @@ pub fn request_add_custom_headers( let mut custom_headers: HashMap = HashMap::new(); match &headers { - Value::Record { cols, vals, .. } => { - for (k, v) in cols.iter().zip(vals.iter()) { + Value::Record { val, .. } => { + for (k, v) in val { custom_headers.insert(k.to_string(), v.clone()); } } @@ -306,8 +306,8 @@ pub fn request_add_custom_headers( if table.len() == 1 { // single row([key1 key2]; [val1 val2]) match &table[0] { - Value::Record { cols, vals, .. } => { - for (k, v) in cols.iter().zip(vals.iter()) { + Value::Record { val, .. } => { + for (k, v) in val { custom_headers.insert(k.to_string(), v.clone()); } } @@ -498,25 +498,19 @@ fn request_handle_response_content( Err(_) => Value::nothing(span), }; - let headers = Value::Record { - cols: vec!["request".to_string(), "response".to_string()], - vals: vec![request_headers_value, response_headers_value], - span, + let headers = record! { + "request" => request_headers_value, + "response" => response_headers_value, }; - let full_response = Value::Record { - cols: vec![ - "headers".to_string(), - "body".to_string(), - "status".to_string(), - ], - vals: vec![ - headers, - consume_response_body(resp)?.into_value(span), - Value::int(response_status as i64, span), - ], + let full_response = Value::record( + record! { + "headers" => Value::record(headers, span), + "body" => consume_response_body(resp)?.into_value(span), + "status" => Value::int(response_status as i64, span), + }, span, - }; + ); Ok(full_response.into_pipeline_data()) } else { @@ -597,15 +591,14 @@ fn extract_response_headers(response: &Response) -> Headers { } fn headers_to_nu(headers: &Headers, span: Span) -> Result { - let cols = vec!["name".to_string(), "value".to_string()]; let mut vals = Vec::with_capacity(headers.len()); for (name, values) in headers { let is_duplicate = vals.iter().any(|val| { - if let Value::Record { vals, .. } = val { + if let Value::Record { val, .. } = val { if let Some(Value::String { val: header_name, .. - }) = vals.get(0) + }) = val.vals.get(0) { return name == header_name; } @@ -616,11 +609,11 @@ fn headers_to_nu(headers: &Headers, span: Span) -> Result Value::string(name, span), + "value" => Value::string(str_value, span), + }; + vals.push(Value::record(record, span)); } } } diff --git a/crates/nu-command/src/network/url/build_query.rs b/crates/nu-command/src/network/url/build_query.rs index 3ffb442cb2..4f6a9abadb 100644 --- a/crates/nu-command/src/network/url/build_query.rs +++ b/crates/nu-command/src/network/url/build_query.rs @@ -65,13 +65,9 @@ fn to_url(input: PipelineData, head: Span) -> Result { let output: Result = input .into_iter() .map(move |value| match value { - Value::Record { - ref cols, - ref vals, - span, - } => { + Value::Record { ref val, span } => { let mut row_vec = vec![]; - for (k, v) in cols.iter().zip(vals.iter()) { + for (k, v) in val { match v.as_string() { Ok(s) => { row_vec.push((k.clone(), s.to_string())); diff --git a/crates/nu-command/src/network/url/join.rs b/crates/nu-command/src/network/url/join.rs index 215199fbf5..f9869b7c5b 100644 --- a/crates/nu-command/src/network/url/join.rs +++ b/crates/nu-command/src/network/url/join.rs @@ -92,16 +92,11 @@ impl Command for SubCommand { let output: Result = input .into_iter() .map(move |value| match value { - Value::Record { - ref cols, - ref vals, - span, - } => { - let url_components = cols - .iter() - .zip(vals.iter()) + Value::Record { val, span } => { + let url_components = val + .into_iter() .try_fold(UrlComponents::new(), |url, (k, v)| { - url.add_component(k.clone(), v.clone(), span) + url.add_component(k, v, span) }); url_components?.to_url(span) @@ -176,14 +171,9 @@ impl UrlComponents { if key == "params" { return match value { - Value::Record { - ref cols, - ref vals, - span, - } => { - let mut qs = cols + Value::Record { ref val, span } => { + let mut qs = val .iter() - .zip(vals.iter()) .map(|(k, v)| match v.as_string() { Ok(val) => Ok(format!("{k}={val}")), Err(err) => Err(err), diff --git a/crates/nu-command/src/network/url/parse.rs b/crates/nu-command/src/network/url/parse.rs index 19e5e125ad..f6bc11627c 100644 --- a/crates/nu-command/src/network/url/parse.rs +++ b/crates/nu-command/src/network/url/parse.rs @@ -2,7 +2,8 @@ use super::url; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, + record, Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, + Type, Value, }; use url::Url; @@ -55,7 +56,7 @@ impl Command for SubCommand { vec![Example { description: "Parses a url", example: "'http://user123:pass567@www.example.com:8081/foo/bar?param1=section&p2=&f[name]=vldc#hello' | url parse", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec![ "scheme".to_string(), "username".to_string(), @@ -76,18 +77,16 @@ impl Command for SubCommand { Value::test_string("/foo/bar"), Value::test_string("param1=section&p2=&f[name]=vldc"), Value::test_string("hello"), - Value::Record { + Value::test_record(Record { cols: vec!["param1".to_string(), "p2".to_string(), "f[name]".to_string()], vals: vec![ Value::test_string("section"), Value::test_string(""), Value::test_string("vldc"), ], - span: Span::test_data(), - }, + }), ], - span: Span::test_data(), - }), + })), }] } } @@ -106,80 +105,31 @@ fn parse(value: Value, head: Span, engine_state: &EngineState) -> Result { - let cols = vec![ - String::from("scheme"), - String::from("username"), - String::from("password"), - String::from("host"), - String::from("port"), - String::from("path"), - String::from("query"), - String::from("fragment"), - String::from("params"), - ]; - let mut vals: Vec = vec![ - Value::String { - val: String::from(url.scheme()), - span: head, - }, - Value::String { - val: String::from(url.username()), - span: head, - }, - Value::String { - val: String::from(url.password().unwrap_or("")), - span: head, - }, - Value::String { - val: String::from(url.host_str().unwrap_or("")), - span: head, - }, - Value::String { - val: url - .port() - .map(|p| p.to_string()) - .unwrap_or_else(|| "".into()), - span: head, - }, - Value::String { - val: String::from(url.path()), - span: head, - }, - Value::String { - val: String::from(url.query().unwrap_or("")), - span: head, - }, - Value::String { - val: String::from(url.fragment().unwrap_or("")), - span: head, - }, - ]; - let params = serde_urlencoded::from_str::>(url.query().unwrap_or("")); match params { Ok(result) => { - let (param_cols, param_vals) = result + let params = result .into_iter() - .map(|(k, v)| (k, Value::String { val: v, span: head })) - .unzip(); + .map(|(k, v)| (k, Value::string(v, head))) + .collect(); - vals.push(Value::Record { - cols: param_cols, - vals: param_vals, - span: head, - }); + let port = url.port().map(|p| p.to_string()).unwrap_or_default(); - Ok(PipelineData::Value( - Value::Record { - cols, - vals, - span: head, - }, - None, - )) + let record = record! { + "scheme" => Value::string(url.scheme(), head), + "username" => Value::string(url.username(), head), + "password" => Value::string(url.password().unwrap_or(""), head), + "host" => Value::string(url.host_str().unwrap_or(""), head), + "port" => Value::string(port, head), + "path" => Value::string(url.path(), head), + "query" => Value::string(url.query().unwrap_or(""), head), + "fragment" => Value::string(url.fragment().unwrap_or(""), head), + "params" => Value::record(params, head), + }; + + Ok(PipelineData::Value(Value::record(record, head), None)) } - _ => Err(ShellError::UnsupportedInput( "String not compatible with url-encoding".to_string(), "value originates from here".into(), diff --git a/crates/nu-command/src/path/join.rs b/crates/nu-command/src/path/join.rs index e16efe25f4..3a68f5604d 100644 --- a/crates/nu-command/src/path/join.rs +++ b/crates/nu-command/src/path/join.rs @@ -5,7 +5,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{EngineState, Stack}; use nu_protocol::{ - engine::Command, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, + engine::Command, Category, Example, PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; @@ -150,7 +150,7 @@ the output of 'path parse' and 'path split' subcommands."# fn handle_value(v: Value, args: &Arguments, head: Span) -> Value { match v { Value::String { ref val, .. } => join_single(Path::new(val), head, args), - Value::Record { cols, vals, span } => join_record(&cols, &vals, head, span, args), + Value::Record { val, span } => join_record(&val, head, span, args), Value::List { vals, span } => join_list(&vals, head, span, args), _ => super::handle_invalid_values(v, head), @@ -177,7 +177,7 @@ fn join_list(parts: &[Value], head: Span, span: Span, args: &Arguments) -> Value Ok(vals) => { let vals = vals .iter() - .map(|(k, v)| join_record(k, v, head, span, args)) + .map(|r| join_record(r, head, span, args)) .collect(); Value::List { vals, span } @@ -194,8 +194,8 @@ fn join_list(parts: &[Value], head: Span, span: Span, args: &Arguments) -> Value } } -fn join_record(cols: &[String], vals: &[Value], head: Span, span: Span, args: &Arguments) -> Value { - match merge_record(cols, vals, head, span) { +fn join_record(record: &Record, head: Span, span: Span, args: &Arguments) -> Value { + match merge_record(record, head, span) { Ok(p) => join_single(p.as_path(), head, args), Err(error) => Value::Error { error: Box::new(error), @@ -203,13 +203,8 @@ fn join_record(cols: &[String], vals: &[Value], head: Span, span: Span, args: &A } } -fn merge_record( - cols: &[String], - vals: &[Value], - head: Span, - span: Span, -) -> Result { - for key in cols { +fn merge_record(record: &Record, head: Span, span: Span) -> Result { + for key in &record.cols { if !super::ALLOWED_COLUMNS.contains(&key.as_str()) { let allowed_cols = super::ALLOWED_COLUMNS.join(", "); return Err(ShellError::UnsupportedInput( @@ -223,7 +218,13 @@ fn merge_record( } } - let entries: HashMap<&str, &Value> = cols.iter().map(String::as_str).zip(vals).collect(); + let entries: HashMap<&str, &Value> = record + .cols + .iter() + .map(String::as_str) + .zip(&record.vals) + .collect(); + let mut result = PathBuf::new(); #[cfg(windows)] diff --git a/crates/nu-command/src/path/parse.rs b/crates/nu-command/src/path/parse.rs index d8972e19c1..483e973676 100644 --- a/crates/nu-command/src/path/parse.rs +++ b/crates/nu-command/src/path/parse.rs @@ -1,11 +1,10 @@ use std::path::Path; -use indexmap::IndexMap; use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{EngineState, Stack}; use nu_protocol::{ - engine::Command, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, + engine::Command, Category, Example, PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; @@ -77,7 +76,7 @@ On Windows, an extra 'prefix' column is added."# Example { description: "Parse a single path", example: r"'C:\Users\viking\spam.txt' | path parse", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec![ "prefix".into(), "parent".into(), @@ -90,8 +89,7 @@ On Windows, an extra 'prefix' column is added."# Value::test_string("spam"), Value::test_string("txt"), ], - span: Span::test_data(), - }), + })), }, Example { description: "Replace a complex extension", @@ -101,7 +99,7 @@ On Windows, an extra 'prefix' column is added."# Example { description: "Ignore the extension", example: r"'C:\Users\viking.d' | path parse -e ''", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec![ "prefix".into(), "parent".into(), @@ -114,14 +112,13 @@ On Windows, an extra 'prefix' column is added."# Value::test_string("viking.d"), Value::test_string(""), ], - span: Span::test_data(), - }), + })), }, Example { description: "Parse all paths in a list", example: r"[ C:\Users\viking.d C:\Users\spam.txt ] | path parse", result: Some(Value::test_list(vec![ - Value::Record { + Value::test_record(Record { cols: vec![ "prefix".into(), "parent".into(), @@ -134,9 +131,8 @@ On Windows, an extra 'prefix' column is added."# Value::test_string("viking"), Value::test_string("d"), ], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec![ "prefix".into(), "parent".into(), @@ -149,8 +145,7 @@ On Windows, an extra 'prefix' column is added."# Value::test_string("spam"), Value::test_string("txt"), ], - span: Span::test_data(), - }, + }), ])), }, ] @@ -162,15 +157,14 @@ On Windows, an extra 'prefix' column is added."# Example { description: "Parse a path", example: r"'/home/viking/spam.txt' | path parse", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["parent".into(), "stem".into(), "extension".into()], vals: vec![ Value::test_string("/home/viking"), Value::test_string("spam"), Value::test_string("txt"), ], - span: Span::test_data(), - }), + })), }, Example { description: "Replace a complex extension", @@ -180,38 +174,35 @@ On Windows, an extra 'prefix' column is added."# Example { description: "Ignore the extension", example: r"'/etc/conf.d' | path parse -e ''", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["parent".into(), "stem".into(), "extension".into()], vals: vec![ Value::test_string("/etc"), Value::test_string("conf.d"), Value::test_string(""), ], - span: Span::test_data(), - }), + })), }, Example { description: "Parse all paths in a list", example: r"[ /home/viking.d /home/spam.txt ] | path parse", result: Some(Value::test_list(vec![ - Value::Record { + Value::test_record(Record { cols: vec!["parent".into(), "stem".into(), "extension".into()], vals: vec![ Value::test_string("/home"), Value::test_string("viking"), Value::test_string("d"), ], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["parent".into(), "stem".into(), "extension".into()], vals: vec![ Value::test_string("/home"), Value::test_string("spam"), Value::test_string("txt"), ], - span: Span::test_data(), - }, + }), ])), }, ] @@ -219,7 +210,7 @@ On Windows, an extra 'prefix' column is added."# } fn parse(path: &Path, span: Span, args: &Arguments) -> Value { - let mut map: IndexMap = IndexMap::new(); + let mut record = Record::new(); #[cfg(windows)] { @@ -231,7 +222,7 @@ fn parse(path: &Path, span: Span, args: &Arguments) -> Value { } _ => "".into(), }; - map.insert("prefix".into(), Value::string(prefix, span)); + record.push("prefix", Value::string(prefix, span)); } let parent = path @@ -239,7 +230,7 @@ fn parse(path: &Path, span: Span, args: &Arguments) -> Value { .unwrap_or_else(|| "".as_ref()) .to_string_lossy(); - map.insert("parent".into(), Value::string(parent, span)); + record.push("parent", Value::string(parent, span)); let basename = path .file_name() @@ -254,14 +245,11 @@ fn parse(path: &Path, span: Span, args: &Arguments) -> Value { let ext_with_dot = [".", extension].concat(); if basename.ends_with(&ext_with_dot) && !extension.is_empty() { let stem = basename.trim_end_matches(&ext_with_dot); - map.insert("stem".into(), Value::string(stem, span)); - map.insert( - "extension".into(), - Value::string(extension, *extension_span), - ); + record.push("stem", Value::string(stem, span)); + record.push("extension", Value::string(extension, *extension_span)); } else { - map.insert("stem".into(), Value::string(basename, span)); - map.insert("extension".into(), Value::string("", span)); + record.push("stem", Value::string(basename, span)); + record.push("extension", Value::string("", span)); } } None => { @@ -274,12 +262,12 @@ fn parse(path: &Path, span: Span, args: &Arguments) -> Value { .unwrap_or_else(|| "".as_ref()) .to_string_lossy(); - map.insert("stem".into(), Value::string(stem, span)); - map.insert("extension".into(), Value::string(extension, span)); + record.push("stem", Value::string(stem, span)); + record.push("extension", Value::string(extension, span)); } } - Value::from(Spanned { item: map, span }) + Value::record(record, span) } #[cfg(test)] diff --git a/crates/nu-command/src/platform/ansi/ansi_.rs b/crates/nu-command/src/platform/ansi/ansi_.rs index a60a6897db..cb8ed32b1b 100644 --- a/crates/nu-command/src/platform/ansi/ansi_.rs +++ b/crates/nu-command/src/platform/ansi/ansi_.rs @@ -1,6 +1,7 @@ use nu_ansi_term::*; use nu_engine::CallExt; use nu_protocol::engine::{EngineState, Stack}; +use nu_protocol::record; use nu_protocol::{ ast::Call, engine::Command, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, @@ -763,7 +764,7 @@ Operating system commands: attr: None, }; // Iterate and populate NuStyle with real values - for (k, v) in record.0.iter().zip(record.1) { + for (k, v) in record { match k.as_str() { "fg" => nu_style.fg = Some(v.as_string()?), "bg" => nu_style.bg = Some(v.as_string()?), @@ -799,17 +800,7 @@ fn generate_ansi_code_list( .iter() .enumerate() .map(move |(i, ansi_code)| { - let cols = if use_ansi_coloring { - vec![ - "name".into(), - "preview".into(), - "short name".into(), - "code".into(), - ] - } else { - vec!["name".into(), "short name".into(), "code".into()] - }; - let name: Value = Value::string(String::from(ansi_code.long_name), call_span); + let name = Value::string(ansi_code.long_name, call_span); let short_name = Value::string(ansi_code.short_name.unwrap_or(""), call_span); // The first 102 items in the ansi array are colors let preview = if i < 389 { @@ -817,18 +808,24 @@ fn generate_ansi_code_list( } else { Value::string("\u{1b}[0m", call_span) }; - let code_string = String::from(&ansi_code.code.replace('\u{1b}', "\\e")); - let code = Value::string(code_string, call_span); - let vals = if use_ansi_coloring { - vec![name, preview, short_name, code] + let code = Value::string(ansi_code.code.replace('\u{1b}', "\\e"), call_span); + + let record = if use_ansi_coloring { + record! { + "name" => name, + "preview" => preview, + "short name" => short_name, + "code" => code, + } } else { - vec![name, short_name, code] + record! { + "name" => name, + "short name" => short_name, + "code" => code, + } }; - Value::Record { - cols, - vals, - span: call_span, - } + + Value::record(record, call_span) }) .into_pipeline_data(engine_state.ctrlc.clone())); } diff --git a/crates/nu-command/src/platform/dir_info.rs b/crates/nu-command/src/platform/dir_info.rs index 2edce599f9..c9c0d6cd59 100644 --- a/crates/nu-command/src/platform/dir_info.rs +++ b/crates/nu-command/src/platform/dir_info.rs @@ -1,6 +1,6 @@ use filesize::file_real_size_fast; use nu_glob::Pattern; -use nu_protocol::{ShellError, Span, Value}; +use nu_protocol::{record, ShellError, Span, Value}; use std::path::PathBuf; use std::sync::atomic::AtomicBool; use std::sync::Arc; @@ -185,30 +185,6 @@ impl DirInfo { impl From for Value { fn from(d: DirInfo) -> Self { - let mut cols = vec![]; - let mut vals = vec![]; - - cols.push("path".into()); - vals.push(Value::string(d.path.display().to_string(), d.tag)); - - cols.push("apparent".into()); - vals.push(Value::Filesize { - val: d.size as i64, - span: d.tag, - }); - - cols.push("physical".into()); - vals.push(Value::Filesize { - val: d.blocks as i64, - span: d.tag, - }); - - cols.push("directories".into()); - vals.push(value_from_vec(d.dirs, d.tag)); - - cols.push("files".into()); - vals.push(value_from_vec(d.files, d.tag)); - // if !d.errors.is_empty() { // let v = d // .errors @@ -223,51 +199,34 @@ impl From for Value { // }) // } - Value::Record { - cols, - vals, - span: d.tag, - } + Value::record( + record! { + "path" => Value::string(d.path.display().to_string(), d.tag), + "apparent" => Value::filesize(d.size as i64, d.tag), + "physical" => Value::filesize(d.blocks as i64, d.tag), + "directories" => value_from_vec(d.dirs, d.tag), + "files" => value_from_vec(d.files, d.tag) + }, + d.tag, + ) } } impl From for Value { fn from(f: FileInfo) -> Self { - let mut cols = vec![]; - let mut vals = vec![]; - - cols.push("path".into()); - vals.push(Value::string(f.path.display().to_string(), f.tag)); - - cols.push("apparent".into()); - vals.push(Value::Filesize { - val: f.size as i64, - span: f.tag, - }); - - cols.push("physical".into()); - vals.push(Value::Filesize { - val: match f.blocks { - Some(b) => b as i64, - None => 0i64, - }, - span: f.tag, - }); - - cols.push("directories".into()); - vals.push(Value::nothing(Span::unknown())); - - cols.push("files".into()); - vals.push(Value::nothing(Span::unknown())); - // cols.push("errors".into()); // vals.push(Value::nothing(Span::unknown())); - Value::Record { - cols, - vals, - span: f.tag, - } + Value::record( + record! { + "path" => Value::string(f.path.display().to_string(), f.tag), + "apparent" => Value::filesize(f.size as i64, f.tag), + "physical" => Value::filesize(f.blocks.unwrap_or(0) as i64, f.tag), + "directories" => Value::nothing(Span::unknown()), + "files" => Value::nothing(Span::unknown()), + }, + f.tag, + ) } } diff --git a/crates/nu-command/src/platform/input/input_listen.rs b/crates/nu-command/src/platform/input/input_listen.rs index fa55ba8068..4cd1ce90ce 100644 --- a/crates/nu-command/src/platform/input/input_listen.rs +++ b/crates/nu-command/src/platform/input/input_listen.rs @@ -7,7 +7,8 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, + record, Category, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, + Type, Value, }; use num_traits::AsPrimitive; use std::io::stdout; @@ -273,13 +274,13 @@ fn create_focus_event( event_type: FocusEventType, ) -> Option { if filter.listen_focus { - let cols = vec!["type".to_string(), "event".to_string()]; - let vals = vec![ - Value::string("focus", head), - Value::string(event_type.string(), head), - ]; - - Some(Value::record(cols, vals, head)) + Some(Value::record( + record! { + "type" => Value::string("focus", head), + "event" => Value::string(event_type.string(), head) + }, + head, + )) } else { None } @@ -308,28 +309,26 @@ fn create_key_event( return None; } - let mut cols = vec![ - "type".to_string(), - "key_type".to_string(), - "code".to_string(), - "modifiers".to_string(), - ]; - - let typ = Value::string("key".to_string(), head); let (key, code) = get_keycode_name(head, raw_code); - let modifiers = parse_modifiers(head, raw_modifiers); - let mut vals = vec![typ, key, code, modifiers]; + + let mut record = record! { + "type" => Value::string("key", head), + "key_type" => key, + "code" => code, + "modifiers" => parse_modifiers(head, raw_modifiers), + }; if add_raw { if let KeyCode::Char(c) = raw_code { - cols.push("raw_code".to_string()); - vals.push(Value::int(c.as_(), head)); + record.push("raw_code", Value::int(c.as_(), head)); } - cols.push("raw_modifiers".to_string()); - vals.push(Value::int(raw_modifiers.bits() as i64, head)); + record.push( + "raw_modifiers", + Value::int(raw_modifiers.bits() as i64, head), + ); } - Some(Value::record(cols, vals, head)) + Some(Value::record(record, head)) } else { None } @@ -373,18 +372,6 @@ fn create_mouse_event( add_raw: bool, ) -> Option { if filter.listen_mouse { - let mut cols = vec![ - "type".to_string(), - "col".to_string(), - "row".to_string(), - "kind".to_string(), - "modifiers".to_string(), - ]; - - let typ = Value::string("mouse".to_string(), head); - let col = Value::int(event.column as i64, head); - let row = Value::int(event.row as i64, head); - let kind = match event.kind { MouseEventKind::Down(btn) => format!("{btn:?}_down"), MouseEventKind::Up(btn) => format!("{btn:?}_up"), @@ -393,17 +380,23 @@ fn create_mouse_event( MouseEventKind::ScrollDown => "scroll_down".to_string(), MouseEventKind::ScrollUp => "scroll_up".to_string(), }; - let kind = Value::string(kind, head); - let modifiers = parse_modifiers(head, &event.modifiers); - let mut vals = vec![typ, col, row, kind, modifiers]; + let mut record = record! { + "type" => Value::string("mouse", head), + "col" => Value::int(event.column as i64, head), + "row" => Value::int(event.row as i64, head), + "kind" => Value::string(kind, head), + "modifiers" => parse_modifiers(head, &event.modifiers), + }; if add_raw { - cols.push("raw_modifiers".to_string()); - vals.push(Value::int(event.modifiers.bits() as i64, head)); + record.push( + "raw_modifiers", + Value::int(event.modifiers.bits() as i64, head), + ); } - Some(Value::record(cols, vals, head)) + Some(Value::record(record, head)) } else { None } @@ -411,10 +404,12 @@ fn create_mouse_event( fn create_paste_event(head: Span, filter: &EventTypeFilter, content: &str) -> Option { if filter.listen_paste { - let cols = vec!["type".to_string(), "content".to_string()]; - let vals = vec![Value::string("paste", head), Value::string(content, head)]; + let record = record! { + "type" => Value::string("paste", head), + "content" => Value::string(content, head), + }; - Some(Value::record(cols, vals, head)) + Some(Value::record(record, head)) } else { None } @@ -427,14 +422,13 @@ fn create_resize_event( rows: u16, ) -> Option { if filter.listen_resize { - let cols = vec!["type".to_string(), "col".to_string(), "row".to_string()]; - let vals = vec![ - Value::string("resize", head), - Value::int(columns as i64, head), - Value::int(rows as i64, head), - ]; + let record = record! { + "type" => Value::string("resize", head), + "col" => Value::int(columns as i64, head), + "row" => Value::int(rows as i64, head), + }; - Some(Value::record(cols, vals, head)) + Some(Value::record(record, head)) } else { None } diff --git a/crates/nu-command/src/platform/input/list.rs b/crates/nu-command/src/platform/input/list.rs index 93e82c0cf8..ea206a4a14 100644 --- a/crates/nu-command/src/platform/input/list.rs +++ b/crates/nu-command/src/platform/input/list.rs @@ -88,8 +88,8 @@ impl Command for InputList { let rows = input.into_iter().collect::>(); rows.iter().for_each(|row| { if let Ok(record) = row.as_record() { - let columns = record.1.len(); - for (i, (col, val)) in record.0.iter().zip(record.1.iter()).enumerate() { + let columns = record.len(); + for (i, (col, val)) in record.iter().enumerate() { if i == columns - 1 { break; } @@ -116,9 +116,8 @@ impl Command for InputList { }) } else if let Ok(record) = x.as_record() { let mut options = Vec::new(); - let columns = record.1.len(); - for (i, (col, val)) in record.0.iter().zip(record.1.iter()).enumerate() - { + let columns = record.len(); + for (i, (col, val)) in record.iter().enumerate() { if let Ok(val) = val.as_string() { let len = nu_utils::strip_ansi_likely(&val).len() + nu_utils::strip_ansi_likely(col).len(); diff --git a/crates/nu-command/src/platform/term_size.rs b/crates/nu-command/src/platform/term_size.rs index e2684c4751..5cbc8c0637 100644 --- a/crates/nu-command/src/platform/term_size.rs +++ b/crates/nu-command/src/platform/term_size.rs @@ -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, }; use terminal_size::{terminal_size, Height, Width}; @@ -63,14 +63,13 @@ impl Command for TermSize { None => (Width(0), Height(0)), }; - Ok(Value::Record { - cols: vec!["columns".into(), "rows".into()], - vals: vec![ - Value::int(cols.0 as i64, head), - Value::int(rows.0 as i64, head), - ], - span: head, - } + Ok(Value::record( + record! { + "columns" => Value::int(cols.0 as i64, head), + "rows" => Value::int(rows.0 as i64, head), + }, + head, + ) .into_pipeline_data()) } } diff --git a/crates/nu-command/src/sort_utils.rs b/crates/nu-command/src/sort_utils.rs index 92d9e395f2..a5121c1877 100644 --- a/crates/nu-command/src/sort_utils.rs +++ b/crates/nu-command/src/sort_utils.rs @@ -63,8 +63,7 @@ pub fn sort( ) -> Result<(), ShellError> { match vec.first() { Some(Value::Record { - cols, - vals: _input_vals, + val: record, span: val_span, }) => { if sort_columns.is_empty() { @@ -78,7 +77,8 @@ pub fn sort( )); } - if let Some(nonexistent) = nonexistent_column(sort_columns.clone(), cols.to_vec()) { + if let Some(nonexistent) = nonexistent_column(sort_columns.clone(), record.cols.clone()) + { return Err(ShellError::CantFindColumn { col_name: nonexistent, span, @@ -229,134 +229,140 @@ pub fn compare( Ordering::Equal } -#[test] -fn test_sort_value() { - let val = 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)], - ), - ], - span: Span::test_data(), - }; +#[cfg(test)] +mod tests { + use super::*; + use nu_protocol::{Record, Span, Value}; - let sorted_alphabetically = - sort_value(&val, vec!["fruit".to_string()], true, false, false).unwrap(); - assert_eq!( - sorted_alphabetically, - Value::List { + #[test] + fn test_sort_value() { + let val = 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("orange"), Value::test_int(7)], - ), - Value::test_record( - vec!["fruit", "count"], - 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("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(), - } - ); + }; - let sorted_by_count_desc = - sort_value(&val, vec!["count".to_string()], false, false, false).unwrap(); - assert_eq!( - sorted_by_count_desc, - Value::List { + let sorted_alphabetically = + sort_value(&val, vec!["fruit".to_string()], true, false, false).unwrap(); + assert_eq!( + sorted_alphabetically, + Value::List { + vals: vec![ + 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("orange"), Value::test_int(7)], + }), + Value::test_record(Record { + cols: vec!["fruit".to_string(), "count".to_string()], + vals: vec![Value::test_string("pear"), Value::test_int(3)], + }), + ], + span: Span::test_data(), + } + ); + + let sorted_by_count_desc = + sort_value(&val, vec!["count".to_string()], false, false, false).unwrap(); + assert_eq!( + sorted_by_count_desc, + Value::List { + vals: vec![ + 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("orange"), Value::test_int(7)], + }), + Value::test_record(Record { + cols: vec!["fruit".to_string(), "count".to_string()], + vals: vec![Value::test_string("pear"), Value::test_int(3)], + }), + ], + span: Span::test_data(), + } + ); + } + + #[test] + fn test_sort_value_in_place() { + let mut val = 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("orange"), Value::test_int(7)], - ), - Value::test_record( - vec!["fruit", "count"], - 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("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(), - } - ); -} - -#[test] -fn test_sort_value_in_place() { - let mut val = 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)], - ), - ], - span: Span::test_data(), - }; - - sort_value_in_place(&mut val, vec!["fruit".to_string()], true, false, false).unwrap(); - assert_eq!( - val, - 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("orange"), Value::test_int(7)], - ), - Value::test_record( - vec!["fruit", "count"], - vec![Value::test_string("pear"), Value::test_int(3)], - ), - ], - span: Span::test_data(), - } - ); - - sort_value_in_place(&mut val, vec!["count".to_string()], false, false, false).unwrap(); - assert_eq!( - val, - 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("orange"), Value::test_int(7)], - ), - Value::test_record( - vec!["fruit", "count"], - vec![Value::test_string("pear"), Value::test_int(3)], - ), - ], - span: Span::test_data(), - } - ); + }; + + sort_value_in_place(&mut val, vec!["fruit".to_string()], true, false, false).unwrap(); + assert_eq!( + val, + Value::List { + vals: vec![ + 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("orange"), Value::test_int(7)], + }), + Value::test_record(Record { + cols: vec!["fruit".to_string(), "count".to_string()], + vals: vec![Value::test_string("pear"), Value::test_int(3)], + }), + ], + span: Span::test_data(), + } + ); + + sort_value_in_place(&mut val, vec!["count".to_string()], false, false, false).unwrap(); + assert_eq!( + val, + Value::List { + vals: vec![ + 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("orange"), Value::test_int(7)], + }), + Value::test_record(Record { + cols: vec!["fruit".to_string(), "count".to_string()], + vals: vec![Value::test_string("pear"), Value::test_int(3)], + }), + ], + span: Span::test_data(), + } + ); + } } diff --git a/crates/nu-command/src/strings/char_.rs b/crates/nu-command/src/strings/char_.rs index 92ab7f9da8..b61918e183 100644 --- a/crates/nu-command/src/strings/char_.rs +++ b/crates/nu-command/src/strings/char_.rs @@ -2,6 +2,7 @@ use indexmap::indexmap; use indexmap::map::IndexMap; use nu_engine::CallExt; use nu_protocol::engine::{EngineState, Stack}; +use nu_protocol::record; use nu_protocol::{ ast::Call, engine::Command, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, @@ -232,9 +233,6 @@ impl Command for Char { return Ok(CHAR_MAP .iter() .map(move |(name, s)| { - let cols = vec!["name".into(), "character".into(), "unicode".into()]; - let name: Value = Value::string(String::from(*name), call_span); - let character = Value::string(s, call_span); let unicode = Value::string( s.chars() .map(|c| format!("{:x}", c as u32)) @@ -242,12 +240,13 @@ impl Command for Char { .join(" "), call_span, ); - let vals = vec![name, character, unicode]; - Value::Record { - cols, - vals, - span: call_span, - } + let record = record! { + "name" => Value::string(*name, call_span), + "character" => Value::string(s, call_span), + "unicode" => unicode, + }; + + Value::record(record, call_span) }) .into_pipeline_data(engine_state.ctrlc.clone())); } diff --git a/crates/nu-command/src/strings/detect_columns.rs b/crates/nu-command/src/strings/detect_columns.rs index 71262bde5f..74bfa2da09 100644 --- a/crates/nu-command/src/strings/detect_columns.rs +++ b/crates/nu-command/src/strings/detect_columns.rs @@ -6,8 +6,8 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoInterruptiblePipelineData, PipelineData, Range, ShellError, Signature, - Span, Spanned, SyntaxShape, Type, Value, + Category, Example, IntoInterruptiblePipelineData, PipelineData, Range, Record, ShellError, + Signature, Span, Spanned, SyntaxShape, Type, Value, }; type Input<'t> = Peekable>; @@ -64,7 +64,7 @@ impl Command for DetectColumns { description: "Splits string across multiple columns", example: "'a b c' | detect columns -n", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec![ "column0".to_string(), "column1".to_string(), @@ -75,8 +75,7 @@ impl Command for DetectColumns { Value::test_string("b"), Value::test_string("c"), ], - span, - }], + })], span, }), }, @@ -211,11 +210,7 @@ fn detect_columns( }; if !(l_idx <= r_idx && (r_idx >= 0 || l_idx < (cols.len() as isize))) { - return Value::Record { - cols, - vals, - span: name_span, - }; + return Value::record(Record { cols, vals }, name_span); } (l_idx.max(0) as usize, (r_idx as usize + 1).min(cols.len())) @@ -228,11 +223,7 @@ fn detect_columns( } } } else { - return Value::Record { - cols, - vals, - span: name_span, - }; + return Value::record(Record { cols, vals }, name_span); }; // Merge Columns @@ -254,11 +245,7 @@ fn detect_columns( vals.push(binding); last_seg.into_iter().for_each(|v| vals.push(v)); - Value::Record { - cols, - vals, - span: name_span, - } + Value::record(Record { cols, vals }, name_span) }) .into_pipeline_data(ctrlc)) } else { diff --git a/crates/nu-command/src/strings/parse.rs b/crates/nu-command/src/strings/parse.rs index c2dc6b63b8..2a520f5e46 100644 --- a/crates/nu-command/src/strings/parse.rs +++ b/crates/nu-command/src/strings/parse.rs @@ -6,8 +6,8 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, ListStream, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, - Type, Value, + Category, Example, ListStream, PipelineData, Record, ShellError, Signature, Span, Spanned, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -44,11 +44,10 @@ impl Command for Parse { fn examples(&self) -> Vec { let result = Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["foo".to_string(), "bar".to_string()], vals: vec![Value::test_string("hi"), Value::test_string("there")], - span: Span::test_data(), - }], + })], span: Span::test_data(), }; @@ -67,11 +66,10 @@ impl Command for Parse { description: "Parse a string using fancy-regex named capture group pattern", example: "\"foo bar.\" | parse -r '\\s*(?\\w+)(?=\\.)'", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["name".to_string()], vals: vec![Value::test_string("bar")], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }, @@ -80,16 +78,14 @@ impl Command for Parse { example: "\"foo! bar.\" | parse -r '(\\w+)(?=\\.)|(\\w+)(?=!)'", result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["capture0".to_string(), "capture1".to_string()], vals: vec![Value::test_string(""), Value::test_string("foo")], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["capture0".to_string(), "capture1".to_string()], vals: vec![Value::test_string("bar"), Value::test_string("")], - span: Span::test_data(), - }, + }), ], span: Span::test_data(), }), @@ -99,14 +95,13 @@ impl Command for Parse { example: "\" @another(foo bar) \" | parse -r '\\s*(?<=[() ])(@\\w+)(\\([^)]*\\))?\\s*'", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["capture0".to_string(), "capture1".to_string()], vals: vec![ Value::test_string("@another"), Value::test_string("(foo bar)"), ], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }, @@ -114,11 +109,10 @@ impl Command for Parse { description: "Parse a string using fancy-regex look ahead atomic group pattern", example: "\"abcd\" | parse -r '^a(bc(?=d)|b)cd$'", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["capture0".to_string()], vals: vec![Value::test_string("b")], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }, @@ -179,7 +173,6 @@ fn operate( let results = regex_pattern.captures_iter(&s); for c in results { - let mut cols = Vec::with_capacity(columns.len()); let captures = match c { Ok(c) => c, Err(e) => { @@ -192,22 +185,18 @@ fn operate( )) } }; - let mut vals = Vec::with_capacity(captures.len()); - for (column_name, cap) in columns.iter().zip(captures.iter().skip(1)) { - let cap_string = cap.map(|v| v.as_str()).unwrap_or("").to_string(); - cols.push(column_name.clone()); - vals.push(Value::String { - val: cap_string, - span: v.span()?, - }); - } + let v_span = v.span()?; + let record = columns + .iter() + .zip(captures.iter().skip(1)) + .map(|(column_name, cap)| { + let cap_string = cap.map(|v| v.as_str()).unwrap_or(""); + (column_name.clone(), Value::string(cap_string, v_span)) + }) + .collect(); - parsed.push(Value::Record { - cols, - vals, - span: head, - }); + parsed.push(Value::record(record, head)); } } Err(_) => { @@ -449,7 +438,6 @@ fn stream_helper( let results = regex.captures_iter(&s); for c in results { - let mut cols = Vec::with_capacity(columns.len()); let captures = match c { Ok(c) => c, Err(e) => { @@ -464,18 +452,17 @@ fn stream_helper( }) } }; - let mut vals = Vec::with_capacity(captures.len()); - for (column_name, cap) in columns.iter().zip(captures.iter().skip(1)) { - let cap_string = cap.map(|v| v.as_str()).unwrap_or("").to_string(); - cols.push(column_name.clone()); - vals.push(Value::String { - val: cap_string, - span, - }); - } + let record = columns + .iter() + .zip(captures.iter().skip(1)) + .map(|(column_name, cap)| { + let cap_string = cap.map(|v| v.as_str()).unwrap_or(""); + (column_name.clone(), Value::string(cap_string, span)) + }) + .collect(); - excess.push(Value::Record { cols, vals, span }); + excess.push(Value::record(record, span)); } if !excess.is_empty() { diff --git a/crates/nu-command/src/strings/size.rs b/crates/nu-command/src/strings/size.rs index faffbc09dd..c4dab8296a 100644 --- a/crates/nu-command/src/strings/size.rs +++ b/crates/nu-command/src/strings/size.rs @@ -1,7 +1,9 @@ use fancy_regex::Regex; 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::{ + record, Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value, +}; use std::collections::BTreeMap; use std::{fmt, str}; use unicode_segmentation::UnicodeSegmentation; @@ -46,7 +48,7 @@ impl Command for Size { Example { description: "Count the number of words in a string", example: r#""There are seven words in this sentence" | size"#, - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec![ "lines".into(), "words".into(), @@ -61,13 +63,12 @@ impl Command for Size { Value::test_int(38), Value::test_int(38), ], - span: Span::test_data(), - }), + })), }, Example { description: "Counts unicode characters", example: r#"'今天天气真好' | size "#, - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec![ "lines".into(), "words".into(), @@ -82,13 +83,12 @@ impl Command for Size { Value::test_int(6), Value::test_int(6), ], - span: Span::test_data(), - }), + })), }, Example { description: "Counts Unicode characters correctly in a string", example: r#""Amélie Amelie" | size"#, - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec![ "lines".into(), "words".into(), @@ -103,8 +103,7 @@ impl Command for Size { Value::test_int(14), Value::test_int(13), ], - span: Span::test_data(), - }), + })), }, ] } @@ -145,55 +144,20 @@ fn size( fn counter(contents: &str, span: Span) -> Value { let counts = uwc_count(&ALL_COUNTERS[..], contents); - let mut cols = vec![]; - let mut vals = vec![]; - cols.push("lines".into()); - vals.push(Value::Int { - val: match counts.get(&Counter::Lines) { - Some(c) => *c as i64, - None => 0, - }, - span, - }); + fn get_count(counts: &BTreeMap, counter: Counter, span: Span) -> Value { + Value::int(counts.get(&counter).copied().unwrap_or(0) as i64, span) + } - cols.push("words".into()); - vals.push(Value::Int { - val: match counts.get(&Counter::Words) { - Some(c) => *c as i64, - None => 0, - }, - span, - }); + let record = record! { + "lines" => get_count(&counts, Counter::Lines, span), + "words" => get_count(&counts, Counter::Words, span), + "bytes" => get_count(&counts, Counter::Bytes, span), + "chars" => get_count(&counts, Counter::CodePoints, span), + "graphemes" => get_count(&counts, Counter::GraphemeClusters, span), + }; - cols.push("bytes".into()); - vals.push(Value::Int { - val: match counts.get(&Counter::Bytes) { - Some(c) => *c as i64, - None => 0, - }, - span, - }); - - cols.push("chars".into()); - vals.push(Value::Int { - val: match counts.get(&Counter::CodePoints) { - Some(c) => *c as i64, - None => 0, - }, - span, - }); - - cols.push("graphemes".into()); - vals.push(Value::Int { - val: match counts.get(&Counter::GraphemeClusters) { - Some(c) => *c as i64, - None => 0, - }, - span, - }); - - Value::Record { cols, vals, span } + Value::record(record, span) } /// Take all the counts in `other_counts` and sum them into `accum`. diff --git a/crates/nu-command/src/strings/split/column.rs b/crates/nu-command/src/strings/split/column.rs index 7852aa49b7..3b95bbeea2 100644 --- a/crates/nu-command/src/strings/split/column.rs +++ b/crates/nu-command/src/strings/split/column.rs @@ -2,8 +2,8 @@ use nu_engine::CallExt; use nu_protocol::{ ast::Call, 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, }; use regex::Regex; @@ -64,7 +64,7 @@ impl Command for SubCommand { description: "Split a string into columns by the specified separator", example: "'a--b--c' | split column '--'", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec![ "column1".to_string(), "column2".to_string(), @@ -75,8 +75,7 @@ impl Command for SubCommand { Value::test_string("b"), Value::test_string("c"), ], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }, @@ -84,7 +83,7 @@ impl Command for SubCommand { description: "Split a string into columns of char and remove the empty columns", example: "'abc' | split column -c ''", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec![ "column1".to_string(), "column2".to_string(), @@ -95,8 +94,7 @@ impl Command for SubCommand { Value::test_string("b"), Value::test_string("c"), ], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }, @@ -105,16 +103,14 @@ impl Command for SubCommand { example: "['a-b' 'c-d'] | split column -", result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["column1".to_string(), "column2".to_string()], vals: vec![Value::test_string("a"), Value::test_string("b")], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["column1".to_string(), "column2".to_string()], vals: vec![Value::test_string("c"), Value::test_string("d")], - span: Span::test_data(), - }, + }), ], span: Span::test_data(), }), @@ -124,16 +120,14 @@ impl Command for SubCommand { example: r"['a - b' 'c - d'] | split column -r '\s*-\s*'", result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["column1".to_string(), "column2".to_string()], vals: vec![Value::test_string("a"), Value::test_string("b")], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["column1".to_string(), "column2".to_string()], vals: vec![Value::test_string("c"), Value::test_string("d")], - span: Span::test_data(), - }, + }), ], span: Span::test_data(), }), @@ -190,8 +184,7 @@ fn split_column_helper( let positional: Vec<_> = rest.iter().map(|f| f.item.clone()).collect(); // If they didn't provide column names, make up our own - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); if positional.is_empty() { let mut gen_columns = vec![]; for i in 0..split_result.len() { @@ -199,20 +192,14 @@ fn split_column_helper( } for (&k, v) in split_result.iter().zip(&gen_columns) { - cols.push(v.to_string()); - vals.push(Value::string(k, head)); + record.push(v, Value::string(k, head)); } } else { for (&k, v) in split_result.iter().zip(&positional) { - cols.push(v.into()); - vals.push(Value::string(k, head)); + record.push(v, Value::string(k, head)); } } - vec![Value::Record { - cols, - vals, - span: head, - }] + vec![Value::record(record, head)] } else { match v.span() { Ok(span) => vec![Value::Error { diff --git a/crates/nu-command/src/strings/str_/case/capitalize.rs b/crates/nu-command/src/strings/str_/case/capitalize.rs index 2f105a85e8..a5fcaf51a3 100644 --- a/crates/nu-command/src/strings/str_/case/capitalize.rs +++ b/crates/nu-command/src/strings/str_/case/capitalize.rs @@ -3,7 +3,9 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; +use nu_protocol::{ + Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value, +}; #[derive(Clone)] pub struct SubCommand; @@ -67,11 +69,10 @@ impl Command for SubCommand { description: "Capitalize a column in a table", example: "[[lang, gems]; [nu_test, 100]] | str capitalize 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(), }), }, diff --git a/crates/nu-command/src/strings/str_/case/downcase.rs b/crates/nu-command/src/strings/str_/case/downcase.rs index 4bfbe69682..fb5d01fb4a 100644 --- a/crates/nu-command/src/strings/str_/case/downcase.rs +++ b/crates/nu-command/src/strings/str_/case/downcase.rs @@ -3,7 +3,9 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; +use nu_protocol::{ + Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value, +}; #[derive(Clone)] pub struct SubCommand; @@ -67,11 +69,10 @@ impl Command for SubCommand { description: "Downcase contents", example: "[[ColA ColB]; [Test ABC]] | str downcase ColA", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["ColA".to_string(), "ColB".to_string()], vals: vec![Value::test_string("test"), Value::test_string("ABC")], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }, @@ -79,11 +80,10 @@ impl Command for SubCommand { description: "Downcase contents", example: "[[ColA ColB]; [Test ABC]] | str downcase ColA ColB", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["ColA".to_string(), "ColB".to_string()], vals: vec![Value::test_string("test"), Value::test_string("abc")], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }, diff --git a/crates/nu-command/src/strings/str_/contains.rs b/crates/nu-command/src/strings/str_/contains.rs index e9ddf3eb83..740fa4451f 100644 --- a/crates/nu-command/src/strings/str_/contains.rs +++ b/crates/nu-command/src/strings/str_/contains.rs @@ -3,8 +3,9 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; +use nu_protocol::{ + Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value, +}; #[derive(Clone)] pub struct SubCommand; @@ -88,21 +89,19 @@ impl Command for SubCommand { Example { description: "Check if input contains string in a record", example: "{ ColA: test, ColB: 100 } | str contains 'e' ColA", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["ColA".to_string(), "ColB".to_string()], vals: vec![Value::test_bool(true), Value::test_int(100)], - span: Span::test_data(), - }), + })), }, Example { description: "Check if input contains string in a table", example: " [[ColA ColB]; [test 100]] | str contains -i 'E' ColA", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["ColA".to_string(), "ColB".to_string()], vals: vec![Value::test_bool(true), Value::test_int(100)], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }, @@ -110,11 +109,10 @@ impl Command for SubCommand { description: "Check if input contains string in a table", example: " [[ColA ColB]; [test hello]] | str contains 'e' ColA ColB", result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["ColA".to_string(), "ColB".to_string()], vals: vec![Value::test_bool(true), Value::test_bool(true)], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }, diff --git a/crates/nu-command/src/strings/str_/distance.rs b/crates/nu-command/src/strings/str_/distance.rs index 45981a02b0..31d102a5f9 100644 --- a/crates/nu-command/src/strings/str_/distance.rs +++ b/crates/nu-command/src/strings/str_/distance.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - levenshtein_distance, Category, Example, PipelineData, ShellError, Signature, Span, + levenshtein_distance, Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value, }; @@ -82,11 +82,10 @@ impl Command for SubCommand { example: "[{a: 'nutshell' b: 'numetal'}] | str distance 'nushell' 'a' 'b'", result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_int(1), Value::test_int(4)], - span: Span::test_data(), - } + }) ], span: Span::test_data(), }), @@ -95,11 +94,10 @@ impl Command for SubCommand { description: "Compute edit distance between strings in record and another string, using cell paths", example: "{a: 'nutshell' b: 'numetal'} | str distance 'nushell' a b", result: Some( - Value::Record { + Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_int(1), Value::test_int(4)], - span: Span::test_data(), - } + }) ), }] } diff --git a/crates/nu-command/src/strings/str_/replace.rs b/crates/nu-command/src/strings/str_/replace.rs index 174b0f08a2..8d42c6b259 100644 --- a/crates/nu-command/src/strings/str_/replace.rs +++ b/crates/nu-command/src/strings/str_/replace.rs @@ -4,8 +4,8 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - report_error_new, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Type, Value, + report_error_new, Category, Example, PipelineData, Record, ShellError, Signature, Span, + Spanned, SyntaxShape, Type, Value, }; struct Arguments { @@ -150,15 +150,14 @@ impl Command for SubCommand { example: "[[ColA ColB ColC]; [abc abc ads]] | str replace -ar 'b' 'z' 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_string("azc"), Value::test_string("abc"), Value::test_string("ads"), ], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }, @@ -166,15 +165,14 @@ impl Command for SubCommand { description: "Find and replace all occurrences of find string in record using regular expression", example: "{ KeyA: abc, KeyB: abc, KeyC: ads } | str replace -ar 'b' 'z' KeyA KeyC", - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["KeyA".to_string(), "KeyB".to_string(), "KeyC".to_string()], vals: vec![ Value::test_string("azc"), Value::test_string("abc"), Value::test_string("ads"), ], - span: Span::test_data(), - }), + })), }, Example { description: "Find and replace contents without using the replace parameter as a regular expression", diff --git a/crates/nu-command/src/strings/str_/trim/trim_.rs b/crates/nu-command/src/strings/str_/trim/trim_.rs index 99fe2ba756..39b8ce9206 100644 --- a/crates/nu-command/src/strings/str_/trim/trim_.rs +++ b/crates/nu-command/src/strings/str_/trim/trim_.rs @@ -1,10 +1,10 @@ use nu_cmd_base::input_handler::{operate, CmdArgument}; use nu_engine::CallExt; -use nu_protocol::Category; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, + Category, Example, PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape, + Type, Value, }; #[derive(Clone)] @@ -180,14 +180,16 @@ fn action(input: &Value, arg: &Arguments, head: Span) -> Value { Value::Error { .. } => input.clone(), other => match mode { ActionMode::Global => match other { - Value::Record { cols, vals, span } => { - let new_vals = vals.iter().map(|v| action(v, arg, head)).collect(); + Value::Record { val: record, span } => { + let new_vals = record.vals.iter().map(|v| action(v, arg, head)).collect(); - Value::Record { - cols: cols.to_vec(), - vals: new_vals, - span: *span, - } + Value::record( + Record { + cols: record.cols.to_vec(), + vals: new_vals, + }, + *span, + ) } Value::List { vals, span } => { let new_vals = vals.iter().map(|v| action(v, arg, head)).collect(); @@ -249,14 +251,12 @@ mod tests { } fn make_record(cols: Vec<&str>, vals: Vec<&str>) -> Value { - Value::Record { - cols: cols.iter().map(|x| x.to_string()).collect(), - vals: vals - .iter() - .map(|x| Value::test_string(x.to_string())) + Value::test_record( + cols.into_iter() + .zip(vals) + .map(|(col, val)| (col.to_owned(), Value::test_string(val))) .collect(), - span: Span::test_data(), - } + ) } fn make_list(vals: Vec<&str>) -> Value { diff --git a/crates/nu-command/src/system/complete.rs b/crates/nu-command/src/system/complete.rs index 016b134884..1012e4d9be 100644 --- a/crates/nu-command/src/system/complete.rs +++ b/crates/nu-command/src/system/complete.rs @@ -1,7 +1,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value, + Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Type, Value, }; use std::thread; @@ -42,8 +42,7 @@ impl Command for Complete { exit_code, .. } => { - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); // use a thread to receive stderr message. // Or we may get a deadlock if child process sends out too much bytes to stdout. @@ -78,46 +77,35 @@ impl Command for Complete { }); if let Some(stdout) = stdout { - cols.push("stdout".to_string()); let stdout = stdout.into_bytes()?; - if let Ok(st) = String::from_utf8(stdout.item.clone()) { - vals.push(Value::String { - val: st, - span: stdout.span, - }) - } else { - vals.push(Value::Binary { - val: stdout.item, - span: stdout.span, - }) - } + record.push( + "stdout", + if let Ok(st) = String::from_utf8(stdout.item.clone()) { + Value::string(st, stdout.span) + } else { + Value::binary(stdout.item, stdout.span) + }, + ) } if let Some((handler, stderr_span)) = stderr_handler { - cols.push("stderr".to_string()); let res = handler.join().map_err(|err| ShellError::ExternalCommand { label: "Fail to receive external commands stderr message".to_string(), help: format!("{err:?}"), span: stderr_span, })??; - vals.push(res) + record.push("stderr", res) }; if let Some(exit_code) = exit_code { let mut v: Vec<_> = exit_code.collect(); if let Some(v) = v.pop() { - cols.push("exit_code".to_string()); - vals.push(v); + record.push("exit_code", v); } } - Ok(Value::Record { - cols, - vals, - span: call.head, - } - .into_pipeline_data()) + Ok(Value::record(record, call.head).into_pipeline_data()) } _ => Err(ShellError::GenericError( "Complete only works with external streams".to_string(), diff --git a/crates/nu-command/src/system/ps.rs b/crates/nu-command/src/system/ps.rs index c549efa8a6..80d449e95d 100644 --- a/crates/nu-command/src/system/ps.rs +++ b/crates/nu-command/src/system/ps.rs @@ -3,8 +3,8 @@ use std::time::Duration; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type, - Value, + Category, Example, IntoInterruptiblePipelineData, PipelineData, Record, ShellError, Signature, + Type, Value, }; #[derive(Clone)] @@ -82,81 +82,41 @@ fn run_ps(engine_state: &EngineState, call: &Call) -> Result Value::string(name, call_span), + "value" => nu_value, + "type" => Value::string(format!("{:?}", reg_type), call_span), + }, + *registry_key_span, + )) } Ok(reg_values.into_pipeline_data(engine_state.ctrlc.clone())) } else { @@ -138,15 +137,14 @@ fn registry_query( match reg_value { Ok(val) => { let (nu_value, reg_type) = reg_value_to_nu_value(val, call_span); - Ok(Value::Record { - cols: vec!["name".to_string(), "value".to_string(), "type".to_string()], - vals: vec![ - Value::string(value.item, call_span), - nu_value, - Value::string(format!("{:?}", reg_type), call_span), - ], - span: value.span, - } + Ok(Value::record( + record! { + "name" => Value::string(value.item, call_span), + "value" => nu_value, + "type" => Value::string(format!("{:?}", reg_type), call_span), + }, + value.span, + ) .into_pipeline_data()) } Err(_) => Err(ShellError::GenericError( diff --git a/crates/nu-command/src/system/sys.rs b/crates/nu-command/src/system/sys.rs index 1188d6812d..072bfbac0e 100644 --- a/crates/nu-command/src/system/sys.rs +++ b/crates/nu-command/src/system/sys.rs @@ -3,8 +3,8 @@ use chrono::Local; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, IntoPipelineData, LazyRecord, PipelineData, ShellError, Signature, Span, - Type, Value, + record, Category, Example, IntoPipelineData, LazyRecord, PipelineData, Record, ShellError, + Signature, Span, Type, Value, }; use std::time::{Duration, UNIX_EPOCH}; use sysinfo::{ @@ -118,52 +118,20 @@ pub fn disks(span: Span) -> Value { let mut output = vec![]; for disk in sys.disks() { - let mut cols = vec![]; - let mut vals = vec![]; + let device = trim_cstyle_null(disk.name().to_string_lossy().to_string()); + let typ = trim_cstyle_null(String::from_utf8_lossy(disk.file_system()).to_string()); - cols.push("device".into()); - vals.push(Value::String { - val: trim_cstyle_null(disk.name().to_string_lossy().to_string()), - span, - }); + let record = record! { + "device" => Value::string(device, span), + "type" => Value::string(typ, span), + "mount" => Value::string(disk.mount_point().to_string_lossy(), span), + "total" => Value::filesize(disk.total_space() as i64, span), + "free" => Value::filesize(disk.available_space() as i64, span), + "removable" => Value::bool(disk.is_removable(), span), + "kind" => Value::string(format!("{:?}", disk.kind()), span), + }; - cols.push("type".into()); - vals.push(Value::String { - val: trim_cstyle_null(String::from_utf8_lossy(disk.file_system()).to_string()), - span, - }); - - cols.push("mount".into()); - vals.push(Value::String { - val: disk.mount_point().to_string_lossy().to_string(), - span, - }); - - cols.push("total".into()); - vals.push(Value::Filesize { - val: disk.total_space() as i64, - span, - }); - - cols.push("free".into()); - vals.push(Value::Filesize { - val: disk.available_space() as i64, - span, - }); - - cols.push("removable".into()); - vals.push(Value::Bool { - val: disk.is_removable(), - span, - }); - - cols.push("kind".into()); - vals.push(Value::String { - val: format!("{:?}", disk.kind()), - span, - }); - - output.push(Value::Record { cols, vals, span }); + output.push(Value::record(record, span)); } Value::List { vals: output, span } } @@ -175,28 +143,13 @@ pub fn net(span: Span) -> Value { let mut output = vec![]; for (iface, data) in sys.networks() { - let mut cols = vec![]; - let mut vals = vec![]; + let record = record! { + "name" => Value::string(trim_cstyle_null(iface.to_string()), span), + "sent" => Value::filesize(data.total_transmitted() as i64, span), + "recv" => Value::filesize(data.total_received() as i64, span), + }; - cols.push("name".into()); - vals.push(Value::String { - val: trim_cstyle_null(iface.to_string()), - span, - }); - - cols.push("sent".into()); - vals.push(Value::Filesize { - val: data.total_transmitted() as i64, - span, - }); - - cols.push("recv".into()); - vals.push(Value::Filesize { - val: data.total_received() as i64, - span, - }); - - output.push(Value::Record { cols, vals, span }); + output.push(Value::record(record, span)); } Value::List { vals: output, span } } @@ -212,54 +165,26 @@ pub fn cpu(span: Span) -> Value { let mut output = vec![]; for cpu in sys.cpus() { - let mut cols = vec![]; - let mut vals = vec![]; - - cols.push("name".into()); - vals.push(Value::String { - val: trim_cstyle_null(cpu.name().to_string()), - span, - }); - - cols.push("brand".into()); - vals.push(Value::String { - val: trim_cstyle_null(cpu.brand().to_string()), - span, - }); - - cols.push("freq".into()); - vals.push(Value::Int { - val: cpu.frequency() as i64, - span, - }); - - cols.push("cpu_usage".into()); - // sysinfo CPU usage numbers are not very precise unless you wait a long time between refreshes. // Round to 1DP (chosen somewhat arbitrarily) so people aren't misled by high-precision floats. let rounded_usage = (cpu.cpu_usage() * 10.0).round() / 10.0; - vals.push(Value::Float { - val: rounded_usage as f64, - span, - }); let load_avg = sys.load_average(); - cols.push("load_average".into()); - vals.push(Value::String { - val: trim_cstyle_null(format!( - "{:.2}, {:.2}, {:.2}", - load_avg.one, load_avg.five, load_avg.fifteen - )), - span, - }); + let load_avg = trim_cstyle_null(format!( + "{:.2}, {:.2}, {:.2}", + load_avg.one, load_avg.five, load_avg.fifteen + )); - cols.push("vendor_id".into()); - vals.push(Value::String { - val: trim_cstyle_null(cpu.vendor_id().to_string()), - span, - }); + let record = record! { + "name" => Value::string(trim_cstyle_null(cpu.name().to_string()), span), + "brand" => Value::string(trim_cstyle_null(cpu.brand().to_string()), span), + "freq" => Value::int(cpu.frequency() as i64, span), + "cpu_usage" => Value::float(rounded_usage as f64, span), + "load_average" => Value::string(load_avg, span), + "vendor_id" => Value::string(trim_cstyle_null(cpu.vendor_id().to_string()), span), + }; - output.push(Value::Record { cols, vals, span }); + output.push(Value::record(record, span)); } Value::List { vals: output, span } @@ -269,9 +194,6 @@ pub fn mem(span: Span) -> Value { let mut sys = System::new(); sys.refresh_memory(); - let mut cols = vec![]; - let mut vals = vec![]; - let total_mem = sys.total_memory(); let free_mem = sys.free_memory(); let used_mem = sys.used_memory(); @@ -281,101 +203,53 @@ pub fn mem(span: Span) -> Value { let free_swap = sys.free_swap(); let used_swap = sys.used_swap(); - cols.push("total".into()); - vals.push(Value::Filesize { - val: total_mem as i64, - span, - }); + let record = record! { + "total" => Value::filesize(total_mem as i64, span), + "free" => Value::filesize(free_mem as i64, span), + "used" => Value::filesize(used_mem as i64, span), + "available" => Value::filesize(avail_mem as i64, span), + "swap total" => Value::filesize(total_swap as i64, span), + "swap free" => Value::filesize(free_swap as i64, span), + "swap used" => Value::filesize(used_swap as i64, span), + }; - cols.push("free".into()); - vals.push(Value::Filesize { - val: free_mem as i64, - span, - }); - - cols.push("used".into()); - vals.push(Value::Filesize { - val: used_mem as i64, - span, - }); - - cols.push("available".into()); - vals.push(Value::Filesize { - val: avail_mem as i64, - span, - }); - - cols.push("swap total".into()); - vals.push(Value::Filesize { - val: total_swap as i64, - span, - }); - - cols.push("swap free".into()); - vals.push(Value::Filesize { - val: free_swap as i64, - span, - }); - - cols.push("swap used".into()); - vals.push(Value::Filesize { - val: used_swap as i64, - span, - }); - - Value::Record { cols, vals, span } + Value::record(record, span) } pub fn host(span: Span) -> Value { let mut sys = System::new(); sys.refresh_users_list(); - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); if let Some(name) = sys.name() { - cols.push("name".into()); - vals.push(Value::String { - val: trim_cstyle_null(name), - span, - }); + record.push("name", Value::string(trim_cstyle_null(name), span)); } if let Some(version) = sys.os_version() { - cols.push("os_version".into()); - vals.push(Value::String { - val: trim_cstyle_null(version), - span, - }); + record.push("os_version", Value::string(trim_cstyle_null(version), span)); } if let Some(long_version) = sys.long_os_version() { - cols.push("long_os_version".into()); - vals.push(Value::String { - val: trim_cstyle_null(long_version), - span, - }); + record.push( + "long_os_version", + Value::string(trim_cstyle_null(long_version), span), + ); } if let Some(version) = sys.kernel_version() { - cols.push("kernel_version".into()); - vals.push(Value::String { - val: trim_cstyle_null(version), - span, - }); + record.push( + "kernel_version", + Value::string(trim_cstyle_null(version), span), + ); } if let Some(hostname) = sys.host_name() { - cols.push("hostname".into()); - vals.push(Value::String { - val: trim_cstyle_null(hostname), - span, - }); + record.push("hostname", Value::string(trim_cstyle_null(hostname), span)); } - cols.push("uptime".into()); - vals.push(Value::Duration { - val: 1000000000 * sys.uptime() as i64, - span, - }); + record.push( + "uptime", + Value::duration(1000000000 * sys.uptime() as i64, span), + ); // Creates a new SystemTime from the specified number of whole seconds let d = UNIX_EPOCH + Duration::from_secs(sys.boot_time()); @@ -384,23 +258,10 @@ pub fn host(span: Span) -> Value { // Convert to local time and then rfc3339 let timestamp_str = datetime.with_timezone(datetime.offset()).to_rfc3339(); - cols.push("boot_time".into()); - vals.push(Value::String { - val: timestamp_str, - span, - }); + record.push("boot_time", Value::string(timestamp_str, span)); let mut users = vec![]; for user in sys.users() { - let mut cols = vec![]; - let mut vals = vec![]; - - cols.push("name".into()); - vals.push(Value::String { - val: trim_cstyle_null(user.name().to_string()), - span, - }); - let mut groups = vec![]; for group in user.groups() { groups.push(Value::String { @@ -409,18 +270,19 @@ pub fn host(span: Span) -> Value { }); } - cols.push("groups".into()); - vals.push(Value::List { vals: groups, span }); + let record = record! { + "name" => Value::string(trim_cstyle_null(user.name().to_string()), span), + "groups" => Value::list(groups, span), + }; - users.push(Value::Record { cols, vals, span }); + users.push(Value::record(record, span)); } if !users.is_empty() { - cols.push("sessions".into()); - vals.push(Value::List { vals: users, span }); + record.push("sessions", Value::list(users, span)); } - Value::Record { cols, vals, span } + Value::record(record, span) } pub fn temp(span: Span) -> Value { @@ -431,35 +293,16 @@ pub fn temp(span: Span) -> Value { let mut output = vec![]; for component in sys.components() { - let mut cols = vec![]; - let mut vals = vec![]; - - cols.push("unit".into()); - vals.push(Value::String { - val: component.label().to_string(), - span, - }); - - cols.push("temp".into()); - vals.push(Value::Float { - val: component.temperature() as f64, - span, - }); - - cols.push("high".into()); - vals.push(Value::Float { - val: component.max() as f64, - span, - }); + let mut record = record! { + "unit" => Value::string(component.label(), span), + "temp" => Value::float(component.temperature() as f64, span), + "high" => Value::float(component.max() as f64, span), + }; if let Some(critical) = component.critical() { - cols.push("critical".into()); - vals.push(Value::Float { - val: critical as f64, - span, - }); + record.push("critical", Value::float(critical as f64, span)); } - output.push(Value::Record { cols, vals, span }); + output.push(Value::record(record, span)); } Value::List { vals: output, span } diff --git a/crates/nu-command/src/system/which_.rs b/crates/nu-command/src/system/which_.rs index a7054a4f80..077770d2e4 100644 --- a/crates/nu-command/src/system/which_.rs +++ b/crates/nu-command/src/system/which_.rs @@ -1,6 +1,7 @@ use log::trace; use nu_engine::env; use nu_engine::CallExt; +use nu_protocol::record; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, @@ -63,19 +64,14 @@ fn entry( cmd_type: impl Into, span: Span, ) -> Value { - let mut cols = vec![]; - let mut vals = vec![]; - - cols.push("command".to_string()); - vals.push(Value::string(arg.into(), span)); - - cols.push("path".to_string()); - vals.push(Value::string(path.into(), span)); - - cols.push("type".to_string()); - vals.push(Value::string(cmd_type.into(), span)); - - Value::Record { cols, vals, span } + Value::record( + record! { + "command" => Value::string(arg.into(), span), + "path" => Value::string(path.into(), span), + "type" => Value::string(cmd_type.into(), span), + }, + span, + ) } fn get_entry_in_commands(engine_state: &EngineState, name: &str, span: Span) -> Option { diff --git a/crates/nu-command/src/viewers/griddle.rs b/crates/nu-command/src/viewers/griddle.rs index 7b1a2e2ce5..95e586267c 100644 --- a/crates/nu-command/src/viewers/griddle.rs +++ b/crates/nu-command/src/viewers/griddle.rs @@ -110,11 +110,11 @@ prints out the list properly."# Ok(PipelineData::empty()) } } - PipelineData::Value(Value::Record { cols, vals, .. }, ..) => { + PipelineData::Value(Value::Record { val, .. }, ..) => { // dbg!("value::record"); let mut items = vec![]; - for (i, (c, v)) in cols.into_iter().zip(vals).enumerate() { + for (i, (c, v)) in val.into_iter().enumerate() { items.push((i, c, v.into_string(", ", config))) } diff --git a/crates/nu-command/src/viewers/table.rs b/crates/nu-command/src/viewers/table.rs index f5ef483b5f..bf0cc97d77 100644 --- a/crates/nu-command/src/viewers/table.rs +++ b/crates/nu-command/src/viewers/table.rs @@ -7,7 +7,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, Category, Config, DataSource, Example, IntoPipelineData, ListStream, PipelineData, - PipelineMetadata, RawStream, ShellError, Signature, Span, SyntaxShape, Type, Value, + PipelineMetadata, RawStream, Record, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use nu_table::common::create_nu_table_config; use nu_table::{ @@ -167,16 +167,14 @@ impl Command for Table { example: r#"[[a b]; [1 2] [3 4]] | table"#, result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_int(1), Value::test_int(2)], - span, - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_int(3), Value::test_int(4)], - span, - }, + }), ], span, }), @@ -186,16 +184,14 @@ impl Command for Table { example: r#"[[a b]; [1 2] [2 [4 4]]] | table --expand"#, result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_int(1), Value::test_int(2)], - span, - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_int(3), Value::test_int(4)], - span, - }, + }), ], span, }), @@ -205,16 +201,14 @@ impl Command for Table { example: r#"[[a b]; [1 2] [2 [4 4]]] | table --collapse"#, result: Some(Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_int(1), Value::test_int(2)], - span, - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_int(3), Value::test_int(4)], - span, - }, + }), ], span, }), @@ -276,12 +270,11 @@ fn handle_table_command( ctrlc, metadata, ), - PipelineData::Value(Value::Record { cols, vals, span }, ..) => { + PipelineData::Value(Value::Record { val, span }, ..) => { let term_width = get_width_param(term_width); handle_record( - cols, - vals, + val, span, engine_state, stack, @@ -344,8 +337,7 @@ fn supported_table_modes() -> Vec { #[allow(clippy::too_many_arguments)] fn handle_record( - cols: Vec, - vals: Vec, + record: Record, span: Span, engine_state: &EngineState, stack: &mut Stack, @@ -359,12 +351,12 @@ fn handle_record( let style_computer = &StyleComputer::from_config(engine_state, stack); let ctrlc1 = ctrlc.clone(); - let result = if cols.is_empty() { + let result = if record.is_empty() { create_empty_placeholder("record", term_width, engine_state, stack) } else { let indent = (config.table_indent.left, config.table_indent.right); let opts = TableOpts::new(config, style_computer, ctrlc, span, 0, term_width, indent); - let result = build_table_kv(cols, vals, table_view, opts, span)?; + let result = build_table_kv(record, table_view, opts, span)?; match result { Some(output) => maybe_strip_color(output, config), None => report_unsuccessful_output(ctrlc1, term_width), @@ -390,24 +382,23 @@ fn report_unsuccessful_output(ctrlc1: Option>, term_width: usize } fn build_table_kv( - cols: Vec, - vals: Vec, + record: Record, table_view: TableView, opts: TableOpts<'_>, span: Span, ) -> StringResult { match table_view { - TableView::General => JustTable::kv_table(&cols, &vals, opts), + TableView::General => JustTable::kv_table(&record, opts), TableView::Expanded { limit, flatten, flatten_separator, } => { let sep = flatten_separator.unwrap_or_else(|| String::from(' ')); - ExpandedTable::new(limit, flatten, sep).build_map(&cols, &vals, opts) + ExpandedTable::new(limit, flatten, sep).build_map(&record, opts) } TableView::Collapsed => { - let value = Value::Record { cols, vals, span }; + let value = Value::record(record, span); CollapsedTable::build(value, opts) } } @@ -460,16 +451,16 @@ fn handle_row_stream( ListStream::from_stream( stream.map(move |mut x| match &mut x { - Value::Record { cols, vals, .. } => { + Value::Record { val: record, .. } => { let mut idx = 0; - while idx < cols.len() { + while idx < record.len() { // Only the name column gets special colors, for now - if cols[idx] == "name" { - if let Some(Value::String { val, span }) = vals.get(idx) { + if record.cols[idx] == "name" { + if let Some(Value::String { val, span }) = record.vals.get(idx) { let val = render_path_name(val, &config, &ls_colors, *span); if let Some(val) = val { - vals[idx] = val; + record.vals[idx] = val; } } } @@ -492,14 +483,14 @@ fn handle_row_stream( ListStream::from_stream( stream.map(move |mut x| match &mut x { - Value::Record { cols, vals, .. } => { + Value::Record { val: record, .. } => { let mut idx = 0; // Every column in the HTML theme table except 'name' is colored - while idx < cols.len() { - if cols[idx] != "name" { + while idx < record.len() { + if record.cols[idx] != "name" { // Simple routine to grab the hex code, convert to a style, // then place it in a new Value::String. - if let Some(Value::String { val, span }) = vals.get(idx) { + if let Some(Value::String { val, span }) = record.vals.get(idx) { let s = match color_from_hex(val) { Ok(c) => match c { // .normal() just sets the text foreground color. @@ -508,7 +499,7 @@ fn handle_row_stream( }, Err(_) => nu_ansi_term::Style::default(), }; - vals[idx] = Value::String { + record.vals[idx] = Value::String { // Apply the style (ANSI codes) to the string val: s.paint(val).to_string(), span: *span, diff --git a/crates/nu-engine/src/column.rs b/crates/nu-engine/src/column.rs index aba520672d..879a3ced8f 100644 --- a/crates/nu-engine/src/column.rs +++ b/crates/nu-engine/src/column.rs @@ -4,11 +4,11 @@ use std::collections::HashSet; pub fn get_columns(input: &[Value]) -> Vec { let mut columns = vec![]; for item in input { - let Value::Record { cols, .. } = item else { + let Value::Record { val, .. } = item else { return vec![]; }; - for col in cols { + for col in &val.cols { if !columns.contains(col) { columns.push(col.to_string()); } diff --git a/crates/nu-engine/src/documentation.rs b/crates/nu-engine/src/documentation.rs index 494d2c4017..d95b3baa6a 100644 --- a/crates/nu-engine/src/documentation.rs +++ b/crates/nu-engine/src/documentation.rs @@ -1,7 +1,7 @@ use nu_protocol::{ ast::Call, engine::{EngineState, Stack}, - Category, Example, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Value, + record, Category, Example, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Value, }; use std::{collections::HashMap, fmt::Write}; @@ -210,14 +210,13 @@ fn get_documentation( let span = Span::unknown(); let mut vals = vec![]; for (input, output) in &sig.input_output_types { - vals.push(Value::Record { - cols: vec!["input".into(), "output".into()], - vals: vec![ - Value::string(input.to_string(), span), - Value::string(output.to_string(), span), - ], + vals.push(Value::record( + record! { + "input" => Value::string(input.to_string(), span), + "output" => Value::string(output.to_string(), span), + }, span, - }); + )); } let mut caller_stack = Stack::new(); diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 9568e80991..66485c9266 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -6,8 +6,9 @@ use nu_protocol::{ Operator, PathMember, PipelineElement, Redirection, }, engine::{EngineState, ProfilingConfig, Stack}, - DataSource, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, PipelineMetadata, - Range, ShellError, Span, Spanned, Unit, Value, VarId, ENV_VARIABLE_ID, + record, DataSource, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, + PipelineMetadata, Range, Record, ShellError, Span, Spanned, Unit, Value, VarId, + ENV_VARIABLE_ID, }; use std::time::Instant; use std::{collections::HashMap, path::PathBuf}; @@ -588,12 +589,12 @@ pub fn eval_expression( }) } Expr::Record(fields) => { - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); + for (col, val) in fields { // avoid duplicate cols. let col_name = eval_expression(engine_state, stack, col)?.as_string()?; - let pos = cols.iter().position(|c| c == &col_name); + let pos = record.cols.iter().position(|c| c == &col_name); match pos { Some(index) => { return Err(ShellError::ColumnDefinedTwice { @@ -602,17 +603,12 @@ pub fn eval_expression( }) } None => { - cols.push(col_name); - vals.push(eval_expression(engine_state, stack, val)?); + record.push(col_name, eval_expression(engine_state, stack, val)?); } } } - Ok(Value::Record { - cols, - vals, - span: expr.span, - }) + Ok(Value::record(record, expr.span)) } Expr::Table(headers, vals) => { let mut output_headers = vec![]; @@ -626,11 +622,13 @@ pub fn eval_expression( for expr in val { row.push(eval_expression(engine_state, stack, expr)?); } - output_rows.push(Value::Record { - cols: output_headers.clone(), - vals: row, - span: expr.span, - }); + output_rows.push(Value::record( + Record { + cols: output_headers.clone(), + vals: row, + }, + expr.span, + )); } Ok(Value::List { vals: output_rows, @@ -1241,201 +1239,154 @@ pub fn eval_nu_variable(engine_state: &EngineState, span: Span) -> Result { - path.push("history.sqlite3"); + record.push( + "history-path", + if let Some(mut path) = nu_path::config_dir() { + path.push("nushell"); + match engine_state.config.history_file_format { + nu_protocol::HistoryFileFormat::Sqlite => { + path.push("history.sqlite3"); + } + nu_protocol::HistoryFileFormat::PlainText => { + path.push("history.txt"); + } } - nu_protocol::HistoryFileFormat::PlainText => { - path.push("history.txt"); - } - } - let canon_hist_path = canonicalize_path(engine_state, &path); - vals.push(Value::String { - val: canon_hist_path.to_string_lossy().to_string(), - span, - }) - } else { - vals.push(Value::Error { - error: Box::new(ShellError::IOError("Could not find history path".into())), - }) - } + let canon_hist_path = canonicalize_path(engine_state, &path); + Value::string(canon_hist_path.to_string_lossy(), span) + } else { + Value::error(ShellError::IOError("Could not find history path".into())) + }, + ); - cols.push("loginshell-path".to_string()); - if let Some(mut path) = nu_path::config_dir() { - path.push("nushell"); - path.push("login.nu"); - let canon_login_path = canonicalize_path(engine_state, &path); - vals.push(Value::String { - val: canon_login_path.to_string_lossy().to_string(), - span, - }) - } else { - vals.push(Value::Error { - error: Box::new(ShellError::IOError( + record.push( + "loginshell-path", + if let Some(mut path) = nu_path::config_dir() { + path.push("nushell"); + path.push("login.nu"); + let canon_login_path = canonicalize_path(engine_state, &path); + Value::string(canon_login_path.to_string_lossy(), span) + } else { + Value::error(ShellError::IOError( "Could not find login shell path".into(), - )), - }) - } + )) + }, + ); #[cfg(feature = "plugin")] { - cols.push("plugin-path".to_string()); - - if let Some(path) = &engine_state.plugin_signatures { - let canon_plugin_path = canonicalize_path(engine_state, path); - vals.push(Value::String { - val: canon_plugin_path.to_string_lossy().to_string(), - span, - }) - } else { - vals.push(Value::Error { - error: Box::new(ShellError::IOError( + record.push( + "plugin-path", + if let Some(path) = &engine_state.plugin_signatures { + let canon_plugin_path = canonicalize_path(engine_state, path); + Value::string(canon_plugin_path.to_string_lossy(), span) + } else { + Value::error(ShellError::IOError( "Could not get plugin signature location".into(), - )), - }) - } + )) + }, + ); } - cols.push("home-path".to_string()); - if let Some(path) = nu_path::home_dir() { - let canon_home_path = canonicalize_path(engine_state, &path); - vals.push(Value::String { - val: canon_home_path.to_string_lossy().into(), + record.push( + "home-path", + if let Some(path) = nu_path::home_dir() { + let canon_home_path = canonicalize_path(engine_state, &path); + Value::string(canon_home_path.to_string_lossy(), span) + } else { + Value::error(ShellError::IOError("Could not get home path".into())) + }, + ); + + record.push("temp-path", { + let canon_temp_path = canonicalize_path(engine_state, &std::env::temp_dir()); + Value::string(canon_temp_path.to_string_lossy(), span) + }); + + record.push("pid", Value::int(std::process::id().into(), span)); + + record.push("os-info", { + let sys = sysinfo::System::new(); + let ver = match sys.kernel_version() { + Some(v) => v, + None => "unknown".into(), + }; + Value::record( + record! { + "name" => Value::string(std::env::consts::OS, span), + "arch" => Value::string(std::env::consts::ARCH, span), + "family" => Value::string(std::env::consts::FAMILY, span), + "kernel_version" => Value::string(ver, span), + }, span, - }) - } else { - vals.push(Value::Error { - error: Box::new(ShellError::IOError("Could not get home path".into())), - }) - } - - cols.push("temp-path".to_string()); - let canon_temp_path = canonicalize_path(engine_state, &std::env::temp_dir()); - vals.push(Value::String { - val: canon_temp_path.to_string_lossy().into(), - span, + ) }); - cols.push("pid".to_string()); - vals.push(Value::int(std::process::id().into(), span)); + record.push( + "startup-time", + Value::duration(engine_state.get_startup_time(), span), + ); - cols.push("os-info".to_string()); - let sys = sysinfo::System::new(); - let ver = match sys.kernel_version() { - Some(v) => v, - None => "unknown".into(), - }; - let os_record = Value::Record { - cols: vec![ - "name".into(), - "arch".into(), - "family".into(), - "kernel_version".into(), - ], - vals: vec![ - Value::string(std::env::consts::OS, span), - Value::string(std::env::consts::ARCH, span), - Value::string(std::env::consts::FAMILY, span), - Value::string(ver, span), - ], - span, - }; - vals.push(os_record); + record.push( + "is-interactive", + Value::bool(engine_state.is_interactive, span), + ); - cols.push("startup-time".to_string()); - vals.push(Value::Duration { - val: engine_state.get_startup_time(), - span, - }); + record.push("is-login", Value::bool(engine_state.is_login, span)); - cols.push("is-interactive".to_string()); - vals.push(Value::Bool { - val: engine_state.is_interactive, - span, - }); - - cols.push("is-login".to_string()); - vals.push(Value::Bool { - val: engine_state.is_login, - span, - }); - - cols.push("current-exe".to_string()); - if let Ok(current_exe) = std::env::current_exe() { - vals.push(Value::String { - val: current_exe.to_string_lossy().into(), - span, - }); - } else { - vals.push(Value::Error { - error: Box::new(ShellError::IOError( + record.push( + "current-exe", + if let Ok(current_exe) = std::env::current_exe() { + Value::string(current_exe.to_string_lossy(), span) + } else { + Value::error(ShellError::IOError( "Could not get current executable path".to_string(), - )), - }) - } + )) + }, + ); - Ok(Value::Record { cols, vals, span }) + Ok(Value::record(record, span)) } pub fn eval_variable( @@ -1459,13 +1410,7 @@ pub fn eval_variable( pairs.sort_by(|a, b| a.0.cmp(&b.0)); - let (env_columns, env_values) = pairs.into_iter().unzip(); - - Ok(Value::Record { - cols: env_columns, - vals: env_values, - span, - }) + Ok(Value::record(pairs.into_iter().collect(), span)) } var_id => stack.get_var(var_id, span), } @@ -1490,30 +1435,20 @@ fn collect_profiling_metadata( let element_str = Value::string(element_str, element_span); let time_ns = (end_time - start_time).as_nanos() as i64; - let mut cols = vec![ - "pipeline_idx".to_string(), - "element_idx".to_string(), - "depth".to_string(), - "span".to_string(), - ]; + let span_record = record! { + "start" => Value::int(element_span.start as i64, element_span), + "end" => Value::int(element_span.end as i64, element_span), + }; - let mut vals = vec![ - Value::int(pipeline_idx as i64, element_span), - Value::int(element_idx as i64, element_span), - Value::int(profiling_config.depth, element_span), - Value::record( - vec!["start".to_string(), "end".to_string()], - vec![ - Value::int(element_span.start as i64, element_span), - Value::int(element_span.end as i64, element_span), - ], - element_span, - ), - ]; + let mut record = record! { + "pipeline_idx" => Value::int(pipeline_idx as i64, element_span), + "element_idx" => Value::int(element_idx as i64, element_span), + "depth" => Value::int(profiling_config.depth, element_span), + "span" => Value::record(span_record, element_span), + }; if profiling_config.collect_source { - cols.push("source".to_string()); - vals.push(element_str); + record.push("source", element_str); } if profiling_config.collect_values { @@ -1529,21 +1464,12 @@ fn collect_profiling_metadata( }, }; - cols.push("value".to_string()); - vals.push(value); + record.push("value", value); } - cols.push("time".to_string()); - vals.push(Value::Duration { - val: time_ns, - span: element_span, - }); + record.push("time", Value::duration(time_ns, element_span)); - let record = Value::Record { - cols, - vals, - span: element_span, - }; + let record = Value::record(record, element_span); let element_metadata = if let Ok((pipeline_data, ..)) = &eval_result { pipeline_data.metadata() diff --git a/crates/nu-engine/src/scope.rs b/crates/nu-engine/src/scope.rs index 2ebf60ef65..787caeeacd 100644 --- a/crates/nu-engine/src/scope.rs +++ b/crates/nu-engine/src/scope.rs @@ -1,7 +1,7 @@ use nu_protocol::{ ast::Expr, engine::{Command, EngineState, Stack, Visibility}, - ModuleId, Signature, Span, SyntaxShape, Type, Value, + record, ModuleId, Record, Signature, Span, SyntaxShape, Type, Value, }; use std::cmp::Ordering; use std::collections::HashMap; @@ -65,17 +65,16 @@ impl<'e, 's> ScopeData<'e, 's> { let var_id_val = Value::int(**var_id as i64, span); - vars.push(Value::Record { - cols: vec![ - "name".to_string(), - "type".to_string(), - "value".to_string(), - "is_const".to_string(), - "var_id".to_string(), - ], - vals: vec![var_name, var_type, var_value, is_const, var_id_val], + vars.push(Value::record( + record! { + "name" => var_name, + "type" => var_type, + "value" => var_value, + "is_const" => is_const, + "var_id" => var_id_val, + }, span, - }) + )); } sort_rows(&mut vars); @@ -89,121 +88,44 @@ impl<'e, 's> ScopeData<'e, 's> { if self.visibility.is_decl_id_visible(decl_id) && !self.engine_state.get_decl(**decl_id).is_alias() { - let mut cols = vec![]; - let mut vals = vec![]; - - cols.push("name".into()); - vals.push(Value::String { - val: String::from_utf8_lossy(command_name).to_string(), - span, - }); - let decl = self.engine_state.get_decl(**decl_id); let signature = decl.signature(); - cols.push("category".to_string()); - vals.push(Value::String { - val: signature.category.to_string(), - span, - }); - - cols.push("signatures".to_string()); - vals.push(self.collect_signatures(&signature, span)); - - cols.push("usage".to_string()); - vals.push(Value::String { - val: decl.usage().into(), - span, - }); - - cols.push("examples".to_string()); - vals.push(Value::List { - vals: decl - .examples() - .into_iter() - .map(|x| Value::Record { - cols: vec!["description".into(), "example".into(), "result".into()], - vals: vec![ - Value::String { - val: x.description.to_string(), - span, - }, - Value::String { - val: x.example.to_string(), - span, - }, - if let Some(result) = x.result { - result - } else { - Value::Nothing { span } - }, - ], + let examples = decl + .examples() + .into_iter() + .map(|x| { + Value::record( + record! { + "description" => Value::string(x.description, span), + "example" => Value::string(x.example, span), + "result" => x.result.unwrap_or(Value::nothing(span)), + }, span, - }) - .collect(), - span, - }); + ) + }) + .collect(); - cols.push("is_builtin".to_string()); - // we can only be a is_builtin or is_custom, not both - vals.push(Value::Bool { - val: !decl.is_custom_command(), - span, - }); + let record = record! { + "name" => Value::string(String::from_utf8_lossy(command_name), span), + "category" => Value::string(signature.category.to_string(), span), + "signatures" => self.collect_signatures(&signature, span), + "usage" => Value::string(decl.usage(), span), + "examples" => Value::list(examples, span), + // we can only be a is_builtin or is_custom, not both + "is_builtin" => Value::bool(!decl.is_custom_command(), span), + "is_sub" => Value::bool(decl.is_sub(), span), + "is_plugin" => Value::bool(decl.is_plugin().is_some(), span), + "is_custom" => Value::bool(decl.is_custom_command(), span), + "is_keyword" => Value::bool(decl.is_parser_keyword(), span), + "is_extern" => Value::bool(decl.is_known_external(), span), + "creates_scope" => Value::bool(signature.creates_scope, span), + "extra_usage" => Value::string(decl.extra_usage(), span), + "search_terms" => Value::string(decl.search_terms().join(", "), span), + "decl_id" => Value::int(**decl_id as i64, span), + }; - cols.push("is_sub".to_string()); - vals.push(Value::Bool { - val: decl.is_sub(), - span, - }); - - cols.push("is_plugin".to_string()); - vals.push(Value::Bool { - val: decl.is_plugin().is_some(), - span, - }); - - cols.push("is_custom".to_string()); - vals.push(Value::Bool { - val: decl.is_custom_command(), - span, - }); - - cols.push("is_keyword".into()); - vals.push(Value::Bool { - val: decl.is_parser_keyword(), - span, - }); - - cols.push("is_extern".to_string()); - vals.push(Value::Bool { - val: decl.is_known_external(), - span, - }); - - cols.push("creates_scope".to_string()); - vals.push(Value::Bool { - val: signature.creates_scope, - span, - }); - - cols.push("extra_usage".to_string()); - vals.push(Value::String { - val: decl.extra_usage().into(), - span, - }); - - let search_terms = decl.search_terms(); - cols.push("search_terms".to_string()); - vals.push(Value::String { - val: search_terms.join(", "), - span, - }); - - cols.push("decl_id".into()); - vals.push(Value::int(**decl_id as i64, span)); - - commands.push(Value::Record { cols, vals, span }) + commands.push(Value::record(record, span)) } } @@ -256,8 +178,7 @@ impl<'e, 's> ScopeData<'e, 's> { // signature usually comes later in the input_output_types, so this will // remove them from the record. sigs.dedup_by(|(k1, _), (k2, _)| k1 == k2); - let (cols, vals) = sigs.into_iter().unzip(); - Value::Record { cols, vals, span } + Value::record(sigs.into_iter().collect(), span) } fn collect_signature_entries( @@ -281,20 +202,22 @@ impl<'e, 's> ScopeData<'e, 's> { ]; // input - sig_records.push(Value::Record { - cols: sig_cols.clone(), - vals: vec![ - Value::nothing(span), - Value::string("input", span), - Value::string(input_type.to_shape().to_string(), span), - Value::bool(false, span), - Value::nothing(span), - Value::nothing(span), - Value::nothing(span), - Value::nothing(span), - ], + sig_records.push(Value::record( + Record { + cols: sig_cols.clone(), + vals: vec![ + Value::nothing(span), + Value::string("input", span), + Value::string(input_type.to_shape().to_string(), span), + Value::bool(false, span), + Value::nothing(span), + Value::nothing(span), + Value::nothing(span), + Value::nothing(span), + ], + }, span, - }); + )); // required_positional for req in &signature.required_positional { @@ -312,11 +235,13 @@ impl<'e, 's> ScopeData<'e, 's> { Value::nothing(span), ]; - sig_records.push(Value::Record { - cols: sig_cols.clone(), - vals: sig_vals, + sig_records.push(Value::record( + Record { + cols: sig_cols.clone(), + vals: sig_vals, + }, span, - }); + )); } // optional_positional @@ -339,11 +264,13 @@ impl<'e, 's> ScopeData<'e, 's> { }, ]; - sig_records.push(Value::Record { - cols: sig_cols.clone(), - vals: sig_vals, + sig_records.push(Value::record( + Record { + cols: sig_cols.clone(), + vals: sig_vals, + }, span, - }); + )); } // rest_positional @@ -362,11 +289,13 @@ impl<'e, 's> ScopeData<'e, 's> { Value::nothing(span), // rest_positional does have default, but parser prohibits specifying it?! ]; - sig_records.push(Value::Record { - cols: sig_cols.clone(), - vals: sig_vals, + sig_records.push(Value::record( + Record { + cols: sig_cols.clone(), + vals: sig_vals, + }, span, - }); + )); } // named flags @@ -410,28 +339,32 @@ impl<'e, 's> ScopeData<'e, 's> { }, ]; - sig_records.push(Value::Record { - cols: sig_cols.clone(), - vals: sig_vals, + sig_records.push(Value::record( + Record { + cols: sig_cols.clone(), + vals: sig_vals, + }, span, - }); + )); } // output - sig_records.push(Value::Record { - cols: sig_cols, - vals: vec![ - Value::nothing(span), - Value::string("output", span), - Value::string(output_type.to_shape().to_string(), span), - Value::bool(false, span), - Value::nothing(span), - Value::nothing(span), - Value::nothing(span), - Value::nothing(span), - ], + sig_records.push(Value::record( + Record { + cols: sig_cols, + vals: vec![ + Value::nothing(span), + Value::string("output", span), + Value::string(output_type.to_shape().to_string(), span), + Value::bool(false, span), + Value::nothing(span), + Value::nothing(span), + Value::nothing(span), + Value::nothing(span), + ], + }, span, - }); + )); sig_records } @@ -443,25 +376,13 @@ impl<'e, 's> ScopeData<'e, 's> { let decl = self.engine_state.get_decl(**decl_id); if decl.is_known_external() { - let mut cols = vec![]; - let mut vals = vec![]; + let record = record! { + "name" => Value::string(String::from_utf8_lossy(command_name), span), + "usage" => Value::string(decl.usage(), span), + "decl_id" => Value::int(**decl_id as i64, span), + }; - cols.push("name".into()); - vals.push(Value::String { - val: String::from_utf8_lossy(command_name).to_string(), - span, - }); - - cols.push("usage".to_string()); - vals.push(Value::String { - val: decl.usage().into(), - span, - }); - - cols.push("decl_id".into()); - vals.push(Value::int(**decl_id as i64, span)); - - externals.push(Value::Record { cols, vals, span }) + externals.push(Value::record(record, span)) } } @@ -483,38 +404,20 @@ impl<'e, 's> ScopeData<'e, 's> { Value::nothing(span) }; - aliases.push(Value::Record { - cols: vec![ - "name".into(), - "expansion".into(), - "usage".into(), - "decl_id".into(), - "aliased_decl_id".into(), - ], - vals: vec![ - Value::String { - val: String::from_utf8_lossy(&decl_name).to_string(), - span, - }, - Value::String { - val: String::from_utf8_lossy( - self.engine_state.get_span_contents(alias.wrapped_call.span), - ) - .to_string(), - span, - }, - Value::String { - val: alias.usage().to_string(), - span, - }, - Value::Int { - val: decl_id as i64, - span, - }, - aliased_decl_id, - ], + let expansion = String::from_utf8_lossy( + self.engine_state.get_span_contents(alias.wrapped_call.span), + ); + + aliases.push(Value::record( + record! { + "name" => Value::string(String::from_utf8_lossy(&decl_name), span), + "expansion" => Value::string(expansion, span), + "usage" => Value::string(alias.usage(), span), + "decl_id" => Value::int(decl_id as i64, span), + "aliased_decl_id" => aliased_decl_id, + }, span, - }); + )); } } } @@ -536,11 +439,10 @@ impl<'e, 's> ScopeData<'e, 's> { if !decl.is_alias() && !decl.is_known_external() { Some(Value::record( - vec!["name".into(), "decl_id".into()], - vec![ - Value::string(String::from_utf8_lossy(name_bytes), span), - Value::int(*decl_id as i64, span), - ], + record! { + "name" => Value::string(String::from_utf8_lossy(name_bytes), span), + "decl_id" => Value::int(*decl_id as i64, span), + }, span, )) } else { @@ -556,11 +458,10 @@ impl<'e, 's> ScopeData<'e, 's> { if decl.is_alias() { Some(Value::record( - vec!["name".into(), "decl_id".into()], - vec![ - Value::string(String::from_utf8_lossy(name_bytes), span), - Value::int(*decl_id as i64, span), - ], + record! { + "name" => Value::string(String::from_utf8_lossy(name_bytes), span), + "decl_id" => Value::int(*decl_id as i64, span), + }, span, )) } else { @@ -576,11 +477,10 @@ impl<'e, 's> ScopeData<'e, 's> { if decl.is_known_external() { Some(Value::record( - vec!["name".into(), "decl_id".into()], - vec![ - Value::string(String::from_utf8_lossy(name_bytes), span), - Value::int(*decl_id as i64, span), - ], + record! { + "name" => Value::string(String::from_utf8_lossy(name_bytes), span), + "decl_id" => Value::int(*decl_id as i64, span), + }, span, )) } else { @@ -600,12 +500,11 @@ impl<'e, 's> ScopeData<'e, 's> { .iter() .map(|(name_bytes, var_id)| { Value::record( - vec!["name".into(), "type".into(), "var_id".into()], - vec![ - Value::string(String::from_utf8_lossy(name_bytes), span), - Value::string(self.engine_state.get_var(*var_id).ty.to_string(), span), - Value::int(*var_id as i64, span), - ], + record! { + "name" => Value::string(String::from_utf8_lossy(name_bytes), span), + "type" => Value::string(self.engine_state.get_var(*var_id).ty.to_string(), span), + "var_id" => Value::int(*var_id as i64, span), + }, span, ) }) @@ -631,46 +530,20 @@ impl<'e, 's> ScopeData<'e, 's> { .map(|(usage, _)| usage) .unwrap_or_default(); - Value::Record { - cols: vec![ - "name".into(), - "commands".into(), - "aliases".into(), - "externs".into(), - "submodules".into(), - "constants".into(), - "env_block".into(), - "usage".into(), - "module_id".into(), - ], - vals: vec![ - Value::string(String::from_utf8_lossy(module_name), span), - Value::List { - vals: export_commands, - span, - }, - Value::List { - vals: export_aliases, - span, - }, - Value::List { - vals: export_externs, - span, - }, - Value::List { - vals: export_submodules, - span, - }, - Value::List { - vals: export_consts, - span, - }, - export_env_block, - Value::string(module_usage, span), - Value::int(*module_id as i64, span), - ], + Value::record( + record! { + "name" => Value::string(String::from_utf8_lossy(module_name), span), + "commands" => Value::list(export_commands, span), + "aliases" => Value::list(export_aliases, span), + "externs" => Value::list(export_externs, span), + "submodules" => Value::list(export_submodules, span), + "constants" => Value::list(export_consts, span), + "env_block" => export_env_block, + "usage" => Value::string(module_usage, span), + "module_id" => Value::int(*module_id as i64, span), + }, span, - } + ) } pub fn collect_modules(&self, span: Span) -> Vec { @@ -685,36 +558,24 @@ impl<'e, 's> ScopeData<'e, 's> { } pub fn collect_engine_state(&self, span: Span) -> Value { - let engine_state_cols = vec![ - "source_bytes".to_string(), - "num_vars".to_string(), - "num_decls".to_string(), - "num_blocks".to_string(), - "num_modules".to_string(), - "num_env_vars".to_string(), - ]; + let num_env_vars = self + .engine_state + .env_vars + .values() + .map(|overlay| overlay.len() as i64) + .sum(); - let engine_state_vals = vec![ - Value::int(self.engine_state.next_span_start() as i64, span), - Value::int(self.engine_state.num_vars() as i64, span), - Value::int(self.engine_state.num_decls() as i64, span), - Value::int(self.engine_state.num_blocks() as i64, span), - Value::int(self.engine_state.num_modules() as i64, span), - Value::int( - self.engine_state - .env_vars - .values() - .map(|overlay| overlay.len() as i64) - .sum(), - span, - ), - ]; - - Value::Record { - cols: engine_state_cols, - vals: engine_state_vals, + Value::record( + record! { + "source_bytes" => Value::int(self.engine_state.next_span_start() as i64, span), + "num_vars" => Value::int(self.engine_state.num_vars() as i64, span), + "num_decls" => Value::int(self.engine_state.num_decls() as i64, span), + "num_blocks" => Value::int(self.engine_state.num_blocks() as i64, span), + "num_modules" => Value::int(self.engine_state.num_modules() as i64, span), + "num_env_vars" => Value::int(num_env_vars, span), + }, span, - } + ) } } @@ -731,10 +592,10 @@ fn extract_custom_completion_from_arg(engine_state: &EngineState, shape: &Syntax fn sort_rows(decls: &mut [Value]) { decls.sort_by(|a, b| match (a, b) { - (Value::Record { vals: rec_a, .. }, Value::Record { vals: rec_b, .. }) => { + (Value::Record { val: rec_a, .. }, Value::Record { val: rec_b, .. }) => { // Comparing the first value from the record // It is expected that the first value is the name of the entry (command, module, alias, etc.) - match (rec_a.get(0), rec_b.get(0)) { + match (rec_a.vals.get(0), rec_b.vals.get(0)) { (Some(val_a), Some(val_b)) => match (val_a, val_b) { (Value::String { val: str_a, .. }, Value::String { val: str_b, .. }) => { str_a.cmp(str_b) diff --git a/crates/nu-explore/src/commands/config.rs b/crates/nu-explore/src/commands/config.rs index ebdb71c346..0c37e9b8be 100644 --- a/crates/nu-explore/src/commands/config.rs +++ b/crates/nu-explore/src/commands/config.rs @@ -2,7 +2,7 @@ use std::io::Result; use nu_protocol::{ engine::{EngineState, Stack}, - Value, + record, Value, }; use crate::{ @@ -158,10 +158,14 @@ impl ViewCommand for ConfigCmd { fn create_default_value() -> Value { let span = NuSpan::unknown(); - let record = |i: usize| Value::Record { - cols: vec![String::from("key"), String::from("value")], - vals: vec![nu_str(format!("key-{i}")), nu_str(format!("{i}"))], - span, + let record = |i: usize| { + Value::record( + record! { + "key" => nu_str(format!("key-{i}")), + "value" => nu_str(format!("{i}")), + }, + span, + ) }; Value::List { diff --git a/crates/nu-explore/src/commands/config_show.rs b/crates/nu-explore/src/commands/config_show.rs index bd3092967c..4abc819cd3 100644 --- a/crates/nu-explore/src/commands/config_show.rs +++ b/crates/nu-explore/src/commands/config_show.rs @@ -1,6 +1,6 @@ use nu_protocol::{ engine::{EngineState, Stack}, - Value, + record, Value, }; use ratatui::layout::Rect; use std::collections::HashMap; @@ -157,8 +157,8 @@ fn convert_styles_value(value: &mut Value) { convert_styles_value(value); } } - Value::Record { vals, .. } => { - for value in vals { + Value::Record { val, .. } => { + for value in &mut val.vals { convert_styles_value(value); } } @@ -168,17 +168,13 @@ fn convert_styles_value(value: &mut Value) { fn convert_style_from_string(s: &str) -> Option { let style = nu_json::from_str::(s).ok()?; - let cols = vec![String::from("bg"), String::from("fg"), String::from("attr")]; - let vals = vec![ - Value::string(style.bg.unwrap_or_default(), NuSpan::unknown()), - Value::string(style.fg.unwrap_or_default(), NuSpan::unknown()), - Value::string(style.attr.unwrap_or_default(), NuSpan::unknown()), - ]; - - Some(Value::Record { - cols, - vals, - span: NuSpan::unknown(), - }) + Some(Value::record( + record! { + "bg" => Value::string(style.bg.unwrap_or_default(), NuSpan::unknown()), + "fg" => Value::string(style.fg.unwrap_or_default(), NuSpan::unknown()), + "attr" => Value::string(style.attr.unwrap_or_default(), NuSpan::unknown()), + }, + NuSpan::unknown(), + )) } diff --git a/crates/nu-explore/src/commands/help.rs b/crates/nu-explore/src/commands/help.rs index 3a873daef8..ae3b19ce98 100644 --- a/crates/nu-explore/src/commands/help.rs +++ b/crates/nu-explore/src/commands/help.rs @@ -4,7 +4,7 @@ use std::io::{self, Result}; use crossterm::event::KeyEvent; use nu_protocol::{ engine::{EngineState, Stack}, - Value, + record, Record, Value, }; use ratatui::layout::Rect; @@ -169,11 +169,7 @@ fn help_frame_data( let (cols, mut vals) = help_manual_data(manual, aliases); let vals = vals.remove(0); - Value::Record { - cols, - vals, - span: NuSpan::unknown(), - } + Value::record(Record { cols, vals }, NuSpan::unknown()) }) .collect(); let commands = Value::List { @@ -192,10 +188,14 @@ fn help_manual_data(manual: &HelpManual, aliases: &[String]) -> (Vec, Ve let arguments = manual .arguments .iter() - .map(|e| Value::Record { - cols: vec![String::from("example"), String::from("description")], - vals: vec![nu_str(&e.example), nu_str(&e.description)], - span: NuSpan::unknown(), + .map(|e| { + Value::record( + record! { + "example" => nu_str(&e.example), + "description" => nu_str(&e.description), + }, + NuSpan::unknown(), + ) }) .collect(); @@ -207,10 +207,14 @@ fn help_manual_data(manual: &HelpManual, aliases: &[String]) -> (Vec, Ve let examples = manual .examples .iter() - .map(|e| Value::Record { - cols: vec![String::from("example"), String::from("description")], - vals: vec![nu_str(&e.example), nu_str(&e.description)], - span: NuSpan::unknown(), + .map(|e| { + Value::record( + record! { + "example" => nu_str(&e.example), + "description" => nu_str(&e.description), + }, + NuSpan::unknown(), + ) }) .collect(); let examples = Value::List { @@ -221,14 +225,15 @@ fn help_manual_data(manual: &HelpManual, aliases: &[String]) -> (Vec, Ve let inputs = manual .input .iter() - .map(|e| Value::Record { - cols: vec![ - String::from("name"), - String::from("context"), - String::from("description"), - ], - vals: vec![nu_str(&e.code), nu_str(&e.context), nu_str(&e.description)], - span: NuSpan::unknown(), + .map(|e| { + Value::record( + record! { + "name" => nu_str(&e.code), + "context" => nu_str(&e.context), + "description" => nu_str(&e.description), + }, + NuSpan::unknown(), + ) }) .collect(); let inputs = Value::List { @@ -243,10 +248,14 @@ fn help_manual_data(manual: &HelpManual, aliases: &[String]) -> (Vec, Ve let values = o .values .iter() - .map(|v| Value::Record { - cols: vec![String::from("example"), String::from("description")], - vals: vec![nu_str(&v.example), nu_str(&v.description)], - span: NuSpan::unknown(), + .map(|v| { + Value::record( + record! { + "example" => nu_str(&v.example), + "description" => nu_str(&v.description), + }, + NuSpan::unknown(), + ) }) .collect(); let values = Value::List { @@ -254,21 +263,15 @@ fn help_manual_data(manual: &HelpManual, aliases: &[String]) -> (Vec, Ve span: NuSpan::unknown(), }; - Value::Record { - cols: vec![ - String::from("name"), - String::from("context"), - String::from("description"), - String::from("values"), - ], - vals: vec![ - nu_str(&o.group), - nu_str(&o.key), - nu_str(&o.description), - values, - ], - span: NuSpan::unknown(), - } + Value::record( + record! { + "name" => nu_str(&o.group), + "context" => nu_str(&o.key), + "description" => nu_str(&o.description), + "values" => values, + }, + NuSpan::unknown(), + ) }) .collect(); let configuration = Value::List { diff --git a/crates/nu-explore/src/explore.rs b/crates/nu-explore/src/explore.rs index 39b2198f08..398da3bde0 100644 --- a/crates/nu-explore/src/explore.rs +++ b/crates/nu-explore/src/explore.rs @@ -296,10 +296,8 @@ fn prepare_default_config(config: &mut HashMap) { } fn parse_hash_map(value: &Value) -> Option> { - value.as_record().ok().map(|(cols, vals)| { - cols.iter() - .take(vals.len()) - .zip(vals) + value.as_record().ok().map(|val| { + val.iter() .map(|(col, val)| (col.clone(), val.clone())) .collect::>() }) diff --git a/crates/nu-explore/src/nu_common/table.rs b/crates/nu-explore/src/nu_common/table.rs index 1d36a74f0f..9a90e9058b 100644 --- a/crates/nu-explore/src/nu_common/table.rs +++ b/crates/nu-explore/src/nu_common/table.rs @@ -1,5 +1,5 @@ use nu_color_config::StyleComputer; -use nu_protocol::{Span, Value}; +use nu_protocol::{Record, Span, Value}; use nu_table::{ common::{nu_value_to_string, nu_value_to_string_clean}, ExpandedTable, TableOpts, @@ -17,9 +17,7 @@ pub fn try_build_table( ) -> String { match value { Value::List { vals, span } => try_build_list(vals, ctrlc, config, span, style_computer), - Value::Record { cols, vals, span } => { - try_build_map(cols, vals, span, style_computer, ctrlc, config) - } + Value::Record { val, span } => try_build_map(val, span, style_computer, ctrlc, config), val if matches!(val, Value::String { .. }) => { nu_value_to_string_clean(&val, config, style_computer).0 } @@ -28,8 +26,7 @@ pub fn try_build_table( } fn try_build_map( - cols: Vec, - vals: Vec, + record: Record, span: Span, style_computer: &StyleComputer, ctrlc: Option>, @@ -44,11 +41,11 @@ fn try_build_map( usize::MAX, (config.table_indent.left, config.table_indent.right), ); - let result = ExpandedTable::new(None, false, String::new()).build_map(&cols, &vals, opts); + let result = ExpandedTable::new(None, false, String::new()).build_map(&record, opts); match result { Ok(Some(result)) => result, Ok(None) | Err(_) => { - nu_value_to_string(&Value::Record { cols, vals, span }, config, style_computer).0 + nu_value_to_string(&Value::record(record, span), config, style_computer).0 } } } diff --git a/crates/nu-explore/src/nu_common/value.rs b/crates/nu-explore/src/nu_common/value.rs index c05931d194..1c880f3d89 100644 --- a/crates/nu-explore/src/nu_common/value.rs +++ b/crates/nu-explore/src/nu_common/value.rs @@ -1,7 +1,9 @@ use std::collections::HashMap; use nu_engine::get_columns; -use nu_protocol::{ast::PathMember, ListStream, PipelineData, PipelineMetadata, RawStream, Value}; +use nu_protocol::{ + ast::PathMember, record, ListStream, PipelineData, PipelineMetadata, RawStream, Value, +}; use super::NuSpan; @@ -80,14 +82,7 @@ fn collect_external_stream( data.push(val); } if metadata.is_some() { - let val = Value::Record { - cols: vec![String::from("data_source")], - vals: vec![Value::String { - val: String::from("ls"), - span, - }], - span, - }; + let val = Value::record(record! { "data_source" => Value::string("ls", span) }, span); columns.push(String::from("metadata")); data.push(val); @@ -98,7 +93,7 @@ fn collect_external_stream( /// Try to build column names and a table grid. pub fn collect_input(value: Value) -> (Vec, Vec>) { match value { - Value::Record { cols, vals, .. } => (cols, vec![vals]), + Value::Record { val: record, .. } => (record.cols, vec![record.vals]), Value::List { vals, .. } => { let mut columns = get_columns(&vals); let data = convert_records_to_dataset(&columns, vals); @@ -191,30 +186,18 @@ fn record_lookup_value(item: &Value, header: &str) -> Value { } pub fn create_map(value: &Value) -> Option> { - let (cols, inner_vals) = value.as_record().ok()?; - - let mut hm: HashMap = HashMap::new(); - for (k, v) in cols.iter().zip(inner_vals) { - hm.insert(k.to_string(), v.clone()); - } - - Some(hm) + Some( + value + .as_record() + .ok()? + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(), + ) } pub fn map_into_value(hm: HashMap) -> Value { - let mut columns = Vec::with_capacity(hm.len()); - let mut values = Vec::with_capacity(hm.len()); - - for (key, value) in hm { - columns.push(key); - values.push(value); - } - - Value::Record { - cols: columns, - vals: values, - span: NuSpan::unknown(), - } + Value::record(hm.into_iter().collect(), NuSpan::unknown()) } pub fn nu_str>(s: S) -> Value { diff --git a/crates/nu-explore/src/pager/mod.rs b/crates/nu-explore/src/pager/mod.rs index 6a3a864f0f..41d4f3510e 100644 --- a/crates/nu-explore/src/pager/mod.rs +++ b/crates/nu-explore/src/pager/mod.rs @@ -23,7 +23,7 @@ use lscolors::LsColors; use nu_color_config::{lookup_ansi_color_style, StyleComputer}; use nu_protocol::{ engine::{EngineState, Stack}, - Value, + Record, Value, }; use ratatui::{backend::CrosstermBackend, layout::Rect, widgets::Block}; @@ -990,11 +990,7 @@ fn set_config(hm: &mut HashMap, path: &[&str], value: Value) -> b if !hm.contains_key(key) { hm.insert( key.to_string(), - Value::Record { - cols: vec![], - vals: vec![], - span: NuSpan::unknown(), - }, + Value::record(Record::new(), NuSpan::unknown()), ); } @@ -1006,27 +1002,26 @@ fn set_config(hm: &mut HashMap, path: &[&str], value: Value) -> b } match val { - Value::Record { cols, vals, .. } => { + Value::Record { val: record, .. } => { if path.len() == 2 { - if cols.len() != vals.len() { + if record.cols.len() != record.vals.len() { return false; } let key = &path[1]; - let pos = cols.iter().position(|v| v == key); + let pos = record.cols.iter().position(|v| v == key); match pos { Some(i) => { - vals[i] = value; + record.vals[i] = value; } None => { - cols.push(key.to_string()); - vals.push(value); + record.push(*key, value); } } } else { let mut hm2: HashMap = HashMap::new(); - for (k, v) in cols.iter().zip(vals) { + for (k, v) in record.iter() { hm2.insert(k.to_string(), v.clone()); } diff --git a/crates/nu-explore/src/views/record/mod.rs b/crates/nu-explore/src/views/record/mod.rs index 8b64573880..20e5b47bb1 100644 --- a/crates/nu-explore/src/views/record/mod.rs +++ b/crates/nu-explore/src/views/record/mod.rs @@ -8,7 +8,7 @@ use crossterm::event::{KeyCode, KeyEvent}; use nu_color_config::{get_color_map, StyleComputer}; use nu_protocol::{ engine::{EngineState, Stack}, - Value, + Record, Value, }; use ratatui::{layout::Rect, widgets::Block}; @@ -683,10 +683,14 @@ fn build_table_as_list(v: &RecordView) -> Value { .records .iter() .cloned() - .map(|vals| Value::Record { - cols: headers.clone(), - vals, - span: NuSpan::unknown(), + .map(|vals| { + Value::record( + Record { + cols: headers.clone(), + vals, + }, + NuSpan::unknown(), + ) }) .collect(); @@ -702,11 +706,7 @@ fn build_table_as_record(v: &RecordView) -> Value { let cols = layer.columns.to_vec(); let vals = layer.records.get(0).map_or(Vec::new(), |row| row.clone()); - Value::Record { - cols, - vals, - span: NuSpan::unknown(), - } + Value::record(Record { cols, vals }, NuSpan::unknown()) } fn report_cursor_position(mode: UIMode, cursor: XYCursor) -> String { diff --git a/crates/nu-parser/src/eval.rs b/crates/nu-parser/src/eval.rs index 8903f39b4c..ae5495ecee 100644 --- a/crates/nu-parser/src/eval.rs +++ b/crates/nu-parser/src/eval.rs @@ -1,7 +1,7 @@ use nu_protocol::{ ast::{Expr, Expression}, engine::StateWorkingSet, - ParseError, Span, Value, + ParseError, Record, Span, Value, }; /// Evaluate a constant value at parse time @@ -59,28 +59,22 @@ pub fn eval_constant( }) } Expr::Record(fields) => { - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); for (col, val) in fields { // avoid duplicate cols. let col_name = value_as_string(eval_constant(working_set, col)?, expr.span)?; - let pos = cols.iter().position(|c| c == &col_name); + let pos = record.cols.iter().position(|c| c == &col_name); match pos { Some(index) => { - vals[index] = eval_constant(working_set, val)?; + record.vals[index] = eval_constant(working_set, val)?; } None => { - cols.push(col_name); - vals.push(eval_constant(working_set, val)?); + record.push(col_name, eval_constant(working_set, val)?); } } } - Ok(Value::Record { - cols, - vals, - span: expr.span, - }) + Ok(Value::record(record, expr.span)) } Expr::Table(headers, vals) => { let mut output_headers = vec![]; @@ -97,11 +91,13 @@ pub fn eval_constant( for expr in val { row.push(eval_constant(working_set, expr)?); } - output_rows.push(Value::Record { - cols: output_headers.clone(), - vals: row, - span: expr.span, - }); + output_rows.push(Value::record( + Record { + cols: output_headers.clone(), + vals: row, + }, + expr.span, + )); } Ok(Value::List { vals: output_rows, diff --git a/crates/nu-protocol/src/config.rs b/crates/nu-protocol/src/config.rs index 925fbf2076..df1618307c 100644 --- a/crates/nu-protocol/src/config.rs +++ b/crates/nu-protocol/src/config.rs @@ -1,4 +1,4 @@ -use crate::{ShellError, Span, Value}; +use crate::{record, Record, ShellError, Span, Value}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -323,7 +323,8 @@ impl Value { // $env.config.ls.use_ls_colors = 2 results in an error, so // the current use_ls_colors config setting is converted to a Value::Boolean and inserted in the // record in place of the 2. - if let Value::Record { cols, vals, span } = self { + if let Value::Record { val, span } = self { + let Record { cols, vals } = val; let span = *span; // Because this whole algorithm removes while iterating, this must iterate in reverse. for index in (0..cols.len()).rev() { @@ -332,7 +333,8 @@ impl Value { match key { // Grouped options "ls" => { - if let Value::Record { cols, vals, span } = &mut vals[index] { + if let Value::Record { val, span } = &mut vals[index] { + let Record { cols, vals } = val; let span = *span; for index in (0..cols.len()).rev() { let value = &vals[index]; @@ -363,17 +365,17 @@ impl Value { invalid!(vals[index].span().ok(), "should be a record"); // Reconstruct vals[index] = Value::record( - vec!["use_ls_colors".into(), "clickable_links".into()], - vec![ - Value::bool(config.use_ls_colors, span), - Value::bool(config.show_clickable_links_in_ls, span), - ], + record! { + "use_ls_colors" => Value::bool(config.use_ls_colors, span), + "clickable_links" => Value::bool(config.show_clickable_links_in_ls, span), + }, span, ); } } "cd" => { - if let Value::Record { cols, vals, span } = &mut vals[index] { + if let Value::Record { val, span } = &mut vals[index] { + let Record { cols, vals } = val; for index in (0..cols.len()).rev() { let value = &vals[index]; let key2 = cols[index].as_str(); @@ -396,17 +398,17 @@ impl Value { invalid!(vals[index].span().ok(), "should be a record"); // Reconstruct vals[index] = Value::record( - vec!["use_ls_colors".into(), "clickable_links".into()], - vec![ - Value::bool(config.use_ls_colors, span), - Value::bool(config.show_clickable_links_in_ls, span), - ], + record! { + "use_ls_colors" => Value::bool(config.use_ls_colors, span), + "clickable_links" => Value::bool(config.show_clickable_links_in_ls, span), + }, span, ); } } "rm" => { - if let Value::Record { cols, vals, span } = &mut vals[index] { + if let Value::Record { val, span } = &mut vals[index] { + let Record { cols, vals } = val; for index in (0..cols.len()).rev() { let value = &vals[index]; let key2 = cols[index].as_str(); @@ -429,8 +431,9 @@ impl Value { invalid!(vals[index].span().ok(), "should be a record"); // Reconstruct vals[index] = Value::record( - vec!["always_trash".into()], - vec![Value::bool(config.rm_always_trash, span)], + record! { + "always_trash" => Value::bool(config.rm_always_trash, span), + }, span, ); } @@ -447,7 +450,8 @@ impl Value { ) }; } - if let Value::Record { cols, vals, span } = &mut vals[index] { + if let Value::Record { val, span } = &mut vals[index] { + let Record { cols, vals } = val; let span = *span; for index in (0..cols.len()).rev() { let value = &vals[index]; @@ -504,18 +508,12 @@ impl Value { invalid!(vals[index].span().ok(), "should be a record"); // Reconstruct vals[index] = Value::record( - vec![ - "sync_on_enter".into(), - "max_size".into(), - "file_format".into(), - "isolation".into(), - ], - vec![ - Value::bool(config.sync_history_on_enter, span), - Value::int(config.max_history_size, span), - reconstruct_history_file_format!(span), - Value::bool(config.history_isolation, span), - ], + record! { + "sync_on_enter" => Value::bool(config.sync_history_on_enter, span), + "max_size" => Value::int(config.max_history_size, span), + "file_format" => reconstruct_history_file_format!(span), + "isolation" => Value::bool(config.history_isolation, span), + }, span, ); } @@ -536,17 +534,17 @@ impl Value { macro_rules! reconstruct_external { ($span: expr) => { Value::record( - vec!["max_results".into(), "completer".into(), "enable".into()], - vec![ - Value::int(config.max_external_completion_results, $span), - reconstruct_external_completer!($span), - Value::bool(config.enable_external_completion, $span), - ], + record! { + "max_results" => Value::int(config.max_external_completion_results, $span), + "completer" => reconstruct_external_completer!($span), + "enable" => Value::bool(config.enable_external_completion, $span), + }, $span, ) }; } - if let Value::Record { cols, vals, span } = &mut vals[index] { + if let Value::Record { val, span } = &mut vals[index] { + let Record { cols, vals } = val; let span = *span; for index in (0..cols.len()).rev() { let value = &vals[index]; @@ -596,8 +594,8 @@ impl Value { ) } "external" => { - if let Value::Record { cols, vals, span } = &mut vals[index] - { + if let Value::Record { val, span } = &mut vals[index] { + let Record { cols, vals } = val; let span = *span; for index in (0..cols.len()).rev() { let value = &vals[index]; @@ -672,20 +670,13 @@ impl Value { invalid!(vals[index].span().ok(), "should be a record"); // Reconstruct record vals[index] = Value::record( - vec![ - "quick".into(), - "partial".into(), - "algorithm".into(), - "case_sensitive".into(), - "external".into(), - ], - vec![ - Value::bool(config.quick_completions, span), - Value::bool(config.partial_completions, span), - Value::string(config.completion_algorithm.clone(), span), - Value::bool(config.case_sensitive_completions, span), - reconstruct_external!(span), - ], + record! { + "quick" => Value::bool(config.quick_completions, span), + "partial" => Value::bool(config.partial_completions, span), + "algorithm" => Value::string(config.completion_algorithm.clone(), span), + "case_sensitive" => Value::bool(config.case_sensitive_completions, span), + "external" => reconstruct_external!(span), + }, span, ); } @@ -706,7 +697,8 @@ impl Value { ) }; } - if let Value::Record { cols, vals, span } = &mut vals[index] { + if let Value::Record { val, span } = &mut vals[index] { + let Record { cols, vals } = val; let span = *span; for index in (0..cols.len()).rev() { let value = &vals[index]; @@ -870,12 +862,11 @@ impl Value { invalid!(vals[index].span().ok(), "should be a record"); // Reconstruct vals[index] = Value::record( - vec!["vi_insert".into(), "vi_normal".into(), "emacs".into()], - vec![ - reconstruct_cursor_shape!(config.cursor_shape_vi_insert, span), - reconstruct_cursor_shape!(config.cursor_shape_vi_normal, span), - reconstruct_cursor_shape!(config.cursor_shape_emacs, span), - ], + record! { + "vi_insert" => reconstruct_cursor_shape!(config.cursor_shape_vi_insert, span), + "vi_normal" => reconstruct_cursor_shape!(config.cursor_shape_vi_normal, span), + "emacs" => reconstruct_cursor_shape!(config.cursor_shape_emacs, span), + }, span, ); } @@ -897,34 +888,30 @@ impl Value { ($span:expr) => { match &config.trim_strategy { TrimStrategy::Wrap { try_to_keep_words } => Value::record( - vec![ - "methodology".into(), - "wrapping_try_keep_words".into(), - ], - vec![ - Value::string("wrapping", $span), - Value::bool(*try_to_keep_words, $span), - ], + record! { + "methodology" => Value::string("wrapping", $span), + "wrapping_try_keep_words" => Value::bool(*try_to_keep_words, $span), + }, $span, ), TrimStrategy::Truncate { suffix } => Value::record( - vec!["methodology".into(), "truncating_suffix".into()], match suffix { - Some(s) => vec![ - Value::string("truncating", $span), - Value::string(s.clone(), $span), - ], - None => vec![ - Value::string("truncating", $span), - Value::Nothing { span: $span }, - ], + Some(s) => record! { + "methodology" => Value::string("truncating", $span), + "truncating_suffix" => Value::string(s.clone(), $span), + }, + None => record! { + "methodology" => Value::string("truncating", $span), + "truncating_suffix" => Value::Nothing { span: $span }, + }, }, $span, ), } }; } - if let Value::Record { cols, vals, span } = &mut vals[index] { + if let Value::Record { val, span } = &mut vals[index] { + let Record { cols, vals } = val; let span = *span; for index in (0..cols.len()).rev() { let value = &vals[index]; @@ -951,7 +938,8 @@ impl Value { config.table_indent.left = *val as usize; config.table_indent.right = *val as usize; } - Value::Record { vals, cols, .. } => { + Value::Record { val, .. } => { + let Record { cols, vals } = val; let left = cols.iter().position(|e| e == "left"); let right = cols.iter().position(|e| e == "right"); @@ -1044,24 +1032,19 @@ impl Value { invalid!(vals[index].span().ok(), "should be a record"); // Reconstruct vals[index] = Value::record( - vec![ - "mode".into(), - "index_mode".into(), - "trim".into(), - "show_empty".into(), - ], - vec![ - Value::string(config.table_mode.clone(), span), - reconstruct_index_mode!(span), - reconstruct_trim_strategy!(span), - Value::bool(config.table_show_empty, span), - ], + record! { + "mode" => Value::string(config.table_mode.clone(), span), + "index_mode" => reconstruct_index_mode!(span), + "trim" => reconstruct_trim_strategy!(span), + "show_empty" => Value::bool(config.table_show_empty, span), + }, span, ) } } "filesize" => { - if let Value::Record { cols, vals, span } = &mut vals[index] { + if let Value::Record { val, span } = &mut vals[index] { + let Record { cols, vals } = val; let span = *span; for index in (0..cols.len()).rev() { let value = &vals[index]; @@ -1095,11 +1078,10 @@ impl Value { invalid!(vals[index].span().ok(), "should be a record"); // Reconstruct vals[index] = Value::record( - vec!["metric".into(), "format".into()], - vec![ - Value::bool(config.filesize_metric, span), - Value::string(config.filesize_format.clone(), span), - ], + record! { + "metric" => Value::bool(config.filesize_metric, span), + "format" => Value::string(config.filesize_format.clone(), span), + }, span, ); } @@ -1110,7 +1092,14 @@ impl Value { } else { invalid!(vals[index].span().ok(), "should be a record"); // Reconstruct - vals[index] = Value::record_from_hashmap(&config.explore, span); + vals[index] = Value::record( + config + .explore + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(), + span, + ); } } // Misc. options @@ -1120,7 +1109,14 @@ impl Value { } else { invalid!(vals[index].span().ok(), "should be a record"); // Reconstruct - vals[index] = Value::record_from_hashmap(&config.color_config, span); + vals[index] = Value::record( + config + .color_config + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(), + span, + ); } } "use_grid_icons" => { @@ -1206,25 +1202,17 @@ impl Value { menu_type, // WARNING: this is not the same name as what is used in Config.nu! ("type") source, }| { - Value::Record { - cols: vec![ - "name".into(), - "only_buffer_difference".into(), - "marker".into(), - "style".into(), - "type".into(), - "source".into(), - ], - vals: vec![ - name.clone(), - only_buffer_difference.clone(), - marker.clone(), - style.clone(), - menu_type.clone(), - source.clone(), - ], + Value::record( + record! { + "name" => name.clone(), + "only_buffer_difference" => only_buffer_difference.clone(), + "marker" => marker.clone(), + "style" => style.clone(), + "type" => menu_type.clone(), + "source" => source.clone(), + }, span, - } + ) }, ) .collect(), @@ -1250,21 +1238,15 @@ impl Value { mode, event, }| { - Value::Record { - cols: vec![ - "modifier".into(), - "keycode".into(), - "mode".into(), - "event".into(), - ], - vals: vec![ - modifier.clone(), - keycode.clone(), - mode.clone(), - event.clone(), - ], + Value::record( + record! { + "modifier" => modifier.clone(), + "keycode" => keycode.clone(), + "mode" => mode.clone(), + "event" => event.clone(), + }, span, - } + ) }, ) .collect(), @@ -1279,33 +1261,26 @@ impl Value { invalid!(Some(span), "should be a valid hooks list"); errors.push(e); // Reconstruct - let mut hook_cols = vec![]; - let mut hook_vals = vec![]; + let mut hook = Record::new(); if let Some(ref value) = config.hooks.pre_prompt { - hook_cols.push("pre_prompt".into()); - hook_vals.push(value.clone()); + hook.push("pre_prompt", value.clone()); } if let Some(ref value) = config.hooks.pre_execution { - hook_cols.push("pre_execution".into()); - hook_vals.push(value.clone()); + hook.push("pre_execution", value.clone()); } if let Some(ref value) = config.hooks.env_change { - hook_cols.push("env_change".into()); - hook_vals.push(value.clone()); + hook.push("env_change", value.clone()); } if let Some(ref value) = config.hooks.display_output { - hook_cols.push("display_output".into()); - hook_vals.push(value.clone()); + hook.push("display_output", value.clone()); } - vals.push(Value::Record { - cols: hook_cols, - vals: hook_vals, - span, - }); + vals.push(Value::record(hook, span)); } }, "datetime_format" => { - if let Value::Record { cols, vals, span } = &mut vals[index] { + if let Value::Record { val, span } = &mut vals[index] { + let Record { cols, vals } = val; + let span = *span; for index in (0..cols.len()).rev() { let value = &vals[index]; let key2 = cols[index].as_str(); @@ -1314,14 +1289,14 @@ impl Value { if let Ok(v) = value.as_string() { config.datetime_normal_format = Some(v); } else { - invalid!(Some(*span), "should be a string"); + invalid!(Some(span), "should be a string"); } } "table" => { if let Ok(v) = value.as_string() { config.datetime_table_format = Some(v); } else { - invalid!(Some(*span), "should be a string"); + invalid!(Some(span), "should be a string"); } } x => { @@ -1339,11 +1314,10 @@ impl Value { invalid!(vals[index].span().ok(), "should be a record"); // Reconstruct vals[index] = Value::record( - vec!["metric".into(), "format".into()], - vec![ - Value::bool(config.filesize_metric, span), - Value::string(config.filesize_format.clone(), span), - ], + record! { + "metric" => Value::bool(config.filesize_metric, span), + "format" => Value::string(config.filesize_format.clone(), span), + }, span, ); } @@ -1481,29 +1455,26 @@ fn try_parse_trim_methodology(value: &Value) -> Option { } fn create_map(value: &Value) -> Result, ShellError> { - let (cols, inner_vals) = value.as_record()?; - let mut hm: HashMap = HashMap::new(); - - for (k, v) in cols.iter().zip(inner_vals) { - hm.insert(k.to_string(), v.clone()); - } - - Ok(hm) + Ok(value + .as_record()? + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect()) } // Parse the hooks to find the blocks to run when the hooks fire fn create_hooks(value: &Value) -> Result { match value { - Value::Record { cols, vals, span } => { + Value::Record { val, span } => { let mut hooks = Hooks::new(); - for idx in 0..cols.len() { - match cols[idx].as_str() { - "pre_prompt" => hooks.pre_prompt = Some(vals[idx].clone()), - "pre_execution" => hooks.pre_execution = Some(vals[idx].clone()), - "env_change" => hooks.env_change = Some(vals[idx].clone()), - "display_output" => hooks.display_output = Some(vals[idx].clone()), - "command_not_found" => hooks.command_not_found = Some(vals[idx].clone()), + for (col, val) in val { + match col.as_str() { + "pre_prompt" => hooks.pre_prompt = Some(val.clone()), + "pre_execution" => hooks.pre_execution = Some(val.clone()), + "env_change" => hooks.env_change = Some(val.clone()), + "display_output" => hooks.display_output = Some(val.clone()), + "command_not_found" => hooks.command_not_found = Some(val.clone()), x => { return Err(ShellError::UnsupportedConfigValue( "'pre_prompt', 'pre_execution', 'env_change', 'display_output', 'command_not_found'" @@ -1528,13 +1499,13 @@ fn create_hooks(value: &Value) -> Result { // Parses the config object to extract the strings that will compose a keybinding for reedline fn create_keybindings(value: &Value) -> Result, ShellError> { match value { - Value::Record { cols, vals, span } => { + Value::Record { val, span } => { let span = *span; // Finding the modifier value in the record - let modifier = extract_value("modifier", cols, vals, span)?.clone(); - let keycode = extract_value("keycode", cols, vals, span)?.clone(); - let mode = extract_value("mode", cols, vals, span)?.clone(); - let event = extract_value("event", cols, vals, span)?.clone(); + let modifier = extract_value("modifier", val, span)?.clone(); + let keycode = extract_value("keycode", val, span)?.clone(); + let mode = extract_value("mode", val, span)?.clone(); + let event = extract_value("event", val, span)?.clone(); let keybinding = ParsedKeybinding { modifier, @@ -1566,18 +1537,18 @@ fn create_keybindings(value: &Value) -> Result, ShellError // Parses the config object to extract the strings that will compose a keybinding for reedline pub fn create_menus(value: &Value) -> Result, ShellError> { match value { - Value::Record { cols, vals, span } => { + Value::Record { val, span } => { let span = *span; // Finding the modifier value in the record - let name = extract_value("name", cols, vals, span)?.clone(); - let marker = extract_value("marker", cols, vals, span)?.clone(); + let name = extract_value("name", val, span)?.clone(); + let marker = extract_value("marker", val, span)?.clone(); let only_buffer_difference = - extract_value("only_buffer_difference", cols, vals, span)?.clone(); - let style = extract_value("style", cols, vals, span)?.clone(); - let menu_type = extract_value("type", cols, vals, span)?.clone(); + extract_value("only_buffer_difference", val, span)?.clone(); + let style = extract_value("style", val, span)?.clone(); + let menu_type = extract_value("type", val, span)?.clone(); // Source is an optional value - let source = match extract_value("source", cols, vals, span) { + let source = match extract_value("source", val, span) { Ok(source) => source.clone(), Err(_) => Value::Nothing { span }, }; @@ -1609,12 +1580,11 @@ pub fn create_menus(value: &Value) -> Result, ShellError> { pub fn extract_value<'record>( name: &str, - cols: &'record [String], - vals: &'record [Value], + record: &'record Record, span: Span, ) -> Result<&'record Value, ShellError> { - cols.iter() - .position(|col| col.as_str() == name) - .and_then(|index| vals.get(index)) + record + .iter() + .find_map(|(col, val)| if col == name { Some(val) } else { None }) .ok_or_else(|| ShellError::MissingConfigValue(name.to_string(), span)) } diff --git a/crates/nu-protocol/src/engine/pattern_match.rs b/crates/nu-protocol/src/engine/pattern_match.rs index d302e3bff5..aef44d18c4 100644 --- a/crates/nu-protocol/src/engine/pattern_match.rs +++ b/crates/nu-protocol/src/engine/pattern_match.rs @@ -21,12 +21,12 @@ impl Matcher for Pattern { Pattern::IgnoreRest => false, // `..` and `..$foo` only match in specific contexts Pattern::Rest(_) => false, // so we return false here and handle them elsewhere Pattern::Record(field_patterns) => match value { - Value::Record { cols, vals, .. } => { + Value::Record { val, .. } => { 'top: for field_pattern in field_patterns { - for (col_idx, col) in cols.iter().enumerate() { + for (col, val) in val { if col == &field_pattern.0 { // We have found the field - let result = field_pattern.1.match_value(&vals[col_idx], matches); + let result = field_pattern.1.match_value(val, matches); if !result { return false; } else { diff --git a/crates/nu-protocol/src/module.rs b/crates/nu-protocol/src/module.rs index fab723f173..f45dd55d9a 100644 --- a/crates/nu-protocol/src/module.rs +++ b/crates/nu-protocol/src/module.rs @@ -136,16 +136,14 @@ impl Module { } } - let mut const_cols = vec![]; - let mut const_vals = vec![]; - - for (name, val) in const_rows { - const_cols.push(String::from_utf8_lossy(&name).to_string()); - const_vals.push(val); - } - let span = self.span.unwrap_or(backup_span); - let const_record = Value::record(const_cols, const_vals, span); + let const_record = Value::record( + const_rows + .into_iter() + .map(|(name, val)| (String::from_utf8_lossy(&name).to_string(), val)) + .collect(), + span, + ); return ( ResolvedImportPattern::new( diff --git a/crates/nu-protocol/src/value/from_value.rs b/crates/nu-protocol/src/value/from_value.rs index c69e188e66..48a08d1195 100644 --- a/crates/nu-protocol/src/value/from_value.rs +++ b/crates/nu-protocol/src/value/from_value.rs @@ -4,8 +4,7 @@ use std::str::FromStr; use crate::ast::{CellPath, MatchPattern, PathMember}; use crate::engine::{Block, Closure}; -use crate::ShellError; -use crate::{Range, Spanned, Value}; +use crate::{Range, Record, ShellError, Spanned, Value}; use chrono::{DateTime, FixedOffset}; pub trait FromValue: Sized { @@ -490,11 +489,10 @@ impl FromValue for Vec { } } -// A record -impl FromValue for (Vec, Vec) { +impl FromValue for Record { fn from_value(v: &Value) -> Result { match v { - Value::Record { cols, vals, .. } => Ok((cols.clone(), vals.clone())), + Value::Record { val, .. } => Ok(val.clone()), v => Err(ShellError::CantConvert { to_type: "Record".into(), from_type: v.get_type().to_string(), diff --git a/crates/nu-protocol/src/value/lazy_record.rs b/crates/nu-protocol/src/value/lazy_record.rs index a956b82e79..f4f0f93b0a 100644 --- a/crates/nu-protocol/src/value/lazy_record.rs +++ b/crates/nu-protocol/src/value/lazy_record.rs @@ -1,4 +1,4 @@ -use crate::{ShellError, Span, Value}; +use crate::{Record, ShellError, Span, Value}; use std::fmt; // Trait definition for a lazy record (where columns are evaluated on-demand) @@ -15,20 +15,14 @@ pub trait LazyRecord<'a>: fmt::Debug + Send + Sync { // Convert the lazy record into a regular Value::Record by collecting all its columns fn collect(&'a self) -> Result { - let mut cols = vec![]; - let mut vals = vec![]; - - for column in self.column_names() { - cols.push(column.into()); - let val = self.get_column_value(column)?; - vals.push(val); - } - - Ok(Value::Record { - cols, - vals, - span: self.span(), - }) + self.column_names() + .into_iter() + .map(|col| { + let val = self.get_column_value(col)?; + Ok((col.to_owned(), val)) + }) + .collect::>() + .map(|record| Value::record(record, self.span())) } fn clone_value(&self, span: Span) -> Value; diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index c6c6bb9774..3db34049c2 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -3,6 +3,7 @@ mod from; mod from_value; mod lazy_record; mod range; +mod record; mod stream; mod unit; @@ -18,19 +19,18 @@ use chrono_humanize::HumanTime; pub use custom_value::CustomValue; use fancy_regex::Regex; pub use from_value::FromValue; -use indexmap::map::IndexMap; pub use lazy_record::LazyRecord; use nu_utils::get_system_locale; use nu_utils::locale::get_system_locale_string; use num_format::ToFormattedString; pub use range::*; +pub use record::Record; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fmt::Write; use std::{ borrow::Cow, fmt::{Display, Formatter, Result as FmtResult}, - iter, path::PathBuf, {cmp::Ordering, fmt::Debug}, }; @@ -75,8 +75,7 @@ pub enum Value { span: Span, }, Record { - cols: Vec, - vals: Vec, + val: Record, span: Span, }, List { @@ -148,9 +147,8 @@ impl Clone for Value { val: val.clone(), span: *span, }, - Value::Record { cols, vals, span } => Value::Record { - cols: cols.clone(), - vals: vals.clone(), + Value::Record { val, span } => Value::Record { + val: val.clone(), span: *span, }, Value::LazyRecord { val, span } => val.clone_value(*span), @@ -367,9 +365,9 @@ impl Value { } } - pub fn as_record(&self) -> Result<(&[String], &[Value]), ShellError> { + pub fn as_record(&self) -> Result<&Record, ShellError> { match self { - Value::Record { cols, vals, .. } => Ok((cols, vals)), + Value::Record { val, .. } => Ok(val), x => Err(ShellError::CantConvert { to_type: "record".into(), from_type: x.get_type().to_string(), @@ -547,12 +545,9 @@ impl Value { Value::Date { .. } => Type::Date, Value::Range { .. } => Type::Range, Value::String { .. } => Type::String, - Value::Record { cols, vals, .. } => Type::Record( - cols.iter() - .zip(vals.iter()) - .map(|(x, y)| (x.clone(), y.get_type())) - .collect(), - ), + Value::Record { val, .. } => { + Type::Record(val.iter().map(|(x, y)| (x.clone(), y.get_type())).collect()) + } Value::List { vals, .. } => { let mut ty = None; for val in vals { @@ -595,9 +590,8 @@ impl Value { pub fn get_data_by_key(&self, name: &str) -> Option { match self { - Value::Record { cols, vals, .. } => cols + Value::Record { val, .. } => val .iter() - .zip(vals.iter()) .find(|(col, _)| col == &name) .map(|(_, val)| val.clone()), Value::List { vals, span } => { @@ -676,10 +670,9 @@ impl Value { .collect::>() .join(separator) ), - Value::Record { cols, vals, .. } => format!( + Value::Record { val, .. } => format!( "{{{}}}", - cols.iter() - .zip(vals.iter()) + val.iter() .map(|(x, y)| format!("{}: {}", x, y.into_string(", ", config))) .collect::>() .join(separator) @@ -739,10 +732,10 @@ impl Value { ) } } - Value::Record { cols, .. } => format!( + Value::Record { val, .. } => format!( "{{record {} field{}}}", - cols.len(), - if cols.len() == 1 { "" } else { "s" } + val.len(), + if val.len() == 1 { "" } else { "s" } ), Value::LazyRecord { val, .. } => match val.collect() { Ok(val) => val.into_abbreviated_string(config), @@ -800,10 +793,9 @@ impl Value { .collect::>() .join(separator) ), - Value::Record { cols, vals, .. } => format!( + Value::Record { val, .. } => format!( "{{{}}}", - cols.iter() - .zip(vals.iter()) + val.iter() .map(|(x, y)| format!("{}: {}", x, y.into_string_parsable(", ", config))) .collect::>() .join(separator) @@ -838,10 +830,9 @@ impl Value { .collect::>() .join(separator) ), - Value::Record { cols, vals, .. } => format!( + Value::Record { val, .. } => format!( "{{{}}}", - cols.iter() - .zip(vals.iter()) + val.iter() .map(|(x, y)| format!("{}: {}", x, y.into_string(", ", config))) .collect::>() .join(separator) @@ -866,7 +857,7 @@ impl Value { match self { Value::String { val, .. } => val.is_empty(), Value::List { vals, .. } => vals.is_empty(), - Value::Record { cols, .. } => cols.is_empty(), + Value::Record { val, .. } => val.is_empty(), Value::Binary { val, .. } => val.is_empty(), Value::Nothing { .. } => true, _ => false, @@ -981,12 +972,11 @@ impl Value { span: origin_span, optional, } => match &mut current { - Value::Record { cols, vals, span } => { - let cols = cols.clone(); + Value::Record { val, span } => { let span = *span; // Make reverse iterate to avoid duplicate column leads to first value, actually last value is expected. - if let Some(found) = cols.iter().zip(vals.iter()).rev().find(|x| { + if let Some(found) = val.iter().rev().find(|x| { if insensitive { x.0.to_lowercase() == column_name.to_lowercase() } else { @@ -998,7 +988,7 @@ impl Value { return Ok(Value::nothing(*origin_span)); // short-circuit } else { if from_user_input { - if let Some(suggestion) = did_you_mean(&cols, column_name) { + if let Some(suggestion) = did_you_mean(&val.cols, column_name) { return Err(ShellError::DidYouMean(suggestion, *origin_span)); } } @@ -1126,12 +1116,12 @@ impl Value { Value::List { vals, .. } => { for val in vals.iter_mut() { match val { - Value::Record { cols, vals, .. } => { + Value::Record { val: record, .. } => { let mut found = false; - for col in cols.iter().zip(vals.iter_mut()) { - if col.0 == col_name { + for (col, val) in record.iter_mut() { + if col == col_name { found = true; - col.1.upsert_data_at_cell_path( + val.upsert_data_at_cell_path( &cell_path[1..], new_val.clone(), )? @@ -1139,15 +1129,11 @@ impl Value { } if !found { if cell_path.len() == 1 { - cols.push(col_name.clone()); - vals.push(new_val); + record.push(col_name, new_val); break; } else { - let mut new_col = Value::Record { - cols: vec![], - vals: vec![], - span: new_val.span()?, - }; + let mut new_col = + Value::record(Record::new(), new_val.span()?); new_col.upsert_data_at_cell_path( &cell_path[1..], new_val, @@ -1168,30 +1154,25 @@ impl Value { } } } - Value::Record { cols, vals, .. } => { + Value::Record { val: record, .. } => { let mut found = false; - for col in cols.iter().zip(vals.iter_mut()) { - if col.0 == col_name { + for (col, val) in record.iter_mut() { + if col == col_name { found = true; - - col.1 - .upsert_data_at_cell_path(&cell_path[1..], new_val.clone())? + val.upsert_data_at_cell_path(&cell_path[1..], new_val.clone())? } } if !found { - cols.push(col_name.clone()); - if cell_path.len() == 1 { - vals.push(new_val); + let new_col = if cell_path.len() == 1 { + new_val } else { - let mut new_col = Value::Record { - cols: vec![], - vals: vec![], - span: new_val.span()?, - }; + let mut new_col = Value::record(Record::new(), new_val.span()?); new_col.upsert_data_at_cell_path(&cell_path[1..], new_val)?; - vals.push(new_col); - } + new_col + }; + + record.push(col_name, new_col); } } Value::LazyRecord { val, .. } => { @@ -1275,15 +1256,14 @@ impl Value { for val in vals.iter_mut() { match val { Value::Record { - cols, - vals, + val: record, span: v_span, } => { let mut found = false; - for col in cols.iter().zip(vals.iter_mut()) { - if col.0 == col_name { + for (col, val) in record.iter_mut() { + if col == col_name { found = true; - col.1.update_data_at_cell_path( + val.update_data_at_cell_path( &cell_path[1..], new_val.clone(), )? @@ -1309,18 +1289,15 @@ impl Value { } } Value::Record { - cols, - vals, + val: record, span: v_span, } => { let mut found = false; - for col in cols.iter().zip(vals.iter_mut()) { - if col.0 == col_name { + for (col, val) in record.iter_mut() { + if col == col_name { found = true; - - col.1 - .update_data_at_cell_path(&cell_path[1..], new_val.clone())? + val.update_data_at_cell_path(&cell_path[1..], new_val.clone())? } } if !found { @@ -1392,16 +1369,15 @@ impl Value { for val in vals.iter_mut() { match val { Value::Record { - cols, - vals, + val: record, span: v_span, } => { let mut found = false; let mut index = 0; - cols.retain_mut(|col| { + record.cols.retain_mut(|col| { if col == col_name { found = true; - vals.remove(index); + record.vals.remove(index); false } else { index += 1; @@ -1428,18 +1404,21 @@ impl Value { Ok(()) } Value::Record { - cols, - vals, + val: record, span: v_span, } => { let mut found = false; - for (i, col) in cols.clone().iter().enumerate() { + let mut index = 0; + record.cols.retain_mut(|col| { if col == col_name { - cols.remove(i); - vals.remove(i); found = true; + record.vals.remove(index); + false + } else { + index += 1; + true } - } + }); if !found && !optional { return Err(ShellError::CantFindColumn { col_name: col_name.to_string(), @@ -1501,15 +1480,14 @@ impl Value { for val in vals.iter_mut() { match val { Value::Record { - cols, - vals, + val: record, span: v_span, } => { let mut found = false; - for col in cols.iter().zip(vals.iter_mut()) { - if col.0 == col_name { + for (col, val) in record.iter_mut() { + if col == col_name { found = true; - col.1.remove_data_at_cell_path(&cell_path[1..])? + val.remove_data_at_cell_path(&cell_path[1..])? } } if !found && !optional { @@ -1532,17 +1510,15 @@ impl Value { Ok(()) } Value::Record { - cols, - vals, + val: record, span: v_span, } => { let mut found = false; - for col in cols.iter().zip(vals.iter_mut()) { - if col.0 == col_name { + for (col, val) in record.iter_mut() { + if col == col_name { found = true; - - col.1.remove_data_at_cell_path(&cell_path[1..])? + val.remove_data_at_cell_path(&cell_path[1..])? } } if !found && !optional { @@ -1613,12 +1589,11 @@ impl Value { for val in vals.iter_mut() { match val { Value::Record { - cols, - vals, + val: record, span: v_span, } => { - for col in cols.iter().zip(vals.iter_mut()) { - if col.0 == col_name { + for (col, val) in record.iter_mut() { + if col == col_name { if cell_path.len() == 1 { return Err(ShellError::ColumnAlreadyExists { col_name: col_name.to_string(), @@ -1626,7 +1601,7 @@ impl Value { src_span: *v_span, }); } else { - return col.1.insert_data_at_cell_path( + return val.insert_data_at_cell_path( &cell_path[1..], new_val, head_span, @@ -1635,8 +1610,7 @@ impl Value { } } - cols.push(col_name.clone()); - vals.push(new_val.clone()); + record.push(col_name, new_val.clone()); } // SIGH... Value::Error { error } => return Err(*error.clone()), @@ -1652,12 +1626,11 @@ impl Value { } } Value::Record { - cols, - vals, + val: record, span: v_span, } => { - for col in cols.iter().zip(vals.iter_mut()) { - if col.0 == col_name { + for (col, val) in record.iter_mut() { + if col == col_name { if cell_path.len() == 1 { return Err(ShellError::ColumnAlreadyExists { col_name: col_name.to_string(), @@ -1665,7 +1638,7 @@ impl Value { src_span: *v_span, }); } else { - return col.1.insert_data_at_cell_path( + return val.insert_data_at_cell_path( &cell_path[1..], new_val, head_span, @@ -1674,8 +1647,7 @@ impl Value { } } - cols.push(col_name.clone()); - vals.push(new_val); + record.push(col_name, new_val); } Value::LazyRecord { val, span } => { // convert to Record first. @@ -1734,7 +1706,7 @@ impl Value { pub fn columns(&self) -> &[String] { match self { - Value::Record { cols, .. } => cols, + Value::Record { val, .. } => &val.cols, _ => &[], } } @@ -1777,18 +1749,8 @@ impl Value { } } - pub fn record(cols: Vec, vals: Vec, span: Span) -> Value { - Value::Record { cols, vals, span } - } - - pub fn record_from_hashmap(map: &HashMap, span: Span) -> Value { - let mut cols = vec![]; - let mut vals = vec![]; - for (key, val) in map.iter() { - cols.push(key.clone()); - vals.push(val.clone()); - } - Value::record(cols, vals, span) + pub fn record(val: Record, span: Span) -> Value { + Value::Record { val, span } } pub fn list(vals: Vec, span: Span) -> Value { @@ -1894,12 +1856,8 @@ impl Value { /// Note: Only use this for test data, *not* live data, as it will point into unknown source /// when used in errors. - pub fn test_record(cols: Vec>, vals: Vec) -> Value { - Value::record( - cols.into_iter().map(|s| s.into()).collect(), - vals, - Span::test_data(), - ) + pub fn test_record(val: Record) -> Value { + Value::record(val, Span::test_data()) } /// Note: Only use this for test data, *not* live data, as it will point into unknown source @@ -2148,14 +2106,7 @@ impl PartialOrd for Value { Value::CustomValue { .. } => Some(Ordering::Less), Value::MatchPattern { .. } => Some(Ordering::Less), }, - ( - Value::Record { - cols: lhs_cols, - vals: lhs_vals, - .. - }, - rhs, - ) => match rhs { + (Value::Record { val: lhs, .. }, rhs) => match rhs { Value::Bool { .. } => Some(Ordering::Greater), Value::Int { .. } => Some(Ordering::Greater), Value::Float { .. } => Some(Ordering::Greater), @@ -2164,18 +2115,12 @@ impl PartialOrd for Value { Value::Date { .. } => Some(Ordering::Greater), Value::Range { .. } => Some(Ordering::Greater), Value::String { .. } => Some(Ordering::Greater), - Value::Record { - cols: rhs_cols, - vals: rhs_vals, - .. - } => { + Value::Record { val: rhs, .. } => { // reorder cols and vals to make more logically compare. // more general, if two record have same col and values, // the order of cols shouldn't affect the equal property. - let (lhs_cols_ordered, lhs_vals_ordered) = - reorder_record_inner(lhs_cols, lhs_vals); - let (rhs_cols_ordered, rhs_vals_ordered) = - reorder_record_inner(rhs_cols, rhs_vals); + let (lhs_cols_ordered, lhs_vals_ordered) = reorder_record_inner(lhs); + let (rhs_cols_ordered, rhs_vals_ordered) = reorder_record_inner(rhs); let result = lhs_cols_ordered.partial_cmp(&rhs_cols_ordered); if result == Some(Ordering::Equal) { @@ -3190,8 +3135,8 @@ impl Value { val: rhs.contains(lhs), span, }), - (Value::String { val: lhs, .. }, Value::Record { cols: rhs, .. }) => Ok(Value::Bool { - val: rhs.contains(lhs), + (Value::String { val: lhs, .. }, Value::Record { val: rhs, .. }) => Ok(Value::Bool { + val: rhs.cols.contains(lhs), span, }), (Value::String { .. } | Value::Int { .. }, Value::CellPath { val: rhs, .. }) => { @@ -3247,8 +3192,8 @@ impl Value { val: !rhs.contains(lhs), span, }), - (Value::String { val: lhs, .. }, Value::Record { cols: rhs, .. }) => Ok(Value::Bool { - val: !rhs.contains(lhs), + (Value::String { val: lhs, .. }, Value::Record { val: rhs, .. }) => Ok(Value::Bool { + val: !rhs.cols.contains(lhs), span, }), (Value::String { .. } | Value::Int { .. }, Value::CellPath { val: rhs, .. }) => { @@ -3648,36 +3593,10 @@ impl Value { } } -fn reorder_record_inner(cols: &[String], vals: &[Value]) -> (Vec, Vec) { - let mut kv_pairs = - iter::zip(cols.to_owned(), vals.to_owned()).collect::>(); - kv_pairs.sort_by(|a, b| { - a.0.partial_cmp(&b.0) - .expect("Columns should support compare") - }); - let (mut cols, mut vals) = (vec![], vec![]); - for (col, val) in kv_pairs { - cols.push(col); - vals.push(val); - } - (cols, vals) -} - -/// Create a Value::Record from a spanned hashmap -impl From>> for Value { - fn from(input: Spanned>) -> Self { - let span = input.span; - let (cols, vals) = input - .item - .into_iter() - .fold((vec![], vec![]), |mut acc, (k, v)| { - acc.0.push(k); - acc.1.push(v); - acc - }); - - Value::Record { cols, vals, span } - } +fn reorder_record_inner(record: &Record) -> (Vec<&String>, Vec<&Value>) { + let mut kv_pairs = record.iter().collect::>(); + kv_pairs.sort_by_key(|(col, _)| *col); + kv_pairs.into_iter().unzip() } fn type_compatible(a: Type, b: Type) -> bool { @@ -3688,23 +3607,6 @@ fn type_compatible(a: Type, b: Type) -> bool { matches!((a, b), (Type::Int, Type::Float) | (Type::Float, Type::Int)) } -/// Create a Value::Record from a spanned indexmap -impl From>> for Value { - fn from(input: Spanned>) -> Self { - let span = input.span; - let (cols, vals) = input - .item - .into_iter() - .fold((vec![], vec![]), |mut acc, (k, v)| { - acc.0.push(k); - acc.1.push(v); - acc - }); - - Value::Record { cols, vals, span } - } -} - /// Is the given year a leap year? #[allow(clippy::nonminimal_bool)] pub fn is_leap_year(year: i32) -> bool { @@ -3985,7 +3887,7 @@ fn get_filesize_format(format_value: &str, filesize_metric: Option) -> (By #[cfg(test)] mod tests { - use super::{Span, Value}; + use super::{Record, Span, Value}; mod is_empty { use super::*; @@ -4013,26 +3915,24 @@ mod tests { #[test] fn test_record() { - let no_columns_nor_cell_values = Value::Record { - cols: vec![], - vals: vec![], - span: Span::unknown(), - }; - let one_column_and_one_cell_value_with_empty_strings = Value::Record { + let no_columns_nor_cell_values = Value::test_record(Record::new()); + + let one_column_and_one_cell_value_with_empty_strings = Value::test_record(Record { cols: vec![String::from("")], vals: vec![Value::string("", Span::unknown())], - span: Span::unknown(), - }; - let one_column_with_a_string_and_one_cell_value_with_empty_string = Value::Record { - cols: vec![String::from("column")], - vals: vec![Value::string("", Span::unknown())], - span: Span::unknown(), - }; - let one_column_with_empty_string_and_one_value_with_a_string = Value::Record { - cols: vec![String::from("")], - vals: vec![Value::string("text", Span::unknown())], - span: Span::unknown(), - }; + }); + + let one_column_with_a_string_and_one_cell_value_with_empty_string = + Value::test_record(Record { + cols: vec![String::from("column")], + vals: vec![Value::string("", Span::unknown())], + }); + + let one_column_with_empty_string_and_one_value_with_a_string = + Value::test_record(Record { + cols: vec![String::from("")], + vals: vec![Value::string("text", Span::unknown())], + }); assert!(no_columns_nor_cell_values.is_empty()); assert!(!one_column_and_one_cell_value_with_empty_strings.is_empty()); diff --git a/crates/nu-protocol/src/value/record.rs b/crates/nu-protocol/src/value/record.rs new file mode 100644 index 0000000000..d28f3df2cf --- /dev/null +++ b/crates/nu-protocol/src/value/record.rs @@ -0,0 +1,99 @@ +use crate::Value; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct Record { + pub cols: Vec, + pub vals: Vec, +} + +impl Record { + pub fn new() -> Self { + Self::default() + } + + pub fn with_capacity(capacity: usize) -> Self { + Self { + cols: Vec::with_capacity(capacity), + vals: Vec::with_capacity(capacity), + } + } + + pub fn iter(&self) -> Iter { + self.into_iter() + } + + pub fn iter_mut(&mut self) -> IterMut { + self.into_iter() + } + + pub fn is_empty(&self) -> bool { + self.cols.is_empty() || self.vals.is_empty() + } + + pub fn len(&self) -> usize { + usize::min(self.cols.len(), self.vals.len()) + } + + pub fn push(&mut self, col: impl Into, val: Value) { + self.cols.push(col.into()); + self.vals.push(val); + } +} + +impl FromIterator<(String, Value)> for Record { + fn from_iter>(iter: T) -> Self { + let (cols, vals) = iter.into_iter().unzip(); + Self { cols, vals } + } +} + +pub type IntoIter = std::iter::Zip, std::vec::IntoIter>; + +impl IntoIterator for Record { + type Item = (String, Value); + + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.cols.into_iter().zip(self.vals) + } +} + +pub type Iter<'a> = std::iter::Zip, std::slice::Iter<'a, Value>>; + +impl<'a> IntoIterator for &'a Record { + type Item = (&'a String, &'a Value); + + type IntoIter = Iter<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.cols.iter().zip(&self.vals) + } +} + +pub type IterMut<'a> = std::iter::Zip, std::slice::IterMut<'a, Value>>; + +impl<'a> IntoIterator for &'a mut Record { + type Item = (&'a String, &'a mut Value); + + type IntoIter = IterMut<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.cols.iter().zip(&mut self.vals) + } +} + +#[macro_export] +macro_rules! record { + {$($col:expr => $val:expr),+ $(,)?} => { + $crate::Record { + cols: vec![$($col.into(),)+], + vals: vec![$($val,)+] + } + }; + {} => { + $crate::Record::new() + }; +} diff --git a/crates/nu-table/src/types/collapse.rs b/crates/nu-table/src/types/collapse.rs index b2367d4bfb..2ab2eadd7f 100644 --- a/crates/nu-table/src/types/collapse.rs +++ b/crates/nu-table/src/types/collapse.rs @@ -40,14 +40,14 @@ fn collapsed_table( fn colorize_value(value: &mut Value, config: &Config, style_computer: &StyleComputer) { match value { - Value::Record { cols, vals, .. } => { - for val in vals { + Value::Record { val: record, .. } => { + for val in &mut record.vals { colorize_value(val, config, style_computer); } let style = get_index_style(style_computer); if let Some(color) = style.color_style { - for header in cols { + for header in &mut record.cols { *header = color.paint(header.to_owned()).to_string(); } } diff --git a/crates/nu-table/src/types/expanded.rs b/crates/nu-table/src/types/expanded.rs index 458478496d..597b42ef03 100644 --- a/crates/nu-table/src/types/expanded.rs +++ b/crates/nu-table/src/types/expanded.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use nu_color_config::{Alignment, StyleComputer, TextStyle}; use nu_engine::column::get_columns; -use nu_protocol::{ast::PathMember, Config, ShellError, Span, TableIndexMode, Value}; +use nu_protocol::{ast::PathMember, Config, Record, ShellError, Span, TableIndexMode, Value}; use tabled::grid::config::Position; use crate::{ @@ -35,8 +35,8 @@ impl ExpandedTable { expanded_table_entry2(item, Cfg { opts, format: self }) } - pub fn build_map(self, cols: &[String], vals: &[Value], opts: TableOpts<'_>) -> StringResult { - expanded_table_kv(cols, vals, Cfg { opts, format: self }) + pub fn build_map(self, record: &Record, opts: TableOpts<'_>) -> StringResult { + expanded_table_kv(record, Cfg { opts, format: self }) } pub fn build_list(self, vals: &[Value], opts: TableOpts<'_>) -> StringResult { @@ -340,9 +340,14 @@ fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult { Ok(Some(TableOutput::new(table, true, with_index))) } -fn expanded_table_kv(cols: &[String], vals: &[Value], cfg: Cfg<'_>) -> StringResult { +fn expanded_table_kv(record: &Record, cfg: Cfg<'_>) -> StringResult { let theme = load_theme_from_config(cfg.opts.config); - let key_width = cols.iter().map(|col| string_width(col)).max().unwrap_or(0); + let key_width = record + .cols + .iter() + .map(|col| string_width(col)) + .max() + .unwrap_or(0); let count_borders = theme.has_inner() as usize + theme.has_right() as usize + theme.has_left() as usize; let padding = 2; @@ -352,8 +357,8 @@ fn expanded_table_kv(cols: &[String], vals: &[Value], cfg: Cfg<'_>) -> StringRes let value_width = cfg.opts.width - key_width - count_borders - padding - padding; - let mut data = Vec::with_capacity(cols.len()); - for (key, value) in cols.iter().zip(vals) { + let mut data = Vec::with_capacity(record.len()); + for (key, value) in record { if nu_utils::ctrl_c::was_pressed(&cfg.opts.ctrlc) { return Ok(None); } @@ -422,8 +427,8 @@ fn expand_table_value( } } } - Value::Record { cols, vals, span } => { - if cols.is_empty() { + Value::Record { val: record, span } => { + if record.is_empty() { // Like list case return styled string instead of empty value return Ok(Some(( value_to_wrapped_string(value, cfg, value_width), @@ -433,7 +438,7 @@ fn expand_table_value( let mut inner_cfg = dive_options(cfg, *span); inner_cfg.opts.width = value_width; - let result = expanded_table_kv(cols, vals, inner_cfg)?; + let result = expanded_table_kv(record, inner_cfg)?; match result { Some(result) => Ok(Some((result, true))), None => Ok(Some(( @@ -480,14 +485,14 @@ fn expanded_table_entry2(item: &Value, cfg: Cfg<'_>) -> NuText { } match &item { - Value::Record { cols, vals, span } => { - if cols.is_empty() && vals.is_empty() { + Value::Record { val: record, span } => { + if record.is_empty() { return nu_value_to_string(item, cfg.opts.config, cfg.opts.style_computer); } // we verify what is the structure of a Record cause it might represent let inner_cfg = dive_options(&cfg, *span); - let table = expanded_table_kv(cols, vals, inner_cfg); + let table = expanded_table_kv(record, inner_cfg); match table { Ok(Some(table)) => (table, TextStyle::default()), diff --git a/crates/nu-table/src/types/general.rs b/crates/nu-table/src/types/general.rs index bc32c91969..7f9195f6aa 100644 --- a/crates/nu-table/src/types/general.rs +++ b/crates/nu-table/src/types/general.rs @@ -1,6 +1,6 @@ use nu_color_config::TextStyle; use nu_engine::column::get_columns; -use nu_protocol::{ast::PathMember, Config, ShellError, Span, TableIndexMode, Value}; +use nu_protocol::{ast::PathMember, Config, Record, ShellError, Span, TableIndexMode, Value}; use crate::{ clean_charset, @@ -18,8 +18,8 @@ impl JustTable { create_table(input, opts) } - pub fn kv_table(cols: &[String], vals: &[Value], opts: TableOpts<'_>) -> StringResult { - kv_table(cols, vals, opts) + pub fn kv_table(record: &Record, opts: TableOpts<'_>) -> StringResult { + kv_table(record, opts) } } @@ -38,9 +38,9 @@ fn create_table(input: &[Value], opts: TableOpts<'_>) -> Result, } } -fn kv_table(cols: &[String], vals: &[Value], opts: TableOpts<'_>) -> StringResult { - let mut data = vec![Vec::with_capacity(2); cols.len()]; - for ((column, value), row) in cols.iter().zip(vals.iter()).zip(data.iter_mut()) { +fn kv_table(record: &Record, opts: TableOpts<'_>) -> StringResult { + let mut data = vec![Vec::with_capacity(2); record.len()]; + for ((column, value), row) in record.iter().zip(data.iter_mut()) { if nu_utils::ctrl_c::was_pressed(&opts.ctrlc) { return Ok(None); } diff --git a/crates/nu-table/src/unstructured_table.rs b/crates/nu-table/src/unstructured_table.rs index bc3196cb49..a5b2e5d123 100644 --- a/crates/nu-table/src/unstructured_table.rs +++ b/crates/nu-table/src/unstructured_table.rs @@ -1,5 +1,5 @@ use nu_color_config::StyleComputer; -use nu_protocol::{Config, Span, Value}; +use nu_protocol::{Config, Record, Span, Value}; use tabled::{ grid::{ color::{AnsiColor, StaticColor}, @@ -90,7 +90,7 @@ fn build_table( fn convert_nu_value_to_table_value(value: Value, config: &Config) -> TableValue { match value { - Value::Record { cols, vals, .. } => build_vertical_map(cols, vals, config), + Value::Record { val, .. } => build_vertical_map(val, config), Value::List { vals, .. } => { let rebuild_array_as_map = is_valid_record(&vals) && count_columns_in_record(&vals) > 0; if rebuild_array_as_map { @@ -110,9 +110,9 @@ fn convert_nu_value_to_table_value(value: Value, config: &Config) -> TableValue } } -fn build_vertical_map(cols: Vec, vals: Vec, config: &Config) -> TableValue { - let mut rows = Vec::with_capacity(cols.len()); - for (key, value) in cols.into_iter().zip(vals) { +fn build_vertical_map(record: Record, config: &Config) -> TableValue { + let mut rows = Vec::with_capacity(record.len()); + for (key, value) in record { let val = convert_nu_value_to_table_value(value, config); let row = TableValue::Row(vec![TableValue::Cell(key), val]); rows.push(row); @@ -160,14 +160,14 @@ fn is_valid_record(vals: &[Value]) -> bool { let mut used_cols: Option<&[String]> = None; for val in vals { match val { - Value::Record { cols, .. } => { + Value::Record { val, .. } => { let cols_are_not_equal = - used_cols.is_some() && !matches!(used_cols, Some(used) if cols == used); + used_cols.is_some() && !matches!(used_cols, Some(used) if val.cols == used); if cols_are_not_equal { return false; } - used_cols = Some(cols); + used_cols = Some(&val.cols); } _ => return false, } @@ -178,7 +178,7 @@ fn is_valid_record(vals: &[Value]) -> bool { fn count_columns_in_record(vals: &[Value]) -> usize { match vals.iter().next() { - Some(Value::Record { cols, .. }) => cols.len(), + Some(Value::Record { val, .. }) => val.len(), _ => 0, } } @@ -194,8 +194,8 @@ fn build_map_from_record(vals: Vec, config: &Config) -> TableValue { for val in vals { match val { - Value::Record { vals, .. } => { - for (i, cell) in vals.into_iter().take(count_columns).enumerate() { + Value::Record { val, .. } => { + for (i, cell) in val.vals.into_iter().take(count_columns).enumerate() { let cell = convert_nu_value_to_table_value(cell, config); list[i].push(cell); } @@ -211,7 +211,7 @@ fn build_map_from_record(vals: Vec, config: &Config) -> TableValue { fn get_columns_in_record(vals: &[Value]) -> Vec { match vals.iter().next() { - Some(Value::Record { cols, .. }) => cols.clone(), + Some(Value::Record { val, .. }) => val.cols.clone(), _ => vec![], } } diff --git a/crates/nu_plugin_example/src/example.rs b/crates/nu_plugin_example/src/example.rs index 75df43f4d7..7245c55088 100644 --- a/crates/nu_plugin_example/src/example.rs +++ b/crates/nu_plugin_example/src/example.rs @@ -1,5 +1,5 @@ use nu_plugin::{EvaluatedCall, LabeledError}; -use nu_protocol::Value; +use nu_protocol::{Record, Value}; pub struct Example; impl Example { @@ -67,11 +67,13 @@ impl Example { .map(|v| Value::int(v * i, call.head)) .collect::>(); - Value::Record { - cols: cols.clone(), - vals, - span: call.head, - } + Value::record( + Record { + cols: cols.clone(), + vals, + }, + call.head, + ) }) .collect::>(); diff --git a/crates/nu_plugin_formats/src/from/eml.rs b/crates/nu_plugin_formats/src/from/eml.rs index 30b6a9d6b1..fc792f3410 100644 --- a/crates/nu_plugin_formats/src/from/eml.rs +++ b/crates/nu_plugin_formats/src/from/eml.rs @@ -2,7 +2,7 @@ use eml_parser::eml::*; use eml_parser::EmlParser; use indexmap::map::IndexMap; use nu_plugin::{EvaluatedCall, LabeledError}; -use nu_protocol::{PluginExample, ShellError, Span, Spanned, Value}; +use nu_protocol::{record, PluginExample, Record, ShellError, Span, Value}; const DEFAULT_BODY_PREVIEW: usize = 50; pub const CMD_NAME: &str = "from eml"; @@ -24,7 +24,7 @@ Subject: Welcome To: someone@somewhere.com Test' | from eml" .into(), - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec![ "Subject".to_string(), "From".to_string(), @@ -33,26 +33,23 @@ Test' | from eml" ], vals: vec![ Value::test_string("Welcome"), - Value::Record { + Value::test_record(Record { cols: vec!["Name".to_string(), "Address".to_string()], vals: vec![ Value::nothing(Span::test_data()), Value::test_string("test@email.com"), ], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["Name".to_string(), "Address".to_string()], vals: vec![ Value::nothing(Span::test_data()), Value::test_string("someone@somewhere.com"), ], - span: Span::test_data(), - }, + }), Value::test_string("Test"), ], - span: Span::test_data(), - }), + })), }, PluginExample { description: "Convert eml structured data into record".into(), @@ -61,7 +58,7 @@ Subject: Welcome To: someone@somewhere.com Test' | from eml -b 1" .into(), - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec![ "Subject".to_string(), "From".to_string(), @@ -70,26 +67,23 @@ Test' | from eml -b 1" ], vals: vec![ Value::test_string("Welcome"), - Value::Record { + Value::test_record(Record { cols: vec!["Name".to_string(), "Address".to_string()], vals: vec![ Value::nothing(Span::test_data()), Value::test_string("test@email.com"), ], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec!["Name".to_string(), "Address".to_string()], vals: vec![ Value::nothing(Span::test_data()), Value::test_string("someone@somewhere.com"), ], - span: Span::test_data(), - }, + }), Value::test_string("T"), ], - span: Span::test_data(), - }), + })), }, ] } @@ -115,11 +109,13 @@ fn emailaddress_to_value(span: Span, email_address: &EmailAddress) -> Value { ), }; - Value::Record { - cols: vec!["Name".to_string(), "Address".to_string()], - vals: vec![n, a], + Value::record( + record! { + "Name" => n, + "Address" => a, + }, span, - } + ) } fn headerfieldvalue_to_value(head: Span, value: &HeaderFieldValue) -> Value { @@ -186,8 +182,5 @@ fn from_eml(input: &Value, body_preview: usize, head: Span) -> Result Vec { .into(), description: "Converts ics formatted string to table".into(), result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec![ "properties".to_string(), "events".to_string(), @@ -90,37 +90,25 @@ pub fn examples() -> Vec { span: Span::test_data(), }, ], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }] } fn calendar_to_value(calendar: IcalCalendar, span: Span) -> Value { - let mut row = IndexMap::new(); - - row.insert( - "properties".to_string(), - properties_to_value(calendar.properties, span), - ); - row.insert("events".to_string(), events_to_value(calendar.events, span)); - row.insert("alarms".to_string(), alarms_to_value(calendar.alarms, span)); - row.insert("to-Dos".to_string(), todos_to_value(calendar.todos, span)); - row.insert( - "journals".to_string(), - journals_to_value(calendar.journals, span), - ); - row.insert( - "free-busys".to_string(), - free_busys_to_value(calendar.free_busys, span), - ); - row.insert( - "timezones".to_string(), - timezones_to_value(calendar.timezones, span), - ); - - Value::from(Spanned { item: row, span }) + Value::record( + record! { + "properties" => properties_to_value(calendar.properties, span), + "events" => events_to_value(calendar.events, span), + "alarms" => alarms_to_value(calendar.alarms, span), + "to-Dos" => todos_to_value(calendar.todos, span), + "journals" => journals_to_value(calendar.journals, span), + "free-busys" => free_busys_to_value(calendar.free_busys, span), + "timezones" => timezones_to_value(calendar.timezones, span), + }, + span, + ) } fn events_to_value(events: Vec, span: Span) -> Value { @@ -128,13 +116,13 @@ fn events_to_value(events: Vec, span: Span) -> Value { vals: events .into_iter() .map(|event| { - let mut row = IndexMap::new(); - row.insert( - "properties".to_string(), - properties_to_value(event.properties, span), - ); - row.insert("alarms".to_string(), alarms_to_value(event.alarms, span)); - Value::from(Spanned { item: row, span }) + Value::record( + record! { + "properties" => properties_to_value(event.properties, span), + "alarms" => alarms_to_value(event.alarms, span), + }, + span, + ) }) .collect::>(), span, @@ -146,12 +134,10 @@ fn alarms_to_value(alarms: Vec, span: Span) -> Value { vals: alarms .into_iter() .map(|alarm| { - let mut row = IndexMap::new(); - row.insert( - "properties".to_string(), - properties_to_value(alarm.properties, span), - ); - Value::from(Spanned { item: row, span }) + Value::record( + record! { "properties" => properties_to_value(alarm.properties, span), }, + span, + ) }) .collect::>(), span, @@ -163,13 +149,13 @@ fn todos_to_value(todos: Vec, span: Span) -> Value { vals: todos .into_iter() .map(|todo| { - let mut row = IndexMap::new(); - row.insert( - "properties".to_string(), - properties_to_value(todo.properties, span), - ); - row.insert("alarms".to_string(), alarms_to_value(todo.alarms, span)); - Value::from(Spanned { item: row, span }) + Value::record( + record! { + "properties" => properties_to_value(todo.properties, span), + "alarms" => alarms_to_value(todo.alarms, span), + }, + span, + ) }) .collect::>(), span, @@ -181,12 +167,10 @@ fn journals_to_value(journals: Vec, span: Span) -> Value { vals: journals .into_iter() .map(|journal| { - let mut row = IndexMap::new(); - row.insert( - "properties".to_string(), - properties_to_value(journal.properties, span), - ); - Value::from(Spanned { item: row, span }) + Value::record( + record! { "properties" => properties_to_value(journal.properties, span), }, + span, + ) }) .collect::>(), span, @@ -198,12 +182,10 @@ fn free_busys_to_value(free_busys: Vec, span: Span) -> Value { vals: free_busys .into_iter() .map(|free_busy| { - let mut row = IndexMap::new(); - row.insert( - "properties".to_string(), - properties_to_value(free_busy.properties, span), - ); - Value::from(Spanned { item: row, span }) + Value::record( + record! { "properties" => properties_to_value(free_busy.properties, span) }, + span, + ) }) .collect::>(), span, @@ -215,16 +197,13 @@ fn timezones_to_value(timezones: Vec, span: Span) -> Value { vals: timezones .into_iter() .map(|timezone| { - let mut row = IndexMap::new(); - row.insert( - "properties".to_string(), - properties_to_value(timezone.properties, span), - ); - row.insert( - "transitions".to_string(), - timezone_transitions_to_value(timezone.transitions, span), - ); - Value::from(Spanned { item: row, span }) + Value::record( + record! { + "properties" => properties_to_value(timezone.properties, span), + "transitions" => timezone_transitions_to_value(timezone.transitions, span), + }, + span, + ) }) .collect::>(), span, @@ -236,12 +215,10 @@ fn timezone_transitions_to_value(transitions: Vec, span: vals: transitions .into_iter() .map(|transition| { - let mut row = IndexMap::new(); - row.insert( - "properties".to_string(), - properties_to_value(transition.properties, span), - ); - Value::from(Spanned { item: row, span }) + Value::record( + record! { "properties" => properties_to_value(transition.properties, span) }, + span, + ) }) .collect::>(), span, @@ -253,8 +230,6 @@ fn properties_to_value(properties: Vec, span: Span) -> Value { vals: properties .into_iter() .map(|prop| { - let mut row = IndexMap::new(); - let name = Value::String { val: prop.name, span, @@ -268,10 +243,14 @@ fn properties_to_value(properties: Vec, span: Span) -> Value { None => Value::nothing(span), }; - row.insert("name".to_string(), name); - row.insert("value".to_string(), value); - row.insert("params".to_string(), params); - Value::from(Spanned { item: row, span }) + Value::record( + record! { + "name" => name, + "value" => value, + "params" => params, + }, + span, + ) }) .collect::>(), span, @@ -290,5 +269,5 @@ fn params_to_value(params: Vec<(String, Vec)>, span: Span) -> Value { row.insert(param_name, values); } - Value::from(Spanned { item: row, span }) + Value::record(row.into_iter().collect(), span) } diff --git a/crates/nu_plugin_formats/src/from/ini.rs b/crates/nu_plugin_formats/src/from/ini.rs index 3c09934f1f..50df889ffe 100644 --- a/crates/nu_plugin_formats/src/from/ini.rs +++ b/crates/nu_plugin_formats/src/from/ini.rs @@ -1,5 +1,5 @@ use nu_plugin::{EvaluatedCall, LabeledError}; -use nu_protocol::{PluginExample, ShellError, Span, Value}; +use nu_protocol::{PluginExample, Record, ShellError, Value}; pub const CMD_NAME: &str = "from ini"; @@ -11,52 +11,34 @@ pub fn from_ini_call(call: &EvaluatedCall, input: &Value) -> Result = ini::Ini::load_from_str(&input_string); match ini_config { Ok(config) => { - let mut sections: Vec = Vec::new(); - let mut sections_key_value_pairs: Vec = Vec::new(); + let mut sections = Record::new(); for (section, properties) in config.iter() { - let mut keys_for_section: Vec = Vec::new(); - let mut values_for_section: Vec = Vec::new(); + let mut section_record = Record::new(); + + // section's key value pairs + for (key, value) in properties.iter() { + section_record.push(key, Value::string(value, span)); + } + + let section_record = Value::record(section_record, span); // section match section { Some(section_name) => { - sections.push(section_name.to_owned()); + sections.push(section_name, section_record); } None => { // Section (None) allows for key value pairs without a section if !properties.is_empty() { - sections.push(String::new()); + sections.push(String::new(), section_record); } } } - - // section's key value pairs - for (key, value) in properties.iter() { - keys_for_section.push(key.to_owned()); - values_for_section.push(Value::String { - val: value.to_owned(), - span, - }); - } - - // section with its key value pairs - // Only add section if contains key,value pair - if !properties.is_empty() { - sections_key_value_pairs.push(Value::Record { - cols: keys_for_section, - vals: values_for_section, - span, - }); - } } // all sections with all its key value pairs - Ok(Value::Record { - cols: sections, - vals: sections_key_value_pairs, - span, - }) + Ok(Value::record(sections, span)) } Err(err) => Err(ShellError::UnsupportedInput( format!("Could not load ini: {err}"), @@ -75,14 +57,12 @@ a=1 b=2' | from ini" .into(), description: "Converts ini formatted string to record".into(), - result: Some(Value::Record { + result: Some(Value::test_record(Record { cols: vec!["foo".to_string()], - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![Value::test_string("1"), Value::test_string("2")], - span: Span::test_data(), - }], - span: Span::test_data(), - }), + })], + })), }] } diff --git a/crates/nu_plugin_formats/src/from/vcf.rs b/crates/nu_plugin_formats/src/from/vcf.rs index 8fd69b1d4e..e8ddcec7ee 100644 --- a/crates/nu_plugin_formats/src/from/vcf.rs +++ b/crates/nu_plugin_formats/src/from/vcf.rs @@ -2,7 +2,7 @@ use ical::parser::vcard::component::*; use ical::property::Property; use indexmap::map::IndexMap; use nu_plugin::{EvaluatedCall, LabeledError}; -use nu_protocol::{PluginExample, ShellError, Span, Spanned, Value}; +use nu_protocol::{record, PluginExample, Record, ShellError, Span, Value}; pub const CMD_NAME: &str = "from vcf"; @@ -50,11 +50,11 @@ END:VCARD' | from vcf" .into(), description: "Converts ics formatted string to table".into(), result: Some(Value::List { - vals: vec![Value::Record { + vals: vec![Value::test_record(Record { cols: vec!["properties".to_string()], vals: vec![Value::List { vals: vec![ - Value::Record { + Value::test_record(Record { cols: vec![ "name".to_string(), "value".to_string(), @@ -67,9 +67,8 @@ END:VCARD' | from vcf" span: Span::test_data(), }, ], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec![ "name".to_string(), "value".to_string(), @@ -82,9 +81,8 @@ END:VCARD' | from vcf" span: Span::test_data(), }, ], - span: Span::test_data(), - }, - Value::Record { + }), + Value::test_record(Record { cols: vec![ "name".to_string(), "value".to_string(), @@ -97,25 +95,21 @@ END:VCARD' | from vcf" span: Span::test_data(), }, ], - span: Span::test_data(), - }, + }), ], span: Span::test_data(), }], - span: Span::test_data(), - }], + })], span: Span::test_data(), }), }] } fn contact_to_value(contact: VcardContact, span: Span) -> Value { - let mut row = IndexMap::new(); - row.insert( - "properties".to_string(), - properties_to_value(contact.properties, span), - ); - Value::from(Spanned { item: row, span }) + Value::record( + record! { "properties" => properties_to_value(contact.properties, span) }, + span, + ) } fn properties_to_value(properties: Vec, span: Span) -> Value { @@ -123,8 +117,6 @@ fn properties_to_value(properties: Vec, span: Span) -> Value { vals: properties .into_iter() .map(|prop| { - let mut row = IndexMap::new(); - let name = Value::String { val: prop.name, span, @@ -138,10 +130,14 @@ fn properties_to_value(properties: Vec, span: Span) -> Value { None => Value::Nothing { span }, }; - row.insert("name".to_string(), name); - row.insert("value".to_string(), value); - row.insert("params".to_string(), params); - Value::from(Spanned { item: row, span }) + Value::record( + record! { + "name" => name, + "value" => value, + "params" => params, + }, + span, + ) }) .collect::>(), span, @@ -160,5 +156,5 @@ fn params_to_value(params: Vec<(String, Vec)>, span: Span) -> Value { row.insert(param_name, values); } - Value::from(Spanned { item: row, span }) + Value::record(row.into_iter().collect(), span) } diff --git a/crates/nu_plugin_gstat/src/gstat.rs b/crates/nu_plugin_gstat/src/gstat.rs index fd9f9985b7..98efb70773 100644 --- a/crates/nu_plugin_gstat/src/gstat.rs +++ b/crates/nu_plugin_gstat/src/gstat.rs @@ -1,6 +1,6 @@ use git2::{Branch, BranchType, DescribeOptions, Repository}; use nu_plugin::LabeledError; -use nu_protocol::{Span, Spanned, Value}; +use nu_protocol::{record, Span, Spanned, Value}; use std::fmt::Write; use std::ops::BitAnd; use std::path::PathBuf; @@ -142,57 +142,6 @@ impl GStat { "no_tag".to_string() }; - let mut cols = vec![]; - let mut vals = vec![]; - - cols.push("idx_added_staged".into()); - vals.push(Value::int(stats.idx_added_staged as i64, span)); - cols.push("idx_modified_staged".into()); - vals.push(Value::int(stats.idx_modified_staged as i64, span)); - cols.push("idx_deleted_staged".into()); - vals.push(Value::int(stats.idx_deleted_staged as i64, span)); - cols.push("idx_renamed".into()); - vals.push(Value::int(stats.idx_renamed as i64, span)); - cols.push("idx_type_changed".into()); - vals.push(Value::int(stats.idx_type_changed as i64, span)); - cols.push("wt_untracked".into()); - vals.push(Value::int(stats.wt_untracked as i64, span)); - cols.push("wt_modified".into()); - vals.push(Value::int(stats.wt_modified as i64, span)); - cols.push("wt_deleted".into()); - vals.push(Value::int(stats.wt_deleted as i64, span)); - cols.push("wt_type_changed".into()); - vals.push(Value::int(stats.wt_type_changed as i64, span)); - cols.push("wt_renamed".into()); - vals.push(Value::int(stats.wt_renamed as i64, span)); - cols.push("ignored".into()); - vals.push(Value::int(stats.ignored as i64, span)); - cols.push("conflicts".into()); - vals.push(Value::int(stats.conflicts as i64, span)); - cols.push("ahead".into()); - vals.push(Value::int(stats.ahead as i64, span)); - cols.push("behind".into()); - vals.push(Value::int(stats.behind as i64, span)); - cols.push("stashes".into()); - vals.push(Value::int(stats.stashes as i64, span)); - cols.push("repo_name".into()); - vals.push(Value::String { - val: repo_name, - span, - }); - cols.push("tag".into()); - vals.push(Value::String { val: tag, span }); - cols.push("branch".into()); - vals.push(Value::String { - val: stats.branch, - span, - }); - cols.push("remote".into()); - vals.push(Value::String { - val: stats.remote, - span, - }); - // Leave this in case we want to turn it into a table instead of a list // Ok(Value::List { // vals: vec![Value::Record { @@ -203,53 +152,57 @@ impl GStat { // span: *span, // }) - Ok(Value::Record { cols, vals, span }) + Ok(Value::record( + record! { + "idx_added_staged" => Value::int(stats.idx_added_staged as i64, span), + "idx_modified_staged" => Value::int(stats.idx_modified_staged as i64, span), + "idx_deleted_staged" => Value::int(stats.idx_deleted_staged as i64, span), + "idx_renamed" => Value::int(stats.idx_renamed as i64, span), + "idx_type_changed" => Value::int(stats.idx_type_changed as i64, span), + "wt_untracked" => Value::int(stats.wt_untracked as i64, span), + "wt_modified" => Value::int(stats.wt_modified as i64, span), + "wt_deleted" => Value::int(stats.wt_deleted as i64, span), + "wt_type_changed" => Value::int(stats.wt_type_changed as i64, span), + "wt_renamed" => Value::int(stats.wt_renamed as i64, span), + "ignored" => Value::int(stats.ignored as i64, span), + "conflicts" => Value::int(stats.conflicts as i64, span), + "ahead" => Value::int(stats.ahead as i64, span), + "behind" => Value::int(stats.behind as i64, span), + "stashes" => Value::int(stats.stashes as i64, span), + "repo_name" => Value::string(repo_name, span), + "tag" => Value::string(tag, span), + "branch" => Value::string(stats.branch, span), + "remote" => Value::string(stats.remote, span), + }, + span, + )) } fn create_empty_git_status(&self, span: Span) -> Value { - let mut cols = vec![]; - let mut vals = vec![]; - - cols.push("idx_added_staged".into()); - vals.push(Value::int(-1, span)); - cols.push("idx_modified_staged".into()); - vals.push(Value::int(-1, span)); - cols.push("idx_deleted_staged".into()); - vals.push(Value::int(-1, span)); - cols.push("idx_renamed".into()); - vals.push(Value::int(-1, span)); - cols.push("idx_type_changed".into()); - vals.push(Value::int(-1, span)); - cols.push("wt_untracked".into()); - vals.push(Value::int(-1, span)); - cols.push("wt_modified".into()); - vals.push(Value::int(-1, span)); - cols.push("wt_deleted".into()); - vals.push(Value::int(-1, span)); - cols.push("wt_type_changed".into()); - vals.push(Value::int(-1, span)); - cols.push("wt_renamed".into()); - vals.push(Value::int(-1, span)); - cols.push("ignored".into()); - vals.push(Value::int(-1, span)); - cols.push("conflicts".into()); - vals.push(Value::int(-1, span)); - cols.push("ahead".into()); - vals.push(Value::int(-1, span)); - cols.push("behind".into()); - vals.push(Value::int(-1, span)); - cols.push("stashes".into()); - vals.push(Value::int(-1, span)); - cols.push("repo_name".into()); - vals.push(Value::string("no_repository", span)); - cols.push("tag".into()); - vals.push(Value::string("no_tag", span)); - cols.push("branch".into()); - vals.push(Value::string("no_branch", span)); - cols.push("remote".into()); - vals.push(Value::string("no_remote", span)); - - Value::Record { cols, vals, span } + Value::record( + record! { + "idx_added_staged" => Value::int(-1, span), + "idx_modified_staged" => Value::int(-1, span), + "idx_deleted_staged" => Value::int(-1, span), + "idx_renamed" => Value::int(-1, span), + "idx_type_changed" => Value::int(-1, span), + "wt_untracked" => Value::int(-1, span), + "wt_modified" => Value::int(-1, span), + "wt_deleted" => Value::int(-1, span), + "wt_type_changed" => Value::int(-1, span), + "wt_renamed" => Value::int(-1, span), + "ignored" => Value::int(-1, span), + "conflicts" => Value::int(-1, span), + "ahead" => Value::int(-1, span), + "behind" => Value::int(-1, span), + "stashes" => Value::int(-1, span), + "repo_name" => Value::string("no_repository", span), + "tag" => Value::string("no_tag", span), + "branch" => Value::string("no_branch", span), + "remote" => Value::string("no_remote", span), + }, + span, + ) } } diff --git a/crates/nu_plugin_query/src/query_json.rs b/crates/nu_plugin_query/src/query_json.rs index 05a6882aef..b36f23873a 100644 --- a/crates/nu_plugin_query/src/query_json.rs +++ b/crates/nu_plugin_query/src/query_json.rs @@ -1,6 +1,6 @@ use gjson::Value as gjValue; use nu_plugin::{EvaluatedCall, LabeledError}; -use nu_protocol::{Span, Spanned, Value}; +use nu_protocol::{Record, Span, Spanned, Value}; pub fn execute_json_query( _name: &str, @@ -94,14 +94,12 @@ fn convert_gjson_value_to_nu_value(v: &gjValue, span: Span) -> Value { gjson::Kind::String => Value::string(v.str(), span), gjson::Kind::True => Value::bool(true, span), gjson::Kind::Object => { - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); v.each(|k, v| { - cols.push(k.to_string()); - vals.push(convert_gjson_value_to_nu_value(&v, span)); + record.push(k.to_string(), convert_gjson_value_to_nu_value(&v, span)); true }); - Value::Record { cols, vals, span } + Value::record(record, span) } } } diff --git a/crates/nu_plugin_query/src/query_web.rs b/crates/nu_plugin_query/src/query_web.rs index 60e14e2d6b..a9eb4c2890 100644 --- a/crates/nu_plugin_query/src/query_web.rs +++ b/crates/nu_plugin_query/src/query_web.rs @@ -1,6 +1,6 @@ use crate::web_tables::WebTable; use nu_plugin::{EvaluatedCall, LabeledError}; -use nu_protocol::{Span, Value}; +use nu_protocol::{Record, Span, Value}; use scraper::{Html, Selector as ScraperSelector}; pub struct Selector { @@ -170,15 +170,13 @@ fn retrieve_table(mut table: WebTable, columns: &Value, span: Span) -> Value { at_least_one_row_filled = true; let table_with_no_empties: Vec<_> = table.iter().filter(|item| !item.is_empty()).collect(); - let mut cols = vec![]; - let mut vals = vec![]; + let mut record = Record::new(); for row in &table_with_no_empties { for (counter, cell) in row.iter().enumerate() { - cols.push(format!("column{counter}")); - vals.push(Value::string(cell.to_string(), span)) + record.push(format!("column{counter}"), Value::string(cell, span)); } } - table_out.push(Value::Record { cols, vals, span }) + table_out.push(Value::record(record, span)) } else { for row in &table { let mut vals = vec![]; @@ -194,11 +192,13 @@ fn retrieve_table(mut table: WebTable, columns: &Value, span: Span) -> Value { } vals.push(Value::string(val, span)); } - table_out.push(Value::Record { - cols: record_cols.to_vec(), - vals, + table_out.push(Value::record( + Record { + cols: record_cols.to_vec(), + vals, + }, span, - }) + )) } } if !at_least_one_row_filled { diff --git a/crates/nu_plugin_query/src/query_xml.rs b/crates/nu_plugin_query/src/query_xml.rs index aa170e208b..0fe8974678 100644 --- a/crates/nu_plugin_query/src/query_xml.rs +++ b/crates/nu_plugin_query/src/query_xml.rs @@ -1,5 +1,5 @@ use nu_plugin::{EvaluatedCall, LabeledError}; -use nu_protocol::{Span, Spanned, Value}; +use nu_protocol::{record, Record, Span, Spanned, Value}; use sxd_document::parser; use sxd_xpath::{Context, Factory}; @@ -53,39 +53,30 @@ pub fn execute_xpath_query( match res { Ok(r) => { - let mut cols: Vec = vec![]; - let mut vals: Vec = vec![]; + let mut record = Record::new(); let mut records: Vec = vec![]; match r { sxd_xpath::Value::Nodeset(ns) => { for n in ns.into_iter() { - cols.push(key.to_string()); - vals.push(Value::string(n.string_value(), call.head)); + record.push(key.clone(), Value::string(n.string_value(), call.head)); } } sxd_xpath::Value::Boolean(b) => { - cols.push(key.to_string()); - vals.push(Value::bool(b, call.head)); + record.push(key, Value::bool(b, call.head)); } sxd_xpath::Value::Number(n) => { - cols.push(key.to_string()); - vals.push(Value::float(n, call.head)); + record.push(key, Value::float(n, call.head)); } sxd_xpath::Value::String(s) => { - cols.push(key.to_string()); - vals.push(Value::string(s, call.head)); + record.push(key, Value::string(s, call.head)); } }; // convert the cols and vecs to a table by creating individual records // for each item so we can then use a list to make a table - for (k, v) in cols.iter().zip(vals.iter()) { - records.push(Value::Record { - cols: vec![k.to_string()], - vals: vec![v.clone()], - span: call.head, - }) + for (k, v) in record { + records.push(Value::record(record! { k => v }, call.head)) } Ok(Value::List { @@ -123,7 +114,7 @@ fn build_xpath(xpath_str: &str, span: Span) -> Result