Remove Record::from_raw_cols_vals_unchecked (#11810)

# Description
Follows from #11718 and replaces all usages of
`Record::from_raw_cols_vals_unchecked` with iterator or `record!`
equivalents.
This commit is contained in:
Ian Manske 2024-02-18 12:20:22 +00:00 committed by GitHub
parent 28f0f32ae7
commit fb4251aba7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 267 additions and 359 deletions

View File

@ -5,7 +5,7 @@ use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value, record, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
}; };
use polars::prelude::*; use polars::prelude::*;
@ -52,13 +52,10 @@ impl Command for CastDF {
description: "Cast a column in a dataframe to a different dtype", description: "Cast a column in a dataframe to a different dtype",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr cast u8 a | dfr schema", example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr cast u8 a | dfr schema",
result: Some(Value::record( result: Some(Value::record(
Record::from_raw_cols_vals_unchecked( record! {
vec!["a".to_string(), "b".to_string()], "a" => Value::string("u8", Span::test_data()),
vec![ "b" => Value::string("i64", Span::test_data()),
Value::string("u8", Span::test_data()), },
Value::string("i64", Span::test_data()),
],
),
Span::test_data(), Span::test_data(),
)), )),
}, },
@ -66,13 +63,10 @@ impl Command for CastDF {
description: "Cast a column in a lazy dataframe to a different dtype", description: "Cast a column in a lazy dataframe to a different dtype",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr into-lazy | dfr cast u8 a | dfr schema", example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr into-lazy | dfr cast u8 a | dfr schema",
result: Some(Value::record( result: Some(Value::record(
Record::from_raw_cols_vals_unchecked( record! {
vec!["a".to_string(), "b".to_string()], "a" => Value::string("u8", Span::test_data()),
vec![ "b" => Value::string("i64", Span::test_data()),
Value::string("u8", Span::test_data()), },
Value::string("i64", Span::test_data()),
],
),
Span::test_data(), Span::test_data(),
)), )),
}, },

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value, record, Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -33,13 +33,10 @@ impl Command for SchemaDF {
description: "Dataframe schema", description: "Dataframe schema",
example: r#"[[a b]; [1 "foo"] [3 "bar"]] | dfr into-df | dfr schema"#, example: r#"[[a b]; [1 "foo"] [3 "bar"]] | dfr into-df | dfr schema"#,
result: Some(Value::record( result: Some(Value::record(
Record::from_raw_cols_vals_unchecked( record! {
vec!["a".to_string(), "b".to_string()], "a" => Value::string("i64", Span::test_data()),
vec![ "b" => Value::string("str", Span::test_data()),
Value::string("i64", Span::test_data()), },
Value::string("str", Span::test_data()),
],
),
Span::test_data(), Span::test_data(),
)), )),
}] }]
@ -98,10 +95,11 @@ fn datatype_list(span: Span) -> Value {
] ]
.iter() .iter()
.map(|(dtype, note)| { .map(|(dtype, note)| {
Value::record(Record::from_raw_cols_vals_unchecked( Value::record(record! {
vec!["dtype".to_string(), "note".to_string()], "dtype" => Value::string(*dtype, span),
vec![Value::string(*dtype, span), Value::string(*note, span)], "note" => Value::string(*note, span),
),span) },
span)
}) })
.collect(); .collect();
Value::list(types, span) Value::list(types, span)

View File

@ -1033,15 +1033,14 @@ fn series_to_values(
Either::Right(it) Either::Right(it)
} }
.map(|any_values| { .map(|any_values| {
let vals: Result<Vec<Value>, ShellError> = any_values let record = polar_fields
.iter() .iter()
.map(|v| any_value_to_value(v, span)) .zip(any_values)
.collect(); .map(|(field, val)| {
let cols: Vec<String> = polar_fields any_value_to_value(val, span).map(|val| (field.name.to_string(), val))
.iter() })
.map(|field| field.name.to_string()) .collect::<Result<_, _>>()?;
.collect();
let record = Record::from_raw_cols_vals_unchecked(cols, vals?);
Ok(Value::record(record, span)) Ok(Value::record(record, span))
}) })
.collect(); .collect();
@ -1138,20 +1137,16 @@ fn any_value_to_value(any_value: &AnyValue, span: Span) -> Result<Value, ShellEr
any_value_to_value(&static_value, span) any_value_to_value(&static_value, span)
} }
AnyValue::StructOwned(struct_tuple) => { AnyValue::StructOwned(struct_tuple) => {
let values: Result<Vec<Value>, ShellError> = struct_tuple let record = struct_tuple
.0
.iter()
.map(|s| any_value_to_value(s, span))
.collect();
let fields = struct_tuple
.1 .1
.iter() .iter()
.map(|f| f.name().to_string()) .zip(&struct_tuple.0)
.collect(); .map(|(field, val)| {
Ok(Value::Record { any_value_to_value(val, span).map(|val| (field.name.to_string(), val))
val: Record::from_raw_cols_vals_unchecked(fields, values?),
internal_span: span,
}) })
.collect::<Result<_, _>>()?;
Ok(Value::record(record, span))
} }
AnyValue::StringOwned(s) => Ok(Value::string(s.to_string(), span)), AnyValue::StringOwned(s) => Ok(Value::string(s.to_string(), span)),
AnyValue::Binary(bytes) => Ok(Value::binary(*bytes, span)), AnyValue::Binary(bytes) => Ok(Value::binary(*bytes, span)),

View File

@ -154,15 +154,13 @@ impl NuDataFrame {
match value { match value {
Value::CustomValue { .. } => return Self::try_from_value(value), Value::CustomValue { .. } => return Self::try_from_value(value),
Value::List { vals, .. } => { Value::List { vals, .. } => {
let cols = (0..vals.len()) let record = vals
.map(|i| format!("{i}")) .into_iter()
.collect::<Vec<String>>(); .enumerate()
.map(|(i, val)| (format!("{i}"), val))
.collect();
conversion::insert_record( conversion::insert_record(&mut column_values, record, &maybe_schema)?
&mut column_values,
Record::from_raw_cols_vals_unchecked(cols, vals),
&maybe_schema,
)?
} }
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
conversion::insert_record(&mut column_values, record, &maybe_schema)? conversion::insert_record(&mut column_values, record, &maybe_schema)?

View File

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use nu_protocol::{Record, ShellError, Span, Value}; use nu_protocol::{ShellError, Span, Value};
use polars::prelude::{DataType, Field, Schema, SchemaRef, TimeUnit}; use polars::prelude::{DataType, Field, Schema, SchemaRef, TimeUnit};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -37,15 +37,14 @@ impl From<NuSchema> for SchemaRef {
} }
fn fields_to_value(fields: impl Iterator<Item = Field>, span: Span) -> Value { fn fields_to_value(fields: impl Iterator<Item = Field>, span: Span) -> Value {
let (cols, vals) = fields let record = fields
.map(|field| { .map(|field| {
let val = dtype_to_value(field.data_type(), span);
let col = field.name().to_string(); let col = field.name().to_string();
let val = dtype_to_value(field.data_type(), span);
(col, val) (col, val)
}) })
.unzip(); .collect();
let record = Record::from_raw_cols_vals_unchecked(cols, vals);
Value::record(record, Span::unknown()) Value::record(record, Span::unknown())
} }
@ -188,42 +187,23 @@ fn str_to_time_unit(ts_string: &str, span: Span) -> Result<TimeUnit, ShellError>
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use nu_protocol::record;
use super::*; use super::*;
#[test] #[test]
fn test_value_to_schema() { fn test_value_to_schema() {
let value = Value::Record { let address = record! {
val: Record::from_raw_cols_vals_unchecked( "street" => Value::test_string("str"),
vec!["name".into(), "age".into(), "address".into()], "city" => Value::test_string("str"),
vec![
Value::String {
val: "str".into(),
internal_span: Span::test_data(),
},
Value::String {
val: "i32".into(),
internal_span: Span::test_data(),
},
Value::Record {
val: Record::from_raw_cols_vals_unchecked(
vec!["street".into(), "city".into()],
vec![
Value::String {
val: "str".into(),
internal_span: Span::test_data(),
},
Value::String {
val: "str".into(),
internal_span: Span::test_data(),
},
],
),
internal_span: Span::test_data(),
},
],
),
internal_span: Span::test_data(),
}; };
let value = Value::test_record(record! {
"name" => Value::test_string("str"),
"age" => Value::test_string("i32"),
"address" => Value::test_record(address)
});
let schema = value_to_schema(&value, Span::unknown()).unwrap(); let schema = value_to_schema(&value, Span::unknown()).unwrap();
let expected = Schema::from_iter(vec![ let expected = Schema::from_iter(vec![
Field::new("name", DataType::String), Field::new("name", DataType::String),

View File

@ -4,7 +4,7 @@ mod roll_left;
mod roll_right; mod roll_right;
mod roll_up; mod roll_up;
use nu_protocol::{Record, ShellError, Value}; use nu_protocol::{ShellError, Value};
pub use roll_::Roll; pub use roll_::Roll;
pub use roll_down::RollDown; pub use roll_down::RollDown;
pub use roll_left::RollLeft; pub use roll_left::RollLeft;
@ -70,10 +70,8 @@ fn horizontal_rotate_value(
HorizontalDirection::Left => vals.rotate_left(rotations), HorizontalDirection::Left => vals.rotate_left(rotations),
} }
Ok(Value::record( let record = cols.into_iter().zip(vals).collect();
Record::from_raw_cols_vals_unchecked(cols, vals), Ok(Value::record(record, span))
span,
))
} }
Value::List { vals, .. } => { Value::List { vals, .. } => {
let values = vals let values = vals

View File

@ -4,7 +4,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
record, Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span, record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
Spanned, SyntaxShape, Type, Value, Spanned, SyntaxShape, Type, Value,
}; };
use std::collections::HashMap; use std::collections::HashMap;
@ -239,13 +239,6 @@ fn histogram_impl(
} }
let mut result = vec![]; let mut result = vec![];
let result_cols = vec![
value_column_name.to_string(),
"count".to_string(),
"quantile".to_string(),
"percentage".to_string(),
freq_column.to_string(),
];
const MAX_FREQ_COUNT: f64 = 100.0; const MAX_FREQ_COUNT: f64 = 100.0;
for (val, count) in counter.into_iter().sorted() { for (val, count) in counter.into_iter().sorted() {
let quantile = match calc_method { let quantile = match calc_method {
@ -259,16 +252,13 @@ fn histogram_impl(
result.push(( result.push((
count, // attach count first for easily sorting. count, // attach count first for easily sorting.
Value::record( Value::record(
Record::from_raw_cols_vals_unchecked( record! {
result_cols.clone(), value_column_name => val.into_value(),
vec![ "count" => Value::int(count, span),
val.into_value(), "quantile" => Value::float(quantile, span),
Value::int(count, span), "percentage" => Value::string(percentage, span),
Value::float(quantile, span), freq_column => Value::string(freq, span),
Value::string(percentage, span), },
Value::string(freq, span),
],
),
span, span,
), ),
)); ));

View File

@ -441,15 +441,15 @@ fn prepared_statement_to_nu_list(
) -> Result<Value, SqliteError> { ) -> Result<Value, SqliteError> {
let column_names = stmt let column_names = stmt
.column_names() .column_names()
.iter() .into_iter()
.map(|c| c.to_string()) .map(String::from)
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let row_results = stmt.query_map([], |row| { let row_results = stmt.query_map([], |row| {
Ok(convert_sqlite_row_to_nu_value( Ok(convert_sqlite_row_to_nu_value(
row, row,
call_span, call_span,
column_names.clone(), &column_names,
)) ))
})?; })?;
@ -491,18 +491,19 @@ fn read_entire_sqlite_db(
Ok(Value::record(tables, call_span)) Ok(Value::record(tables, call_span))
} }
pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, column_names: Vec<String>) -> Value { pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, column_names: &[String]) -> Value {
let mut vals = Vec::with_capacity(column_names.len()); let record = column_names
.iter()
for i in 0..column_names.len() { .enumerate()
let val = convert_sqlite_value_to_nu_value(row.get_ref_unwrap(i), span); .map(|(i, col)| {
vals.push(val); (
} col.clone(),
convert_sqlite_value_to_nu_value(row.get_ref_unwrap(i), span),
Value::record(
Record::from_raw_cols_vals_unchecked(column_names, vals),
span,
) )
})
.collect();
Value::record(record, span)
} }
pub fn convert_sqlite_value_to_nu_value(value: ValueRef, span: Span) -> Value { pub fn convert_sqlite_value_to_nu_value(value: ValueRef, span: Span) -> Value {

View File

@ -1,5 +1,5 @@
use csv::{ReaderBuilder, Trim}; use csv::{ReaderBuilder, Trim};
use nu_protocol::{IntoPipelineData, PipelineData, Record, ShellError, Span, Value}; use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Value};
fn from_delimited_string_to_value( fn from_delimited_string_to_value(
DelimitedReaderConfig { DelimitedReaderConfig {
@ -36,28 +36,28 @@ fn from_delimited_string_to_value(
let mut rows = vec![]; let mut rows = vec![];
for row in reader.records() { for row in reader.records() {
let row = row?; let row = row?;
let output_row = (0..headers.len()) let columns = headers.iter().cloned();
.map(|i| { let values = row
row.get(i) .into_iter()
.map(|value| { .map(|s| {
if no_infer { if no_infer {
Value::string(value.to_string(), span) Value::string(s, span)
} else if let Ok(i) = value.parse::<i64>() { } else if let Ok(i) = s.parse() {
Value::int(i, span) Value::int(i, span)
} else if let Ok(f) = value.parse::<f64>() { } else if let Ok(f) = s.parse() {
Value::float(f, span) Value::float(f, span)
} else { } else {
Value::string(value.to_string(), span) Value::string(s, span)
} }
}) })
.unwrap_or(Value::nothing(span)) .chain(std::iter::repeat(Value::nothing(span)));
})
.collect::<Vec<Value>>();
rows.push(Value::record( // If there are more values than the number of headers,
Record::from_raw_cols_vals_unchecked(headers.clone(), output_row), // then the remaining values are ignored.
span, //
)); // Otherwise, if there are less values than headers,
// then `Value::nothing(span)` is used to fill the remaining columns.
rows.push(Value::record(columns.zip(values).collect(), span));
} }
Ok(Value::list(rows, span)) Ok(Value::list(rows, span))

View File

@ -421,15 +421,16 @@ fn convert_to_value(
span: expr.span, span: expr.span,
}); });
} }
let vals: Vec<Value> = row
.into_iter() let record = cols
.map(|cell| convert_to_value(cell, span, original_text)) .iter()
.zip(row)
.map(|(col, cell)| {
convert_to_value(cell, span, original_text).map(|val| (col.clone(), val))
})
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
output.push(Value::record( output.push(Value::record(record, span));
Record::from_raw_cols_vals_unchecked(cols.clone(), vals),
span,
));
} }
Ok(Value::list(output, span)) Ok(Value::list(output, span))

View File

@ -136,13 +136,11 @@ fn detect_columns(
.map(move |x| { .map(move |x| {
let row = find_columns(&x); let row = find_columns(&x);
let mut cols = vec![]; let mut record = Record::new();
let mut vals = vec![];
if headers.len() == row.len() { if headers.len() == row.len() {
for (header, val) in headers.iter().zip(row.iter()) { for (header, val) in headers.iter().zip(row.iter()) {
cols.push(header.item.clone()); record.push(&header.item, Value::string(&val.item, name_span));
vals.push(Value::string(val.item.clone(), name_span));
} }
} else { } else {
let mut pre_output = vec![]; let mut pre_output = vec![];
@ -176,8 +174,7 @@ fn detect_columns(
for header in &headers { for header in &headers {
for pre_o in &pre_output { for pre_o in &pre_output {
if pre_o.0 == header.item { if pre_o.0 == header.item {
cols.push(header.item.clone()); record.push(&header.item, pre_o.1.clone());
vals.push(pre_o.1.clone())
} }
} }
} }
@ -187,25 +184,25 @@ fn detect_columns(
match nu_cmd_base::util::process_range(range) { match nu_cmd_base::util::process_range(range) {
Ok((l_idx, r_idx)) => { Ok((l_idx, r_idx)) => {
let l_idx = if l_idx < 0 { let l_idx = if l_idx < 0 {
cols.len() as isize + l_idx record.len() as isize + l_idx
} else { } else {
l_idx l_idx
}; };
let r_idx = if r_idx < 0 { let r_idx = if r_idx < 0 {
cols.len() as isize + r_idx record.len() as isize + r_idx
} else { } else {
r_idx r_idx
}; };
if !(l_idx <= r_idx && (r_idx >= 0 || l_idx < (cols.len() as isize))) { if !(l_idx <= r_idx && (r_idx >= 0 || l_idx < (record.len() as isize))) {
return Value::record( return Value::record(record, name_span);
Record::from_raw_cols_vals_unchecked(cols, vals),
name_span,
);
} }
(l_idx.max(0) as usize, (r_idx as usize + 1).min(cols.len())) (
l_idx.max(0) as usize,
(r_idx as usize + 1).min(record.len()),
)
} }
Err(processing_error) => { Err(processing_error) => {
let err = processing_error("could not find range index", name_span); let err = processing_error("could not find range index", name_span);
@ -213,9 +210,11 @@ fn detect_columns(
} }
} }
} else { } else {
return Value::record(Record::from_raw_cols_vals_unchecked(cols, vals), name_span); return Value::record(record, name_span);
}; };
let (mut cols, mut vals): (Vec<_>, Vec<_>) = record.into_iter().unzip();
// Merge Columns // Merge Columns
((start_index + 1)..(cols.len() - end_index + start_index + 1)).for_each(|idx| { ((start_index + 1)..(cols.len() - end_index + start_index + 1)).for_each(|idx| {
cols.swap(idx, end_index - start_index - 1 + idx); cols.swap(idx, end_index - start_index - 1 + idx);
@ -233,9 +232,12 @@ fn detect_columns(
let last_seg = vals.split_off(end_index); let last_seg = vals.split_off(end_index);
vals.truncate(start_index); vals.truncate(start_index);
vals.push(binding); vals.push(binding);
last_seg.into_iter().for_each(|v| vals.push(v)); vals.extend(last_seg);
Value::record(Record::from_raw_cols_vals_unchecked(cols, vals), name_span) match Record::from_raw_cols_vals(cols, vals, Span::unknown(), name_span) {
Ok(record) => Value::record(record, name_span),
Err(err) => Value::error(err, name_span),
}
}) })
.into_pipeline_data(ctrlc)) .into_pipeline_data(ctrlc))
} else { } else {

View File

@ -1,7 +1,7 @@
use std::{io::Write, path::PathBuf}; use std::{io::Write, path::PathBuf};
use chrono::{DateTime, FixedOffset, NaiveDateTime, Offset}; use chrono::{DateTime, FixedOffset, NaiveDateTime, Offset};
use nu_protocol::{ast::PathMember, Record, Span, Value}; use nu_protocol::{ast::PathMember, record, Span, Value};
use nu_test_support::{ use nu_test_support::{
fs::{line_ending, Stub}, fs::{line_ending, Stub},
nu, pipeline, nu, pipeline,
@ -234,22 +234,16 @@ impl TestRow {
impl From<TestRow> for Value { impl From<TestRow> for Value {
fn from(row: TestRow) -> Self { fn from(row: TestRow) -> Self {
Value::record( Value::record(
Record::from_iter(vec![ record! {
("somebool".into(), Value::bool(row.0, Span::unknown())), "somebool" => Value::bool(row.0, Span::unknown()),
("someint".into(), Value::int(row.1, Span::unknown())), "someint" => Value::int(row.1, Span::unknown()),
("somefloat".into(), Value::float(row.2, Span::unknown())), "somefloat" => Value::float(row.2, Span::unknown()),
( "somefilesize" => Value::filesize(row.3, Span::unknown()),
"somefilesize".into(), "someduration" => Value::duration(row.4, Span::unknown()),
Value::filesize(row.3, Span::unknown()), "somedate" => Value::date(row.5, Span::unknown()),
), "somestring" => Value::string(row.6, Span::unknown()),
( "somebinary" => Value::binary(row.7, Span::unknown()),
"someduration".into(), },
Value::duration(row.4, Span::unknown()),
),
("somedate".into(), Value::date(row.5, Span::unknown())),
("somestring".into(), Value::string(row.6, Span::unknown())),
("somebinary".into(), Value::binary(row.7, Span::unknown())),
]),
Span::unknown(), Span::unknown(),
) )
} }

View File

@ -1,7 +1,7 @@
use nu_protocol::{ use nu_protocol::{
ast::Expr, ast::Expr,
engine::{Command, EngineState, Stack, Visibility}, engine::{Command, EngineState, Stack, Visibility},
record, ModuleId, Record, Signature, Span, SyntaxShape, Type, Value, record, ModuleId, Signature, Span, SyntaxShape, Type, Value,
}; };
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
@ -185,101 +185,81 @@ impl<'e, 's> ScopeData<'e, 's> {
) -> Vec<Value> { ) -> Vec<Value> {
let mut sig_records = vec![]; let mut sig_records = vec![];
let sig_cols = vec![
"parameter_name".to_string(),
"parameter_type".to_string(),
"syntax_shape".to_string(),
"is_optional".to_string(),
"short_flag".to_string(),
"description".to_string(),
"custom_completion".to_string(),
"parameter_default".to_string(),
];
// input // input
sig_records.push(Value::record( sig_records.push(Value::record(
Record::from_raw_cols_vals_unchecked( record! {
sig_cols.clone(), "parameter_name" => Value::nothing(span),
vec![ "parameter_type" => Value::string("input", span),
Value::nothing(span), "syntax_shape" => Value::string(input_type.to_shape().to_string(), span),
Value::string("input", span), "is_optional" => Value::bool(false, span),
Value::string(input_type.to_shape().to_string(), span), "short_flag" => Value::nothing(span),
Value::bool(false, span), "description" => Value::nothing(span),
Value::nothing(span), "custom_completion" => Value::nothing(span),
Value::nothing(span), "parameter_default" => Value::nothing(span),
Value::nothing(span), },
Value::nothing(span),
],
),
span, span,
)); ));
// required_positional // required_positional
for req in &signature.required_positional { for req in &signature.required_positional {
let sig_vals = vec![ let custom = extract_custom_completion_from_arg(self.engine_state, &req.shape);
Value::string(&req.name, span),
Value::string("positional", span),
Value::string(req.shape.to_string(), span),
Value::bool(false, span),
Value::nothing(span),
Value::string(&req.desc, span),
Value::string(
extract_custom_completion_from_arg(self.engine_state, &req.shape),
span,
),
Value::nothing(span),
];
sig_records.push(Value::record( sig_records.push(Value::record(
Record::from_raw_cols_vals_unchecked(sig_cols.clone(), sig_vals), record! {
"parameter_name" => Value::string(&req.name, span),
"parameter_type" => Value::string("positional", span),
"syntax_shape" => Value::string(req.shape.to_string(), span),
"is_optional" => Value::bool(false, span),
"short_flag" => Value::nothing(span),
"description" => Value::string(&req.desc, span),
"custom_completion" => Value::string(custom, span),
"parameter_default" => Value::nothing(span),
},
span, span,
)); ));
} }
// optional_positional // optional_positional
for opt in &signature.optional_positional { for opt in &signature.optional_positional {
let sig_vals = vec![ let custom = extract_custom_completion_from_arg(self.engine_state, &opt.shape);
Value::string(&opt.name, span), let default = if let Some(val) = &opt.default_value {
Value::string("positional", span),
Value::string(opt.shape.to_string(), span),
Value::bool(true, span),
Value::nothing(span),
Value::string(&opt.desc, span),
Value::string(
extract_custom_completion_from_arg(self.engine_state, &opt.shape),
span,
),
if let Some(val) = &opt.default_value {
val.clone() val.clone()
} else { } else {
Value::nothing(span) Value::nothing(span)
}, };
];
sig_records.push(Value::record( sig_records.push(Value::record(
Record::from_raw_cols_vals_unchecked(sig_cols.clone(), sig_vals), record! {
"parameter_name" => Value::string(&opt.name, span),
"parameter_type" => Value::string("positional", span),
"syntax_shape" => Value::string(opt.shape.to_string(), span),
"is_optional" => Value::bool(true, span),
"short_flag" => Value::nothing(span),
"description" => Value::string(&opt.desc, span),
"custom_completion" => Value::string(custom, span),
"parameter_default" => default,
},
span, span,
)); ));
} }
// rest_positional // rest_positional
if let Some(rest) = &signature.rest_positional { if let Some(rest) = &signature.rest_positional {
let sig_vals = vec![ let name = if rest.name == "rest" { "" } else { &rest.name };
Value::string(if rest.name == "rest" { "" } else { &rest.name }, span), let custom = extract_custom_completion_from_arg(self.engine_state, &rest.shape);
Value::string("rest", span),
Value::string(rest.shape.to_string(), span),
Value::bool(true, span),
Value::nothing(span),
Value::string(&rest.desc, span),
Value::string(
extract_custom_completion_from_arg(self.engine_state, &rest.shape),
span,
),
Value::nothing(span), // rest_positional does have default, but parser prohibits specifying it?!
];
sig_records.push(Value::record( sig_records.push(Value::record(
Record::from_raw_cols_vals_unchecked(sig_cols.clone(), sig_vals), record! {
"parameter_name" => Value::string(name, span),
"parameter_type" => Value::string("rest", span),
"syntax_shape" => Value::string(rest.shape.to_string(), span),
"is_optional" => Value::bool(true, span),
"short_flag" => Value::nothing(span),
"description" => Value::string(&rest.desc, span),
"custom_completion" => Value::string(custom, span),
// rest_positional does have default, but parser prohibits specifying it?!
"parameter_default" => Value::nothing(span),
},
span, span,
)); ));
} }
@ -310,42 +290,39 @@ impl<'e, 's> ScopeData<'e, 's> {
Value::nothing(span) Value::nothing(span)
}; };
let sig_vals = vec![ let default = if let Some(val) = &named.default_value {
Value::string(&named.long, span),
flag_type,
shape,
Value::bool(!named.required, span),
short_flag,
Value::string(&named.desc, span),
Value::string(custom_completion_command_name, span),
if let Some(val) = &named.default_value {
val.clone() val.clone()
} else { } else {
Value::nothing(span) Value::nothing(span)
}, };
];
sig_records.push(Value::record( sig_records.push(Value::record(
Record::from_raw_cols_vals_unchecked(sig_cols.clone(), sig_vals), record! {
"parameter_name" => Value::string(&named.long, span),
"parameter_type" => flag_type,
"syntax_shape" => shape,
"is_optional" => Value::bool(!named.required, span),
"short_flag" => short_flag,
"description" => Value::string(&named.desc, span),
"custom_completion" => Value::string(custom_completion_command_name, span),
"parameter_default" => default,
},
span, span,
)); ));
} }
// output // output
sig_records.push(Value::record( sig_records.push(Value::record(
Record::from_raw_cols_vals_unchecked( record! {
sig_cols, "parameter_name" => Value::nothing(span),
vec![ "parameter_type" => Value::string("output", span),
Value::nothing(span), "syntax_shape" => Value::string(output_type.to_shape().to_string(), span),
Value::string("output", span), "is_optional" => Value::bool(false, span),
Value::string(output_type.to_shape().to_string(), span), "short_flag" => Value::nothing(span),
Value::bool(false, span), "description" => Value::nothing(span),
Value::nothing(span), "custom_completion" => Value::nothing(span),
Value::nothing(span), "parameter_default" => Value::nothing(span),
Value::nothing(span), },
Value::nothing(span),
],
),
span, span,
)); ));

View File

@ -4,7 +4,7 @@ use std::io::{self, Result};
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
record, Record, Value, record, Value,
}; };
use ratatui::layout::Rect; use ratatui::layout::Rect;
@ -165,10 +165,7 @@ fn help_frame_data(
let (cols, mut vals) = help_manual_data(manual, aliases); let (cols, mut vals) = help_manual_data(manual, aliases);
let vals = vals.remove(0); let vals = vals.remove(0);
Value::record( Value::record(cols.into_iter().zip(vals).collect(), NuSpan::unknown())
Record::from_raw_cols_vals_unchecked(cols, vals),
NuSpan::unknown(),
)
}) })
.collect(); .collect();
let commands = Value::list(commands, NuSpan::unknown()); let commands = Value::list(commands, NuSpan::unknown());

View File

@ -701,16 +701,13 @@ fn build_last_value(v: &RecordView) -> Value {
fn build_table_as_list(v: &RecordView) -> Value { fn build_table_as_list(v: &RecordView) -> Value {
let layer = v.get_layer_last(); let layer = v.get_layer_last();
let headers = layer.columns.to_vec(); let cols = &layer.columns;
let vals = layer let vals = layer
.records .records
.iter() .iter()
.cloned()
.map(|vals| { .map(|vals| {
Value::record( let record = cols.iter().cloned().zip(vals.iter().cloned()).collect();
Record::from_raw_cols_vals_unchecked(headers.clone(), vals), Value::record(record, NuSpan::unknown())
NuSpan::unknown(),
)
}) })
.collect(); .collect();
@ -720,13 +717,18 @@ fn build_table_as_list(v: &RecordView) -> Value {
fn build_table_as_record(v: &RecordView) -> Value { fn build_table_as_record(v: &RecordView) -> Value {
let layer = v.get_layer_last(); let layer = v.get_layer_last();
let cols = layer.columns.to_vec(); let record = if let Some(row) = layer.records.first() {
let vals = layer.records.first().map_or(Vec::new(), |row| row.clone()); layer
.columns
.iter()
.cloned()
.zip(row.iter().cloned())
.collect()
} else {
Record::new()
};
Value::record( Value::record(record, NuSpan::unknown())
Record::from_raw_cols_vals_unchecked(cols, vals),
NuSpan::unknown(),
)
} }
fn report_cursor_position(mode: UIMode, cursor: XYCursor) -> String { fn report_cursor_position(mode: UIMode, cursor: XYCursor) -> String {

View File

@ -119,13 +119,12 @@ pub trait Eval {
let mut output_rows = vec![]; let mut output_rows = vec![];
for val in vals { for val in vals {
let mut row = vec![]; let record = output_headers.iter().zip(val).map(|(col, expr)| {
for expr in val { Self::eval(state, mut_state, expr).map(|val| (col.clone(), val))
row.push(Self::eval(state, mut_state, expr)?); }).collect::<Result<_,_>>()?;
}
// length equality already ensured in parser
output_rows.push(Value::record( output_rows.push(Value::record(
Record::from_raw_cols_vals_unchecked(output_headers.clone(), row), record,
expr.span, expr.span,
)); ));
} }

View File

@ -26,24 +26,11 @@ impl Record {
} }
} }
/// Constructor that checks that `cols` and `vals` are of the same length. /// Create a [`Record`] from a `Vec` of columns and a `Vec` of [`Value`]s
/// ///
/// WARNING! Panics with assertion failure if cols and vals have different length! /// Returns an error if `cols` and `vals` have different lengths.
/// Should be used only when the same lengths are guaranteed!
/// ///
/// For perf reasons does not validate the rest of the record assumptions. /// For perf reasons, this will not validate the rest of the record assumptions:
/// - unique keys
pub fn from_raw_cols_vals_unchecked(cols: Vec<String>, vals: Vec<Value>) -> Self {
assert_eq!(cols.len(), vals.len());
Self { cols, vals }
}
/// Constructor that checks that `cols` and `vals` are of the same length.
///
/// Returns None if cols and vals have different length.
///
/// For perf reasons does not validate the rest of the record assumptions.
/// - unique keys /// - unique keys
pub fn from_raw_cols_vals( pub fn from_raw_cols_vals(
cols: Vec<String>, cols: Vec<String>,
@ -70,11 +57,13 @@ impl Record {
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.cols.is_empty() || self.vals.is_empty() debug_assert_eq!(self.cols.len(), self.vals.len());
self.cols.is_empty()
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
usize::min(self.cols.len(), self.vals.len()) debug_assert_eq!(self.cols.len(), self.vals.len());
self.cols.len()
} }
/// Naive push to the end of the datastructure. /// Naive push to the end of the datastructure.
@ -99,14 +88,13 @@ impl Record {
let curr_val = &mut self.vals[idx]; let curr_val = &mut self.vals[idx];
Some(std::mem::replace(curr_val, val)) Some(std::mem::replace(curr_val, val))
} else { } else {
self.cols.push(col.into()); self.push(col, val);
self.vals.push(val);
None None
} }
} }
pub fn contains(&self, col: impl AsRef<str>) -> bool { pub fn contains(&self, col: impl AsRef<str>) -> bool {
self.cols.iter().any(|k| k == col.as_ref()) self.columns().any(|k| k == col.as_ref())
} }
pub fn index_of(&self, col: impl AsRef<str>) -> Option<usize> { pub fn index_of(&self, col: impl AsRef<str>) -> Option<usize> {
@ -138,7 +126,6 @@ impl Record {
/// Remove elements in-place that do not satisfy `keep` /// Remove elements in-place that do not satisfy `keep`
/// ///
/// Note: Panics if `vals.len() > cols.len()`
/// ```rust /// ```rust
/// use nu_protocol::{record, Value}; /// use nu_protocol::{record, Value};
/// ///
@ -165,7 +152,6 @@ impl Record {
/// ///
/// This can for example be used to recursively prune nested records. /// This can for example be used to recursively prune nested records.
/// ///
/// Note: Panics if `vals.len() > cols.len()`
/// ```rust /// ```rust
/// use nu_protocol::{record, Record, Value}; /// use nu_protocol::{record, Record, Value};
/// ///
@ -235,6 +221,7 @@ impl Record {
/// Truncate record to the first `len` elements. /// Truncate record to the first `len` elements.
/// ///
/// `len > self.len()` will be ignored /// `len > self.len()` will be ignored
///
/// ```rust /// ```rust
/// use nu_protocol::{record, Value}; /// use nu_protocol::{record, Value};
/// ///
@ -299,11 +286,7 @@ impl Record {
where where
R: RangeBounds<usize> + Clone, R: RangeBounds<usize> + Clone,
{ {
assert_eq!( debug_assert_eq!(self.cols.len(), self.vals.len());
self.cols.len(),
self.vals.len(),
"Length of cols and vals must be equal for sane `Record::drain`"
);
Drain { Drain {
keys: self.cols.drain(range.clone()), keys: self.cols.drain(range.clone()),
values: self.vals.drain(range), values: self.vals.drain(range),
@ -323,8 +306,7 @@ impl Extend<(String, Value)> for Record {
fn extend<T: IntoIterator<Item = (String, Value)>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = (String, Value)>>(&mut self, iter: T) {
for (k, v) in iter { for (k, v) in iter {
// TODO: should this .insert with a check? // TODO: should this .insert with a check?
self.cols.push(k); self.push(k, v)
self.vals.push(v);
} }
} }
} }
@ -552,11 +534,15 @@ impl ExactSizeIterator for Drain<'_> {
#[macro_export] #[macro_export]
macro_rules! record { macro_rules! record {
// The macro only compiles if the number of columns equals the number of values,
// so it's safe to call `unwrap` below.
{$($col:expr => $val:expr),+ $(,)?} => { {$($col:expr => $val:expr),+ $(,)?} => {
$crate::Record::from_raw_cols_vals_unchecked( $crate::Record::from_raw_cols_vals(
::std::vec![$($col.into(),)+], ::std::vec![$($col.into(),)+],
::std::vec![$($val,)+] ::std::vec![$($val,)+],
) $crate::Span::unknown(),
$crate::Span::unknown(),
).unwrap()
}; };
{} => { {} => {
$crate::Record::new() $crate::Record::new()

View File

@ -1,5 +1,5 @@
use nu_plugin::{EvaluatedCall, LabeledError}; use nu_plugin::{EvaluatedCall, LabeledError};
use nu_protocol::{Record, Value}; use nu_protocol::{record, Value};
pub struct Example; pub struct Example;
impl Example { impl Example {
@ -75,20 +75,16 @@ impl Example {
pub fn test2(&self, call: &EvaluatedCall, input: &Value) -> Result<Value, LabeledError> { pub fn test2(&self, call: &EvaluatedCall, input: &Value) -> Result<Value, LabeledError> {
self.print_values(2, call, input)?; self.print_values(2, call, input)?;
let cols = vec!["one".to_string(), "two".to_string(), "three".to_string()];
let vals = (0..10i64) let vals = (0..10i64)
.map(|i| { .map(|i| {
let vals = (0..3) let record = record! {
.map(|v| Value::int(v * i, call.head)) "one" => Value::int(i, call.head),
.collect::<Vec<Value>>(); "two" => Value::int(2 * i, call.head),
"three" => Value::int(3 * i, call.head),
Value::record( };
Record::from_raw_cols_vals_unchecked(cols.clone(), vals), Value::record(record, call.head)
call.head,
)
}) })
.collect::<Vec<Value>>(); .collect();
Ok(Value::list(vals, call.head)) Ok(Value::list(vals, call.head))
} }