This commit is contained in:
Ian Manske 2025-03-09 17:12:03 -07:00 committed by GitHub
commit 25cd4c1713
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 181 additions and 196 deletions

10
Cargo.lock generated
View File

@ -1639,6 +1639,15 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
[[package]]
name = "ecow"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54bfbb1708988623190a6c4dbedaeaf0f53c20c6395abd6a01feb327b3146f4b"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "ego-tree" name = "ego-tree"
version = "0.10.0" version = "0.10.0"
@ -3956,6 +3965,7 @@ dependencies = [
"chrono-humanize", "chrono-humanize",
"dirs", "dirs",
"dirs-sys", "dirs-sys",
"ecow",
"fancy-regex", "fancy-regex",
"heck", "heck",
"indexmap", "indexmap",

View File

@ -170,7 +170,7 @@ fn error_from_reedline(e: ReedlineError) -> ShellError {
fn item_from_value(v: Value) -> Result<HistoryItem, ShellError> { fn item_from_value(v: Value) -> Result<HistoryItem, ShellError> {
let span = v.span(); let span = v.span();
match v { match v {
Value::Record { val, .. } => item_from_record(val.into_owned(), span), Value::Record { val, .. } => item_from_record(val, span),
Value::String { val, .. } => Ok(HistoryItem { Value::String { val, .. } => Ok(HistoryItem {
command_line: val, command_line: val,
id: None, id: None,

View File

@ -58,7 +58,7 @@ fn horizontal_rotate_value(
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
let rotations = by.map(|n| n % record.len()).unwrap_or(1); let rotations = by.map(|n| n % record.len()).unwrap_or(1);
let (mut cols, mut vals): (Vec<_>, Vec<_>) = record.into_owned().into_iter().unzip(); let (mut cols, mut vals): (Vec<_>, Vec<_>) = record.into_iter().unzip();
if !cells_only { if !cells_only {
match direction { match direction {
HorizontalDirection::Right => cols.rotate_right(rotations), HorizontalDirection::Right => cols.rotate_right(rotations),

View File

@ -174,7 +174,7 @@ pub fn rotate(
let span = val.span(); let span = val.span();
match val { match val {
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
let (cols, vals): (Vec<_>, Vec<_>) = record.into_owned().into_iter().unzip(); let (cols, vals): (Vec<_>, Vec<_>) = record.into_iter().unzip();
old_column_names = cols; old_column_names = cols;
new_values.extend_from_slice(&vals); new_values.extend_from_slice(&vals);
} }

View File

@ -127,7 +127,6 @@ impl Iterator for UpdateCellIterator {
let mut value = self.iter.next()?; let mut value = self.iter.next()?;
let value = if let Value::Record { val, .. } = &mut value { let value = if let Value::Record { val, .. } = &mut value {
let val = val.to_mut();
if let Some(columns) = &self.columns { if let Some(columns) = &self.columns {
for (col, val) in val.iter_mut() { for (col, val) in val.iter_mut() {
if columns.contains(col) { if columns.contains(col) {

View File

@ -266,16 +266,15 @@ fn describe_value_inner(
| Value::String { .. } | Value::String { .. }
| Value::Glob { .. } | Value::Glob { .. }
| Value::Nothing { .. } => Description::String(value.get_type().to_string()), | Value::Nothing { .. } => Description::String(value.get_type().to_string()),
Value::Record { val, .. } => { Value::Record { mut val, .. } => {
let mut columns = val.into_owned(); for (_, val) in &mut val {
for (_, val) in &mut columns {
*val = *val =
describe_value_inner(std::mem::take(val), head, engine_state).into_value(head); describe_value_inner(std::mem::take(val), head, engine_state).into_value(head);
} }
Description::Record(record! { Description::Record(record! {
"type" => Value::string("record", head), "type" => Value::string("record", head),
"columns" => Value::record(columns, head), "columns" => Value::record(val, head),
}) })
} }
Value::List { mut vals, .. } => { Value::List { mut vals, .. } => {

View File

@ -289,7 +289,7 @@ impl std::fmt::Debug for DebuggableValue<'_> {
Value::Record { val, .. } => { Value::Record { val, .. } => {
write!(f, "{{")?; write!(f, "{{")?;
let mut first = true; let mut first = true;
for (col, value) in (&**val).into_iter() { for (col, value) in val {
if !first { if !first {
write!(f, ", ")?; write!(f, ", ")?;
} }

View File

@ -134,7 +134,7 @@ fn into_record(call: &Call, input: PipelineData) -> Result<PipelineData, ShellEr
if matches!(expected_type, None | Some(ExpectedType::Record)) => if matches!(expected_type, None | Some(ExpectedType::Record)) =>
{ {
// Don't use .extend() unless that gets changed to check for duplicate keys // Don't use .extend() unless that gets changed to check for duplicate keys
for (key, val) in val.into_owned() { for (key, val) in val {
record.insert(key, val); record.insert(key, val);
} }
expected_type = Some(ExpectedType::Record); expected_type = Some(ExpectedType::Record);

View File

@ -110,8 +110,7 @@ impl Iterator for UpdateCellIterator {
let span = val.span(); let span = val.span();
match val { match val {
Value::Record { val, .. } => Some(Value::record( Value::Record { val, .. } => Some(Value::record(
val.into_owned() val.into_iter()
.into_iter()
.map(|(col, val)| match &self.columns { .map(|(col, val)| match &self.columns {
Some(cols) if !cols.contains(&col) => (col, val), Some(cols) if !cols.contains(&col) => (col, val),
_ => ( _ => (

View File

@ -465,7 +465,7 @@ pub fn nu_value_to_params(value: Value) -> Result<NuSqlParams, ShellError> {
Value::Record { val, .. } => { Value::Record { val, .. } => {
let mut params = Vec::with_capacity(val.len()); let mut params = Vec::with_capacity(val.len());
for (mut column, value) in val.into_owned().into_iter() { for (mut column, value) in val {
let sql_type_erased = value_to_sql(value)?; let sql_type_erased = value_to_sql(value)?;
if !column.starts_with([':', '@', '$']) { if !column.starts_with([':', '@', '$']) {

View File

@ -210,7 +210,7 @@ mod util {
let span = value.span(); let span = value.span();
match value { match value {
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
let (cols, vals): (Vec<_>, Vec<_>) = record.into_owned().into_iter().unzip(); let (cols, vals): (Vec<_>, Vec<_>) = record.into_iter().unzip();
( (
match cols.is_empty() { match cols.is_empty() {
true => vec![String::from("")], true => vec![String::from("")],

View File

@ -43,7 +43,7 @@ impl Command for LoadEnv {
let record = match arg { let record = match arg {
Some(record) => record, Some(record) => record,
None => match input { None => match input {
PipelineData::Value(Value::Record { val, .. }, ..) => val.into_owned(), PipelineData::Value(Value::Record { val, .. }, ..) => val,
_ => { _ => {
return Err(ShellError::UnsupportedInput { return Err(ShellError::UnsupportedInput {
msg: "'load-env' expects a single record".into(), msg: "'load-env' expects a single record".into(),

View File

@ -94,7 +94,6 @@ fn getcol(head: Span, input: PipelineData) -> Result<PipelineData, ShellError> {
.collect() .collect()
} }
Value::Record { val, .. } => val Value::Record { val, .. } => val
.into_owned()
.into_iter() .into_iter()
.map(move |(x, _)| Value::string(x, head)) .map(move |(x, _)| Value::string(x, head))
.collect(), .collect(),

View File

@ -102,7 +102,6 @@ fn default(
val: ref mut record, val: ref mut record,
.. ..
} => { } => {
let record = record.to_mut();
if let Some(val) = record.get_mut(&column.item) { if let Some(val) = record.get_mut(&column.item) {
if matches!(val, Value::Nothing { .. }) { if matches!(val, Value::Nothing { .. }) {
*val = value.clone(); *val = value.clone();

View File

@ -128,7 +128,7 @@ fn drop_cols(
.. ..
} => { } => {
let len = record.len().saturating_sub(columns); let len = record.len().saturating_sub(columns);
record.to_mut().truncate(len); record.truncate(len);
Ok(v.into_pipeline_data_with_metadata(metadata)) Ok(v.into_pipeline_data_with_metadata(metadata))
} }
// Propagate errors // Propagate errors
@ -149,7 +149,9 @@ fn drop_cols(
fn drop_cols_set(val: &mut Value, head: Span, drop: usize) -> Result<HashSet<String>, ShellError> { fn drop_cols_set(val: &mut Value, head: Span, drop: usize) -> Result<HashSet<String>, ShellError> {
if let Value::Record { val: record, .. } = val { if let Value::Record { val: record, .. } = val {
let len = record.len().saturating_sub(drop); let len = record.len().saturating_sub(drop);
Ok(record.to_mut().drain(len..).map(|(col, _)| col).collect()) let set = record.columns().skip(len).cloned().collect();
record.truncate(len);
Ok(set)
} else { } else {
Err(unsupported_value_error(val, head)) Err(unsupported_value_error(val, head))
} }
@ -161,7 +163,7 @@ fn drop_record_cols(
drop_cols: &HashSet<String>, drop_cols: &HashSet<String>,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
if let Value::Record { val, .. } = val { if let Value::Record { val, .. } = val {
val.to_mut().retain(|col, _| !drop_cols.contains(col)); val.retain(|col, _| !drop_cols.contains(col));
Ok(()) Ok(())
} else { } else {
Err(unsupported_value_error(val, head)) Err(unsupported_value_error(val, head))

View File

@ -156,7 +156,7 @@ fn flat_value(columns: &[CellPath], item: Value, all: bool) -> Vec<Value> {
let mut out = IndexMap::<String, Value>::new(); let mut out = IndexMap::<String, Value>::new();
let mut inner_table = None; let mut inner_table = None;
for (column_index, (column, value)) in val.into_owned().into_iter().enumerate() { for (column_index, (column, value)) in val.into_iter().enumerate() {
let column_requested = columns.iter().find(|c| c.to_column_name() == column); let column_requested = columns.iter().find(|c| c.to_column_name() == column);
let need_flatten = { columns.is_empty() || column_requested.is_some() }; let need_flatten = { columns.is_empty() || column_requested.is_some() };
let span = value.span(); let span = value.span();
@ -164,7 +164,7 @@ fn flat_value(columns: &[CellPath], item: Value, all: bool) -> Vec<Value> {
match value { match value {
Value::Record { ref val, .. } => { Value::Record { ref val, .. } => {
if need_flatten { if need_flatten {
for (col, val) in val.clone().into_owned() { for (col, val) in val.clone() {
if out.contains_key(&col) { if out.contains_key(&col) {
out.insert(format!("{column}_{col}"), val); out.insert(format!("{column}_{col}"), val);
} else { } else {

View File

@ -142,7 +142,6 @@ fn replace_headers(
if let Value::Record { val: record, .. } = value { if let Value::Record { val: record, .. } = value {
Ok(Value::record( Ok(Value::record(
record record
.into_owned()
.into_iter() .into_iter()
.filter_map(|(col, val)| { .filter_map(|(col, val)| {
old_headers old_headers

View File

@ -49,7 +49,6 @@ impl Command for Items {
Value::Record { val, .. } => { Value::Record { val, .. } => {
let mut closure = ClosureEval::new(engine_state, stack, closure); let mut closure = ClosureEval::new(engine_state, stack, closure);
Ok(val Ok(val
.into_owned()
.into_iter() .into_iter()
.map_while(move |(col, val)| { .map_while(move |(col, val)| {
let result = closure let result = closure

View File

@ -65,7 +65,7 @@ pub(crate) fn do_merge(
Value::Record { val: lhs, .. }, Value::Record { val: lhs, .. },
Value::Record { val: rhs, .. }, Value::Record { val: rhs, .. },
) => Ok(Value::record( ) => Ok(Value::record(
merge_records(lhs.into_owned(), rhs.into_owned(), strategy, span)?, merge_records(lhs, rhs, strategy, span)?,
span, span,
)), )),
// Deep merge records // Deep merge records
@ -74,7 +74,7 @@ pub(crate) fn do_merge(
Value::Record { val: lhs, .. }, Value::Record { val: lhs, .. },
Value::Record { val: rhs, .. }, Value::Record { val: rhs, .. },
) => Ok(Value::record( ) => Ok(Value::record(
merge_records(lhs.into_owned(), rhs.into_owned(), strategy, span)?, merge_records(lhs, rhs, strategy, span)?,
span, span,
)), )),
// Merge lists by appending // Merge lists by appending

View File

@ -148,8 +148,7 @@ fn rename(
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
let record = let record =
if let Some(closure) = &mut closure { if let Some(closure) = &mut closure {
record record.into_iter()
.into_owned().into_iter()
.map(|(col, val)| { .map(|(col, val)| {
let col = Value::string(col, span); let col = Value::string(col, span);
let data = closure.run_with_value(col)?; let data = closure.run_with_value(col)?;
@ -163,7 +162,7 @@ fn rename(
// record columns are unique so we can track the number // record columns are unique so we can track the number
// of renamed columns to check if any were missed // of renamed columns to check if any were missed
let mut renamed = 0; let mut renamed = 0;
let record = record.into_owned().into_iter().map(|(col, val)| { let record = record.into_iter().map(|(col, val)| {
let col = if let Some(col) = columns.get(&col) { let col = if let Some(col) = columns.get(&col) {
renamed += 1; renamed += 1;
col.clone() col.clone()
@ -194,7 +193,7 @@ fn rename(
} }
} }
None => Ok(record None => Ok(record
.into_owned().into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(i, (col, val))| { .map(|(i, (col, val))| {
(columns.get(i).cloned().unwrap_or(col), val) (columns.get(i).cloned().unwrap_or(col), val)

View File

@ -146,13 +146,7 @@ impl Command for Sort {
let sorted: Value = match value { let sorted: Value = match value {
Value::Record { val, .. } => { Value::Record { val, .. } => {
// Records have two sorting methods, toggled by presence or absence of -v // Records have two sorting methods, toggled by presence or absence of -v
let record = crate::sort_record( let record = crate::sort_record(val, sort_by_value, reverse, insensitive, natural)?;
val.into_owned(),
sort_by_value,
reverse,
insensitive,
natural,
)?;
Value::record(record, span) Value::record(record, span)
} }
value @ Value::List { .. } => { value @ Value::List { .. } => {

View File

@ -194,7 +194,6 @@ fn sort_attributes(val: Value) -> Value {
Value::Record { val, .. } => { Value::Record { val, .. } => {
// TODO: sort inplace // TODO: sort inplace
let sorted = val let sorted = val
.into_owned()
.into_iter() .into_iter()
.sorted_by(|a, b| a.0.cmp(&b.0)) .sorted_by(|a, b| a.0.cmp(&b.0))
.collect_vec(); .collect_vec();

View File

@ -106,7 +106,7 @@ pub fn get_values<'a>(
for item in input { for item in input {
match item { match item {
Value::Record { val, .. } => { Value::Record { val, .. } => {
for (k, v) in &**val { for (k, v) in val {
if let Some(vec) = output.get_mut(k) { if let Some(vec) = output.get_mut(k) {
vec.push(v.clone()); vec.push(v.clone());
} else { } else {

View File

@ -168,7 +168,7 @@ pub fn value_to_json_value(
} }
Value::Record { val, .. } => { Value::Record { val, .. } => {
let mut m = nu_json::Map::new(); let mut m = nu_json::Map::new();
for (k, v) in &**val { for (k, v) in val {
m.insert( m.insert(
k.clone(), k.clone(),
value_to_json_value(engine_state, v, serialize_types)?, value_to_json_value(engine_state, v, serialize_types)?,

View File

@ -178,7 +178,6 @@ fn local_into_string(
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(separator), .join(separator),
Value::Record { val, .. } => val Value::Record { val, .. } => val
.into_owned()
.into_iter() .into_iter()
.map(|(x, y)| { .map(|(x, y)| {
format!( format!(

View File

@ -65,7 +65,7 @@ fn helper(
Value::String { val, .. } | Value::Glob { val, .. } => toml::Value::String(val.clone()), Value::String { val, .. } | Value::Glob { val, .. } => toml::Value::String(val.clone()),
Value::Record { val, .. } => { Value::Record { val, .. } => {
let mut m = toml::map::Map::new(); let mut m = toml::map::Map::new();
for (k, v) in &**val { for (k, v) in val {
m.insert(k.clone(), helper(engine_state, v, serialize_types)?); m.insert(k.clone(), helper(engine_state, v, serialize_types)?);
} }
toml::Value::Table(m) toml::Value::Table(m)

View File

@ -329,7 +329,7 @@ impl Job {
// alternatives like {tag: a attributes: {} content: []}, {tag: a attribbutes: null // alternatives like {tag: a attributes: {} content: []}, {tag: a attribbutes: null
// content: null}, {tag: a}. See to_xml_entry for more // content: null}, {tag: a}. See to_xml_entry for more
let attrs = match attrs { let attrs = match attrs {
Value::Record { val, .. } => val.into_owned(), Value::Record { val, .. } => val,
Value::Nothing { .. } => Record::new(), Value::Nothing { .. } => Record::new(),
_ => { _ => {
return Err(ShellError::CantConvert { return Err(ShellError::CantConvert {

View File

@ -113,7 +113,7 @@ pub fn value_to_yaml_value(
} }
Value::Record { val, .. } => { Value::Record { val, .. } => {
let mut m = serde_yaml::Mapping::new(); let mut m = serde_yaml::Mapping::new();
for (k, v) in &**val { for (k, v) in val {
m.insert( m.insert(
serde_yaml::Value::String(k.clone()), serde_yaml::Value::String(k.clone()),
value_to_yaml_value(engine_state, v, serialize_types)?, value_to_yaml_value(engine_state, v, serialize_types)?,

View File

@ -202,12 +202,11 @@ fn parse_closure_result(
match value { match value {
// {out: ..., next: ...} -> output and continue // {out: ..., next: ...} -> output and continue
Value::Record { val, .. } => { Value::Record { val, .. } => {
let iter = val.into_owned().into_iter();
let mut out = None; let mut out = None;
let mut next = None; let mut next = None;
let mut err = None; let mut err = None;
for (k, v) in iter { for (k, v) in val {
if k.eq_ignore_ascii_case("out") { if k.eq_ignore_ascii_case("out") {
out = Some(v); out = Some(v);
} else if k.eq_ignore_ascii_case("next") { } else if k.eq_ignore_ascii_case("next") {

View File

@ -150,7 +150,7 @@ pub fn highlight_search_in_table(
}); });
}; };
let has_match = record.to_mut().iter_mut().try_fold( let has_match = record.iter_mut().try_fold(
false, false,
|acc: bool, (col, val)| -> Result<bool, ShellError> { |acc: bool, (col, val)| -> Result<bool, ShellError> {
if !searched_cols.contains(&col.as_str()) { if !searched_cols.contains(&col.as_str()) {

View File

@ -27,7 +27,7 @@ fn helper_for_tables(
for val in values { for val in values {
match val { match val {
Value::Record { val, .. } => { Value::Record { val, .. } => {
for (key, value) in &**val { for (key, value) in val {
column_values column_values
.entry(key.clone()) .entry(key.clone())
.and_modify(|v: &mut Vec<Value>| v.push(value.clone())) .and_modify(|v: &mut Vec<Value>| v.push(value.clone()))
@ -80,15 +80,13 @@ pub fn calculate(
), ),
_ => mf(vals, span, name), _ => mf(vals, span, name),
}, },
PipelineData::Value(Value::Record { val, .. }, ..) => { PipelineData::Value(Value::Record { mut val, .. }, ..) => {
let mut record = val.into_owned(); val.iter_mut()
record
.iter_mut()
.try_for_each(|(_, val)| -> Result<(), ShellError> { .try_for_each(|(_, val)| -> Result<(), ShellError> {
*val = mf(slice::from_ref(val), span, name)?; *val = mf(slice::from_ref(val), span, name)?;
Ok(()) Ok(())
})?; })?;
Ok(Value::record(record, span)) Ok(Value::record(val, span))
} }
PipelineData::Value(Value::Range { val, .. }, ..) => { PipelineData::Value(Value::Range { val, .. }, ..) => {
let new_vals: Result<Vec<Value>, ShellError> = val let new_vals: Result<Vec<Value>, ShellError> = val

View File

@ -347,7 +347,7 @@ fn send_form_request(
Value::Record { val, .. } => { Value::Record { val, .. } => {
let mut data: Vec<(String, String)> = Vec::with_capacity(val.len()); let mut data: Vec<(String, String)> = Vec::with_capacity(val.len());
for (col, val) in val.into_owned() { for (col, val) in val {
data.push((col, val.coerce_into_string()?)) data.push((col, val.coerce_into_string()?))
} }
@ -380,7 +380,7 @@ fn send_multipart_request(
) )
}; };
for (col, val) in val.into_owned() { for (col, val) in val {
if let Value::Binary { val, .. } = val { if let Value::Binary { val, .. } = val {
let headers = [ let headers = [
"Content-Type: application/octet-stream".to_string(), "Content-Type: application/octet-stream".to_string(),
@ -580,7 +580,7 @@ pub fn request_add_custom_headers(
match &headers { match &headers {
Value::Record { val, .. } => { Value::Record { val, .. } => {
for (k, v) in &**val { for (k, v) in val {
custom_headers.insert(k.to_string(), v.clone()); custom_headers.insert(k.to_string(), v.clone());
} }
} }
@ -590,7 +590,7 @@ pub fn request_add_custom_headers(
// single row([key1 key2]; [val1 val2]) // single row([key1 key2]; [val1 val2])
match &table[0] { match &table[0] {
Value::Record { val, .. } => { Value::Record { val, .. } => {
for (k, v) in &**val { for (k, v) in val {
custom_headers.insert(k.to_string(), v.clone()); custom_headers.insert(k.to_string(), v.clone());
} }
} }

View File

@ -109,7 +109,6 @@ impl Command for SubCommand {
match value { match value {
Value::Record { val, .. } => { Value::Record { val, .. } => {
let url_components = val let url_components = val
.into_owned()
.into_iter() .into_iter()
.try_fold(UrlComponents::new(), |url, (k, v)| { .try_fold(UrlComponents::new(), |url, (k, v)| {
url.add_component(k, v, head, engine_state) url.add_component(k, v, head, engine_state)

View File

@ -138,7 +138,7 @@ fn handle(
values values
.into_iter() .into_iter()
.map(|val| match val { .map(|val| match val {
Value::Record { val, .. } => Ok(val.into_owned()), Value::Record { val, .. } => Ok(val),
other => Err(ShellError::OnlySupportsThisInputType { other => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "record".into(), exp_input_type: "record".into(),
wrong_type: other.get_type().to_string(), wrong_type: other.get_type().to_string(),

View File

@ -120,7 +120,7 @@ fn handle(
}); });
} }
match value { match value {
Value::Record { val, .. } => Ok(val.into_owned()), Value::Record { val, .. } => Ok(val),
val => Err(ShellError::OnlySupportsThisInputType { val => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "record".into(), exp_input_type: "record".into(),
wrong_type: val.get_type().to_string(), wrong_type: val.get_type().to_string(),

View File

@ -117,7 +117,7 @@ prints out the list properly."#
// dbg!("value::record"); // dbg!("value::record");
let mut items = vec![]; let mut items = vec![];
for (i, (c, v)) in val.into_owned().into_iter().enumerate() { for (i, (c, v)) in val.into_iter().enumerate() {
items.push((i, c, v.to_expanded_string(", ", config))) items.push((i, c, v.to_expanded_string(", ", config)))
} }

View File

@ -450,7 +450,7 @@ fn handle_table_command(mut input: CmdInput<'_>) -> ShellResult<PipelineData> {
} }
PipelineData::Value(Value::Record { val, .. }, ..) => { PipelineData::Value(Value::Record { val, .. }, ..) => {
input.data = PipelineData::Empty; input.data = PipelineData::Empty;
handle_record(input, val.into_owned()) handle_record(input, val)
} }
PipelineData::Value(Value::Error { error, .. }, ..) => { PipelineData::Value(Value::Error { error, .. }, ..) => {
// Propagate this error outward, so that it goes to stderr // Propagate this error outward, so that it goes to stderr
@ -688,7 +688,7 @@ fn handle_row_stream(
stream.map(move |mut value| { stream.map(move |mut value| {
if let Value::Record { val: record, .. } = &mut value { if let Value::Record { val: record, .. } = &mut value {
// Only the name column gets special colors, for now // Only the name column gets special colors, for now
if let Some(value) = record.to_mut().get_mut("name") { if let Some(value) = record.get_mut("name") {
let span = value.span(); let span = value.span();
if let Value::String { val, .. } = value { if let Value::String { val, .. } = value {
if let Some(val) = if let Some(val) =
@ -709,7 +709,7 @@ fn handle_row_stream(
}) => { }) => {
stream.map(|mut value| { stream.map(|mut value| {
if let Value::Record { val: record, .. } = &mut value { if let Value::Record { val: record, .. } = &mut value {
for (rec_col, rec_val) in record.to_mut().iter_mut() { for (rec_col, rec_val) in record.iter_mut() {
// Every column in the HTML theme table except 'name' is colored // Every column in the HTML theme table except 'name' is colored
if rec_col != "name" { if rec_col != "name" {
continue; continue;

View File

@ -380,7 +380,7 @@ fn get_argument_for_color_value(
) -> Option<Argument> { ) -> Option<Argument> {
match color { match color {
Value::Record { val, .. } => { Value::Record { val, .. } => {
let record_exp: Vec<RecordItem> = (**val) let record_exp: Vec<RecordItem> = val
.iter() .iter()
.map(|(k, v)| { .map(|(k, v)| {
RecordItem::Pair( RecordItem::Pair(

View File

@ -92,7 +92,7 @@ pub fn collect_input(value: Value) -> Result<(Vec<String>, Vec<Vec<Value>>)> {
let span = value.span(); let span = value.span();
match value { match value {
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
let (key, val): (_, Vec<Value>) = record.into_owned().into_iter().unzip(); let (key, val): (_, Vec<Value>) = record.into_iter().unzip();
Ok(( Ok((
key, key,

View File

@ -335,8 +335,8 @@ impl PluginTest {
// reorder cols and vals to make more logically compare. // reorder cols and vals to make more logically compare.
// more general, if two record have same col and values, // more general, if two record have same col and values,
// the order of cols shouldn't affect the equal property. // the order of cols shouldn't affect the equal property.
let mut a_rec = a_rec.clone().into_owned(); let mut a_rec = a_rec.clone();
let mut b_rec = b_rec.clone().into_owned(); let mut b_rec = b_rec.clone();
a_rec.sort_cols(); a_rec.sort_cols();
b_rec.sort_cols(); b_rec.sort_cols();

View File

@ -23,14 +23,18 @@ nu-derive-value = { path = "../nu-derive-value", version = "0.102.1" }
brotli = { workspace = true, optional = true } brotli = { workspace = true, optional = true }
bytes = { workspace = true } bytes = { workspace = true }
chrono = { workspace = true, features = [ "serde", "std", "unstable-locales" ], default-features = false } chrono = { workspace = true, features = [
"serde",
"std",
"unstable-locales",
], default-features = false }
chrono-humanize = { workspace = true } chrono-humanize = { workspace = true }
dirs = { workspace = true } dirs = { workspace = true }
fancy-regex = { workspace = true } fancy-regex = { workspace = true }
heck = { workspace = true } heck = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
lru = { workspace = true } lru = { workspace = true }
miette = { workspace = true, features = ["fancy-no-backtrace"]} miette = { workspace = true, features = ["fancy-no-backtrace"] }
num-format = { workspace = true } num-format = { workspace = true }
rmp-serde = { workspace = true, optional = true } rmp-serde = { workspace = true, optional = true }
serde = { workspace = true } serde = { workspace = true }
@ -39,6 +43,7 @@ strum = { workspace = true }
strum_macros = { workspace = true } strum_macros = { workspace = true }
thiserror = "2.0" thiserror = "2.0"
typetag = "0.2" typetag = "0.2"
ecow = { version = "0.2.2", features = ["serde"] }
os_pipe = { workspace = true, optional = true, features = ["io_safety"] } os_pipe = { workspace = true, optional = true, features = ["io_safety"] }
log = { workspace = true } log = { workspace = true }
web-time = { workspace = true } web-time = { workspace = true }
@ -53,16 +58,9 @@ windows-sys = { workspace = true }
[features] [features]
default = ["os"] default = ["os"]
os = [ os = ["nu-utils/os", "os_pipe"]
"nu-utils/os",
"os_pipe",
]
plugin = [ plugin = ["brotli", "os", "rmp-serde"]
"brotli",
"os",
"rmp-serde",
]
[dev-dependencies] [dev-dependencies]
serde_json = { workspace = true } serde_json = { workspace = true }

View File

@ -23,7 +23,7 @@ impl Matcher for Pattern {
Pattern::Record(field_patterns) => match value { Pattern::Record(field_patterns) => match value {
Value::Record { val, .. } => { Value::Record { val, .. } => {
'top: for field_pattern in field_patterns { 'top: for field_pattern in field_patterns {
for (col, val) in &**val { for (col, val) in val {
if col == &field_pattern.0 { if col == &field_pattern.0 {
// We have found the field // We have found the field
let result = field_pattern.1.match_value(val, matches); let result = field_pattern.1.match_value(val, matches);

View File

@ -87,7 +87,7 @@ pub trait Eval {
let inner_span = inner.span(&state); let inner_span = inner.span(&state);
match Self::eval::<D>(state, mut_state, inner)? { match Self::eval::<D>(state, mut_state, inner)? {
Value::Record { val: inner_val, .. } => { Value::Record { val: inner_val, .. } => {
for (col_name, val) in inner_val.into_owned() { for (col_name, val) in inner_val {
if let Some(orig_span) = col_names.get(&col_name) { if let Some(orig_span) = col_names.get(&col_name) {
return Err(ShellError::ColumnDefinedTwice { return Err(ShellError::ColumnDefinedTwice {
col_name, col_name,

View File

@ -698,7 +698,7 @@ impl FromValue for Range {
impl FromValue for Record { impl FromValue for Record {
fn from_value(v: Value) -> Result<Self, ShellError> { fn from_value(v: Value) -> Result<Self, ShellError> {
match v { match v {
Value::Record { val, .. } => Ok(val.into_owned()), Value::Record { val, .. } => Ok(val),
v => Err(ShellError::CantConvert { v => Err(ShellError::CantConvert {
to_type: Self::expected_type().to_string(), to_type: Self::expected_type().to_string(),
from_type: v.get_type().to_string(), from_type: v.get_type().to_string(),

View File

@ -31,7 +31,7 @@ use fancy_regex::Regex;
use nu_utils::{ use nu_utils::{
contains_emoji, contains_emoji,
locale::{get_system_locale_string, LOCALE_OVERRIDE_ENV_VAR}, locale::{get_system_locale_string, LOCALE_OVERRIDE_ENV_VAR},
IgnoreCaseExt, SharedCow, IgnoreCaseExt,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
@ -112,7 +112,7 @@ pub enum Value {
internal_span: Span, internal_span: Span,
}, },
Record { Record {
val: SharedCow<Record>, val: Record,
/// note: spans are being refactored out of Value /// note: spans are being refactored out of Value
/// please use .span() instead of matching this span value /// please use .span() instead of matching this span value
#[serde(rename = "span")] #[serde(rename = "span")]
@ -241,6 +241,11 @@ impl Clone for Value {
} }
} }
// This is to document/enforce the size of `Value` in bytes.
// We should try to avoid increasing the size of `Value`,
// and PRs that do so will have to change the number below so that it's noted in review.
const _: () = assert!(std::mem::size_of::<Value>() <= 48);
impl Value { impl Value {
fn cant_convert_to<T>(&self, typ: &str) -> Result<T, ShellError> { fn cant_convert_to<T>(&self, typ: &str) -> Result<T, ShellError> {
Err(ShellError::CantConvert { Err(ShellError::CantConvert {
@ -537,7 +542,7 @@ impl Value {
/// Unwraps the inner [`Record`] value or returns an error if this `Value` is not a record /// Unwraps the inner [`Record`] value or returns an error if this `Value` is not a record
pub fn into_record(self) -> Result<Record, ShellError> { pub fn into_record(self) -> Result<Record, ShellError> {
if let Value::Record { val, .. } = self { if let Value::Record { val, .. } = self {
Ok(val.into_owned()) Ok(val)
} else { } else {
self.cant_convert_to("record") self.cant_convert_to("record")
} }
@ -1180,7 +1185,7 @@ impl Value {
match current { match current {
Value::Record { mut val, .. } => { Value::Record { mut val, .. } => {
// Make reverse iterate to avoid duplicate column leads to first value, actually last value is expected. // Make reverse iterate to avoid duplicate column leads to first value, actually last value is expected.
if let Some(found) = val.to_mut().iter_mut().rev().find(|x| { if let Some(found) = val.iter_mut().rev().find(|x| {
if insensitive { if insensitive {
x.0.eq_ignore_case(column_name) x.0.eq_ignore_case(column_name)
} else { } else {
@ -1215,15 +1220,13 @@ impl Value {
let val_span = val.span(); let val_span = val.span();
match val { match val {
Value::Record { mut val, .. } => { Value::Record { mut val, .. } => {
if let Some(found) = if let Some(found) = val.iter_mut().rev().find(|x| {
val.to_mut().iter_mut().rev().find(|x| { if insensitive {
if insensitive { x.0.eq_ignore_case(column_name)
x.0.eq_ignore_case(column_name) } else {
} else { x.0 == column_name
x.0 == column_name }
} }) {
})
{
Ok(std::mem::take(found.1)) Ok(std::mem::take(found.1))
} else if *optional { } else if *optional {
Ok(Value::nothing(*origin_span)) Ok(Value::nothing(*origin_span))
@ -1329,7 +1332,6 @@ impl Value {
for val in vals.iter_mut() { for val in vals.iter_mut() {
match val { match val {
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
let record = record.to_mut();
if let Some(val) = record.get_mut(col_name) { if let Some(val) = record.get_mut(col_name) {
val.upsert_data_at_cell_path(path, new_val.clone())?; val.upsert_data_at_cell_path(path, new_val.clone())?;
} else { } else {
@ -1350,7 +1352,6 @@ impl Value {
} }
} }
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
let record = record.to_mut();
if let Some(val) = record.get_mut(col_name) { if let Some(val) = record.get_mut(col_name) {
val.upsert_data_at_cell_path(path, new_val)?; val.upsert_data_at_cell_path(path, new_val)?;
} else { } else {
@ -1432,7 +1433,7 @@ impl Value {
let v_span = val.span(); let v_span = val.span();
match val { match val {
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
if let Some(val) = record.to_mut().get_mut(col_name) { if let Some(val) = record.get_mut(col_name) {
val.update_data_at_cell_path(path, new_val.clone())?; val.update_data_at_cell_path(path, new_val.clone())?;
} else { } else {
return Err(ShellError::CantFindColumn { return Err(ShellError::CantFindColumn {
@ -1454,7 +1455,7 @@ impl Value {
} }
} }
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
if let Some(val) = record.to_mut().get_mut(col_name) { if let Some(val) = record.get_mut(col_name) {
val.update_data_at_cell_path(path, new_val)?; val.update_data_at_cell_path(path, new_val)?;
} else { } else {
return Err(ShellError::CantFindColumn { return Err(ShellError::CantFindColumn {
@ -1519,7 +1520,7 @@ impl Value {
let v_span = val.span(); let v_span = val.span();
match val { match val {
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
if record.to_mut().remove(col_name).is_none() && !optional { if record.remove(col_name).is_none() && !optional {
return Err(ShellError::CantFindColumn { return Err(ShellError::CantFindColumn {
col_name: col_name.clone(), col_name: col_name.clone(),
span: Some(*span), span: Some(*span),
@ -1539,7 +1540,7 @@ impl Value {
Ok(()) Ok(())
} }
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
if record.to_mut().remove(col_name).is_none() && !optional { if record.remove(col_name).is_none() && !optional {
return Err(ShellError::CantFindColumn { return Err(ShellError::CantFindColumn {
col_name: col_name.clone(), col_name: col_name.clone(),
span: Some(*span), span: Some(*span),
@ -1594,7 +1595,7 @@ impl Value {
let v_span = val.span(); let v_span = val.span();
match val { match val {
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
if let Some(val) = record.to_mut().get_mut(col_name) { if let Some(val) = record.get_mut(col_name) {
val.remove_data_at_cell_path(path)?; val.remove_data_at_cell_path(path)?;
} else if !optional { } else if !optional {
return Err(ShellError::CantFindColumn { return Err(ShellError::CantFindColumn {
@ -1616,7 +1617,7 @@ impl Value {
Ok(()) Ok(())
} }
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
if let Some(val) = record.to_mut().get_mut(col_name) { if let Some(val) = record.get_mut(col_name) {
val.remove_data_at_cell_path(path)?; val.remove_data_at_cell_path(path)?;
} else if !optional { } else if !optional {
return Err(ShellError::CantFindColumn { return Err(ShellError::CantFindColumn {
@ -1680,7 +1681,6 @@ impl Value {
let v_span = val.span(); let v_span = val.span();
match val { match val {
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
let record = record.to_mut();
if let Some(val) = record.get_mut(col_name) { if let Some(val) = record.get_mut(col_name) {
if path.is_empty() { if path.is_empty() {
return Err(ShellError::ColumnAlreadyExists { return Err(ShellError::ColumnAlreadyExists {
@ -1714,7 +1714,6 @@ impl Value {
} }
} }
Value::Record { val: record, .. } => { Value::Record { val: record, .. } => {
let record = record.to_mut();
if let Some(val) = record.get_mut(col_name) { if let Some(val) = record.get_mut(col_name) {
if path.is_empty() { if path.is_empty() {
return Err(ShellError::ColumnAlreadyExists { return Err(ShellError::ColumnAlreadyExists {
@ -1818,7 +1817,6 @@ impl Value {
// Check for contained values // Check for contained values
match self { match self {
Value::Record { ref mut val, .. } => val Value::Record { ref mut val, .. } => val
.to_mut()
.iter_mut() .iter_mut()
.try_for_each(|(_, rec_value)| rec_value.recurse_mut(f)), .try_for_each(|(_, rec_value)| rec_value.recurse_mut(f)),
Value::List { ref mut vals, .. } => vals Value::List { ref mut vals, .. } => vals
@ -1953,7 +1951,7 @@ impl Value {
pub fn record(val: Record, span: Span) -> Value { pub fn record(val: Record, span: Span) -> Value {
Value::Record { Value::Record {
val: SharedCow::new(val), val,
internal_span: span, internal_span: span,
} }
} }
@ -2350,8 +2348,8 @@ impl PartialOrd for Value {
// reorder cols and vals to make more logically compare. // reorder cols and vals to make more logically compare.
// more general, if two record have same col and values, // more general, if two record have same col and values,
// the order of cols shouldn't affect the equal property. // the order of cols shouldn't affect the equal property.
let mut lhs = lhs.clone().into_owned(); let mut lhs = lhs.clone();
let mut rhs = rhs.clone().into_owned(); let mut rhs = rhs.clone();
lhs.sort_cols(); lhs.sort_cols();
rhs.sort_cols(); rhs.sort_cols();

View File

@ -1,13 +1,12 @@
//! Our insertion ordered map-type [`Record`] //! Our insertion ordered map-type [`Record`]
use std::{iter::FusedIterator, ops::RangeBounds};
use crate::{ShellError, Span, Value}; use crate::{ShellError, Span, Value};
use ecow::EcoVec;
use serde::{de::Visitor, ser::SerializeMap, Deserialize, Serialize}; use serde::{de::Visitor, ser::SerializeMap, Deserialize, Serialize};
use std::iter::FusedIterator;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Record { pub struct Record {
inner: Vec<(String, Value)>, inner: EcoVec<(String, Value)>,
} }
impl Record { impl Record {
@ -17,7 +16,7 @@ impl Record {
pub fn with_capacity(capacity: usize) -> Self { pub fn with_capacity(capacity: usize) -> Self {
Self { Self {
inner: Vec::with_capacity(capacity), inner: EcoVec::with_capacity(capacity),
} }
} }
@ -100,12 +99,13 @@ impl Record {
pub fn get_mut(&mut self, col: impl AsRef<str>) -> Option<&mut Value> { pub fn get_mut(&mut self, col: impl AsRef<str>) -> Option<&mut Value> {
self.inner self.inner
.make_mut()
.iter_mut() .iter_mut()
.find_map(|(k, v)| if k == col.as_ref() { Some(v) } else { None }) .find_map(|(k, v)| if k == col.as_ref() { Some(v) } else { None })
} }
pub fn get_index(&self, idx: usize) -> Option<(&String, &Value)> { pub fn get_index(&self, idx: usize) -> Option<(&String, &Value)> {
self.inner.get(idx).map(|(col, val): &(_, _)| (col, val)) self.inner.get(idx).map(|(col, val)| (col, val))
} }
/// Remove single value by key /// Remove single value by key
@ -152,7 +152,7 @@ impl Record {
/// ///
/// fn remove_foo_recursively(val: &mut Value) { /// fn remove_foo_recursively(val: &mut Value) {
/// if let Value::Record {val, ..} = val { /// if let Value::Record {val, ..} = val {
/// val.to_mut().retain_mut(keep_non_foo); /// val.retain_mut(keep_non_foo);
/// } /// }
/// } /// }
/// ///
@ -184,7 +184,7 @@ impl Record {
where where
F: FnMut(&str, &mut Value) -> bool, F: FnMut(&str, &mut Value) -> bool,
{ {
self.inner.retain_mut(|(col, val)| keep(col, val)); self.inner.retain(|(k, v)| keep(k, v));
} }
/// Truncate record to the first `len` elements. /// Truncate record to the first `len` elements.
@ -235,35 +235,35 @@ impl Record {
} }
} }
/// Obtain an iterator to remove elements in `range` // /// Obtain an iterator to remove elements in `range`
/// // ///
/// Elements not consumed from the iterator will be dropped // /// Elements not consumed from the iterator will be dropped
/// // ///
/// ```rust // /// ```rust
/// use nu_protocol::{record, Value}; // /// use nu_protocol::{record, Value};
/// // ///
/// let mut rec = record!( // /// let mut rec = record!(
/// "a" => Value::test_nothing(), // /// "a" => Value::test_nothing(),
/// "b" => Value::test_int(42), // /// "b" => Value::test_int(42),
/// "c" => Value::test_string("foo"), // /// "c" => Value::test_string("foo"),
/// ); // /// );
/// { // /// {
/// let mut drainer = rec.drain(1..); // /// let mut drainer = rec.drain(1..);
/// assert_eq!(drainer.next(), Some(("b".into(), Value::test_int(42)))); // /// assert_eq!(drainer.next(), Some(("b".into(), Value::test_int(42))));
/// // Dropping the `Drain` // /// // Dropping the `Drain`
/// } // /// }
/// let mut rec_iter = rec.into_iter(); // /// let mut rec_iter = rec.into_iter();
/// assert_eq!(rec_iter.next(), Some(("a".into(), Value::test_nothing()))); // /// assert_eq!(rec_iter.next(), Some(("a".into(), Value::test_nothing())));
/// assert_eq!(rec_iter.next(), None); // /// assert_eq!(rec_iter.next(), None);
/// ``` // /// ```
pub fn drain<R>(&mut self, range: R) -> Drain // pub fn drain<R>(&mut self, range: R) -> Drain
where // where
R: RangeBounds<usize> + Clone, // R: RangeBounds<usize> + Clone,
{ // {
Drain { // Drain {
iter: self.inner.drain(range), // iter: self.inner.drain(range)
} // }
} // }
/// Sort the record by its columns. /// Sort the record by its columns.
/// ///
@ -288,7 +288,7 @@ impl Record {
/// ); /// );
/// ``` /// ```
pub fn sort_cols(&mut self) { pub fn sort_cols(&mut self) {
self.inner.sort_by(|(k1, _), (k2, _)| k1.cmp(k2)) self.inner.make_mut().sort_by(|(k1, _), (k2, _)| k1.cmp(k2))
} }
} }
@ -383,7 +383,7 @@ impl Extend<(String, Value)> for Record {
} }
pub struct IntoIter { pub struct IntoIter {
iter: std::vec::IntoIter<(String, Value)>, iter: ecow::vec::IntoIter<(String, Value)>,
} }
impl Iterator for IntoIter { impl Iterator for IntoIter {
@ -503,7 +503,7 @@ impl<'a> IntoIterator for &'a mut Record {
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
IterMut { IterMut {
iter: self.inner.iter_mut(), iter: self.inner.make_mut().iter_mut(),
} }
} }
} }
@ -539,7 +539,7 @@ impl ExactSizeIterator for Columns<'_> {
impl FusedIterator for Columns<'_> {} impl FusedIterator for Columns<'_> {}
pub struct IntoColumns { pub struct IntoColumns {
iter: std::vec::IntoIter<(String, Value)>, iter: ecow::vec::IntoIter<(String, Value)>,
} }
impl Iterator for IntoColumns { impl Iterator for IntoColumns {
@ -599,7 +599,7 @@ impl ExactSizeIterator for Values<'_> {
impl FusedIterator for Values<'_> {} impl FusedIterator for Values<'_> {}
pub struct IntoValues { pub struct IntoValues {
iter: std::vec::IntoIter<(String, Value)>, iter: ecow::vec::IntoIter<(String, Value)>,
} }
impl Iterator for IntoValues { impl Iterator for IntoValues {
@ -628,35 +628,39 @@ impl ExactSizeIterator for IntoValues {
impl FusedIterator for IntoValues {} impl FusedIterator for IntoValues {}
pub struct Drain<'a> { // pub struct Drain<'a> {
iter: std::vec::Drain<'a, (String, Value)>, // iter: std::slice::Iter<'a, (String, Value)>,
} // }
impl Iterator for Drain<'_> { // impl Iterator for Drain<'_> {
type Item = (String, Value); // type Item = (String, Value);
fn next(&mut self) -> Option<Self::Item> { // fn next(&mut self) -> Option<Self::Item> {
self.iter.next() // self.iter
} // .next()
// .map(|(col, val)| (col.clone(), val.clone()))
// }
fn size_hint(&self) -> (usize, Option<usize>) { // fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint() // self.iter.size_hint()
} // }
} // }
impl DoubleEndedIterator for Drain<'_> { // impl DoubleEndedIterator for Drain<'_> {
fn next_back(&mut self) -> Option<Self::Item> { // fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back() // self.iter
} // .next_back()
} // .map(|(col, val)| (col.clone(), val.clone()))
// }
// }
impl ExactSizeIterator for Drain<'_> { // impl ExactSizeIterator for Drain<'_> {
fn len(&self) -> usize { // fn len(&self) -> usize {
self.iter.len() // self.iter.len()
} // }
} // }
impl FusedIterator for Drain<'_> {} // impl FusedIterator for Drain<'_> {}
#[macro_export] #[macro_export]
macro_rules! record { macro_rules! record {

View File

@ -1,7 +1,6 @@
use nu_ansi_term::Style; use nu_ansi_term::Style;
use nu_color_config::StyleComputer; use nu_color_config::StyleComputer;
use nu_protocol::{Config, Value}; use nu_protocol::{Config, Value};
use nu_utils::SharedCow;
use crate::{ use crate::{
common::{get_index_style, load_theme, nu_value_to_string_clean}, common::{get_index_style, load_theme, nu_value_to_string_clean},
@ -41,18 +40,14 @@ fn colorize_value(value: &mut Value, config: &Config, style_computer: &StyleComp
// Take ownership of the record and reassign to &mut // Take ownership of the record and reassign to &mut
// We do this to have owned keys through `.into_iter` // We do this to have owned keys through `.into_iter`
let record = std::mem::take(val); let record = std::mem::take(val);
*val = SharedCow::new( *val = record
record .into_iter()
.into_owned() .map(|(header, mut val)| {
.into_iter() colorize_value(&mut val, config, style_computer);
.map(|(mut header, mut val)| { let header = colorize_text(&header, style.color_style).unwrap_or(header);
colorize_value(&mut val, config, style_computer); (header, val)
header = colorize_text(&header, style.color_style).unwrap_or(header); })
.collect();
(header, val)
})
.collect(),
);
} }
Value::List { vals, .. } => { Value::List { vals, .. } => {
for val in vals { for val in vals {

View File

@ -71,7 +71,7 @@ fn build_table(
fn convert_nu_value_to_table_value(value: Value, config: &Config) -> TableValue { fn convert_nu_value_to_table_value(value: Value, config: &Config) -> TableValue {
match value { match value {
Value::Record { val, .. } => build_vertical_map(val.into_owned(), config), Value::Record { val, .. } => build_vertical_map(val, config),
Value::List { vals, .. } => { Value::List { vals, .. } => {
let rebuild_array_as_map = is_valid_record(&vals) && count_columns_in_record(&vals) > 0; let rebuild_array_as_map = is_valid_record(&vals) && count_columns_in_record(&vals) > 0;
if rebuild_array_as_map { if rebuild_array_as_map {
@ -174,7 +174,7 @@ fn build_map_from_record(vals: Vec<Value>, config: &Config) -> TableValue {
for val in vals { for val in vals {
let val = get_as_record(val); let val = get_as_record(val);
for (i, (_, val)) in val.into_owned().into_iter().enumerate() { for (i, (_, val)) in val.into_iter().enumerate() {
let value = convert_nu_value_to_table_value(val, config); let value = convert_nu_value_to_table_value(val, config);
let list = get_table_value_column_mut(&mut list[i]); let list = get_table_value_column_mut(&mut list[i]);
@ -194,7 +194,7 @@ fn get_table_value_column_mut(val: &mut TableValue) -> &mut Vec<TableValue> {
} }
} }
fn get_as_record(val: Value) -> nu_utils::SharedCow<Record> { fn get_as_record(val: Value) -> Record {
match val { match val {
Value::Record { val, .. } => val, Value::Record { val, .. } => val,
_ => unreachable!(), _ => unreachable!(),

View File

@ -183,11 +183,9 @@ impl NuDataFrame {
conversion::insert_record(&mut column_values, record, &maybe_schema)? conversion::insert_record(&mut column_values, record, &maybe_schema)?
} }
Value::Record { val: record, .. } => conversion::insert_record( Value::Record { val: record, .. } => {
&mut column_values, conversion::insert_record(&mut column_values, record, &maybe_schema)?
record.into_owned(), }
&maybe_schema,
)?,
_ => { _ => {
let key = "0".to_string(); let key = "0".to_string();
conversion::insert_value(value, key.into(), &mut column_values, &maybe_schema)? conversion::insert_value(value, key.into(), &mut column_values, &maybe_schema)?

View File

@ -209,7 +209,7 @@ fn value_to_string(
}, },
Value::Record { val, .. } => { Value::Record { val, .. } => {
let mut collection = vec![]; let mut collection = vec![];
for (col, val) in &**val { for (col, val) in val {
let col = if needs_quoting(col) { let col = if needs_quoting(col) {
&escape_quote_string(col) &escape_quote_string(col)
} else { } else {