diff --git a/Cargo.lock b/Cargo.lock index 8a536440a9..35fe1ec0db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1639,6 +1639,15 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "ecow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54bfbb1708988623190a6c4dbedaeaf0f53c20c6395abd6a01feb327b3146f4b" +dependencies = [ + "serde", +] + [[package]] name = "ego-tree" version = "0.10.0" @@ -3956,6 +3965,7 @@ dependencies = [ "chrono-humanize", "dirs", "dirs-sys", + "ecow", "fancy-regex", "heck", "indexmap", diff --git a/crates/nu-cli/src/commands/history/history_import.rs b/crates/nu-cli/src/commands/history/history_import.rs index 80237acb87..0b2c555fed 100644 --- a/crates/nu-cli/src/commands/history/history_import.rs +++ b/crates/nu-cli/src/commands/history/history_import.rs @@ -170,7 +170,7 @@ fn error_from_reedline(e: ReedlineError) -> ShellError { fn item_from_value(v: Value) -> Result { let span = v.span(); 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 { command_line: val, id: None, 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 77c668167a..446200e0b8 100644 --- a/crates/nu-cmd-extra/src/extra/filters/roll/mod.rs +++ b/crates/nu-cmd-extra/src/extra/filters/roll/mod.rs @@ -58,7 +58,7 @@ fn horizontal_rotate_value( Value::Record { val: record, .. } => { 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 { match direction { HorizontalDirection::Right => cols.rotate_right(rotations), diff --git a/crates/nu-cmd-extra/src/extra/filters/rotate.rs b/crates/nu-cmd-extra/src/extra/filters/rotate.rs index 43c83d42b5..b09684fa90 100644 --- a/crates/nu-cmd-extra/src/extra/filters/rotate.rs +++ b/crates/nu-cmd-extra/src/extra/filters/rotate.rs @@ -174,7 +174,7 @@ pub fn rotate( let span = val.span(); match val { 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; new_values.extend_from_slice(&vals); } 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 a3edf7754a..f9462bd90a 100644 --- a/crates/nu-cmd-extra/src/extra/filters/update_cells.rs +++ b/crates/nu-cmd-extra/src/extra/filters/update_cells.rs @@ -127,7 +127,6 @@ impl Iterator for UpdateCellIterator { let mut value = self.iter.next()?; let value = if let Value::Record { val, .. } = &mut value { - let val = val.to_mut(); if let Some(columns) = &self.columns { for (col, val) in val.iter_mut() { if columns.contains(col) { diff --git a/crates/nu-cmd-lang/src/core_commands/describe.rs b/crates/nu-cmd-lang/src/core_commands/describe.rs index 375a840b16..8ae8e81425 100644 --- a/crates/nu-cmd-lang/src/core_commands/describe.rs +++ b/crates/nu-cmd-lang/src/core_commands/describe.rs @@ -266,16 +266,15 @@ fn describe_value_inner( | Value::String { .. } | Value::Glob { .. } | Value::Nothing { .. } => Description::String(value.get_type().to_string()), - Value::Record { val, .. } => { - let mut columns = val.into_owned(); - for (_, val) in &mut columns { + Value::Record { mut val, .. } => { + for (_, val) in &mut val { *val = describe_value_inner(std::mem::take(val), head, engine_state).into_value(head); } Description::Record(record! { "type" => Value::string("record", head), - "columns" => Value::record(columns, head), + "columns" => Value::record(val, head), }) } Value::List { mut vals, .. } => { diff --git a/crates/nu-cmd-lang/src/example_support.rs b/crates/nu-cmd-lang/src/example_support.rs index b3a64dc168..f72ee5fb52 100644 --- a/crates/nu-cmd-lang/src/example_support.rs +++ b/crates/nu-cmd-lang/src/example_support.rs @@ -289,7 +289,7 @@ impl std::fmt::Debug for DebuggableValue<'_> { Value::Record { val, .. } => { write!(f, "{{")?; let mut first = true; - for (col, value) in (&**val).into_iter() { + for (col, value) in val { if !first { write!(f, ", ")?; } diff --git a/crates/nu-command/src/conversions/into/record.rs b/crates/nu-command/src/conversions/into/record.rs index fea5fe1e4e..8d29d0faa3 100644 --- a/crates/nu-command/src/conversions/into/record.rs +++ b/crates/nu-command/src/conversions/into/record.rs @@ -134,7 +134,7 @@ fn into_record(call: &Call, input: PipelineData) -> Result { // 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); } expected_type = Some(ExpectedType::Record); diff --git a/crates/nu-command/src/conversions/into/value.rs b/crates/nu-command/src/conversions/into/value.rs index de371e27b9..4df063b61a 100644 --- a/crates/nu-command/src/conversions/into/value.rs +++ b/crates/nu-command/src/conversions/into/value.rs @@ -110,8 +110,7 @@ impl Iterator for UpdateCellIterator { let span = val.span(); match val { Value::Record { val, .. } => Some(Value::record( - val.into_owned() - .into_iter() + val.into_iter() .map(|(col, val)| match &self.columns { Some(cols) if !cols.contains(&col) => (col, val), _ => ( diff --git a/crates/nu-command/src/database/values/sqlite.rs b/crates/nu-command/src/database/values/sqlite.rs index 8959cf5998..598f02eeb6 100644 --- a/crates/nu-command/src/database/values/sqlite.rs +++ b/crates/nu-command/src/database/values/sqlite.rs @@ -465,7 +465,7 @@ pub fn nu_value_to_params(value: Value) -> Result { Value::Record { val, .. } => { 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)?; if !column.starts_with([':', '@', '$']) { diff --git a/crates/nu-command/src/debug/inspect_table.rs b/crates/nu-command/src/debug/inspect_table.rs index 5f18eb5be3..30f3dc677a 100644 --- a/crates/nu-command/src/debug/inspect_table.rs +++ b/crates/nu-command/src/debug/inspect_table.rs @@ -210,7 +210,7 @@ mod util { let span = value.span(); match value { 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() { true => vec![String::from("")], diff --git a/crates/nu-command/src/env/load_env.rs b/crates/nu-command/src/env/load_env.rs index b508aa6f3b..d1bad3fa11 100644 --- a/crates/nu-command/src/env/load_env.rs +++ b/crates/nu-command/src/env/load_env.rs @@ -43,7 +43,7 @@ impl Command for LoadEnv { let record = match arg { Some(record) => record, None => match input { - PipelineData::Value(Value::Record { val, .. }, ..) => val.into_owned(), + PipelineData::Value(Value::Record { val, .. }, ..) => val, _ => { return Err(ShellError::UnsupportedInput { msg: "'load-env' expects a single record".into(), diff --git a/crates/nu-command/src/filters/columns.rs b/crates/nu-command/src/filters/columns.rs index e0d5b273a8..a288968e8e 100644 --- a/crates/nu-command/src/filters/columns.rs +++ b/crates/nu-command/src/filters/columns.rs @@ -94,7 +94,6 @@ fn getcol(head: Span, input: PipelineData) -> Result { .collect() } Value::Record { val, .. } => val - .into_owned() .into_iter() .map(move |(x, _)| Value::string(x, head)) .collect(), diff --git a/crates/nu-command/src/filters/default.rs b/crates/nu-command/src/filters/default.rs index 6e5610f1be..d608d4a868 100644 --- a/crates/nu-command/src/filters/default.rs +++ b/crates/nu-command/src/filters/default.rs @@ -102,7 +102,6 @@ fn default( val: ref mut record, .. } => { - let record = record.to_mut(); if let Some(val) = record.get_mut(&column.item) { if matches!(val, Value::Nothing { .. }) { *val = value.clone(); diff --git a/crates/nu-command/src/filters/drop/column.rs b/crates/nu-command/src/filters/drop/column.rs index b69a15b2d7..7d083d180f 100644 --- a/crates/nu-command/src/filters/drop/column.rs +++ b/crates/nu-command/src/filters/drop/column.rs @@ -128,7 +128,7 @@ fn drop_cols( .. } => { let len = record.len().saturating_sub(columns); - record.to_mut().truncate(len); + record.truncate(len); Ok(v.into_pipeline_data_with_metadata(metadata)) } // Propagate errors @@ -149,7 +149,9 @@ fn drop_cols( fn drop_cols_set(val: &mut Value, head: Span, drop: usize) -> Result, ShellError> { if let Value::Record { val: record, .. } = val { 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 { Err(unsupported_value_error(val, head)) } @@ -161,7 +163,7 @@ fn drop_record_cols( drop_cols: &HashSet, ) -> Result<(), ShellError> { if let Value::Record { val, .. } = val { - val.to_mut().retain(|col, _| !drop_cols.contains(col)); + val.retain(|col, _| !drop_cols.contains(col)); Ok(()) } else { Err(unsupported_value_error(val, head)) diff --git a/crates/nu-command/src/filters/flatten.rs b/crates/nu-command/src/filters/flatten.rs index 982db17029..014febf723 100644 --- a/crates/nu-command/src/filters/flatten.rs +++ b/crates/nu-command/src/filters/flatten.rs @@ -156,7 +156,7 @@ fn flat_value(columns: &[CellPath], item: Value, all: bool) -> Vec { let mut out = IndexMap::::new(); 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 need_flatten = { columns.is_empty() || column_requested.is_some() }; let span = value.span(); @@ -164,7 +164,7 @@ fn flat_value(columns: &[CellPath], item: Value, all: bool) -> Vec { match value { Value::Record { ref val, .. } => { if need_flatten { - for (col, val) in val.clone().into_owned() { + for (col, val) in val.clone() { if out.contains_key(&col) { out.insert(format!("{column}_{col}"), val); } else { diff --git a/crates/nu-command/src/filters/headers.rs b/crates/nu-command/src/filters/headers.rs index 6798b3142a..3599749685 100644 --- a/crates/nu-command/src/filters/headers.rs +++ b/crates/nu-command/src/filters/headers.rs @@ -142,7 +142,6 @@ fn replace_headers( if let Value::Record { val: record, .. } = value { Ok(Value::record( record - .into_owned() .into_iter() .filter_map(|(col, val)| { old_headers diff --git a/crates/nu-command/src/filters/items.rs b/crates/nu-command/src/filters/items.rs index a32f2808b8..85b8c7312f 100644 --- a/crates/nu-command/src/filters/items.rs +++ b/crates/nu-command/src/filters/items.rs @@ -49,7 +49,6 @@ impl Command for Items { Value::Record { val, .. } => { let mut closure = ClosureEval::new(engine_state, stack, closure); Ok(val - .into_owned() .into_iter() .map_while(move |(col, val)| { let result = closure diff --git a/crates/nu-command/src/filters/merge/common.rs b/crates/nu-command/src/filters/merge/common.rs index 219250d113..20469f8e91 100644 --- a/crates/nu-command/src/filters/merge/common.rs +++ b/crates/nu-command/src/filters/merge/common.rs @@ -65,7 +65,7 @@ pub(crate) fn do_merge( Value::Record { val: lhs, .. }, Value::Record { val: rhs, .. }, ) => Ok(Value::record( - merge_records(lhs.into_owned(), rhs.into_owned(), strategy, span)?, + merge_records(lhs, rhs, strategy, span)?, span, )), // Deep merge records @@ -74,7 +74,7 @@ pub(crate) fn do_merge( Value::Record { val: lhs, .. }, Value::Record { val: rhs, .. }, ) => Ok(Value::record( - merge_records(lhs.into_owned(), rhs.into_owned(), strategy, span)?, + merge_records(lhs, rhs, strategy, span)?, span, )), // Merge lists by appending diff --git a/crates/nu-command/src/filters/rename.rs b/crates/nu-command/src/filters/rename.rs index 174d8d43ae..ac4d607ad6 100644 --- a/crates/nu-command/src/filters/rename.rs +++ b/crates/nu-command/src/filters/rename.rs @@ -148,8 +148,7 @@ fn rename( Value::Record { val: record, .. } => { let record = if let Some(closure) = &mut closure { - record - .into_owned().into_iter() + record.into_iter() .map(|(col, val)| { let col = Value::string(col, span); let data = closure.run_with_value(col)?; @@ -163,7 +162,7 @@ fn rename( // record columns are unique so we can track the number // of renamed columns to check if any were missed 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) { renamed += 1; col.clone() @@ -194,7 +193,7 @@ fn rename( } } None => Ok(record - .into_owned().into_iter() + .into_iter() .enumerate() .map(|(i, (col, val))| { (columns.get(i).cloned().unwrap_or(col), val) diff --git a/crates/nu-command/src/filters/sort.rs b/crates/nu-command/src/filters/sort.rs index 8b8c2e6d60..ccb3a8e3a9 100644 --- a/crates/nu-command/src/filters/sort.rs +++ b/crates/nu-command/src/filters/sort.rs @@ -146,13 +146,7 @@ impl Command for Sort { let sorted: Value = match value { Value::Record { val, .. } => { // Records have two sorting methods, toggled by presence or absence of -v - let record = crate::sort_record( - val.into_owned(), - sort_by_value, - reverse, - insensitive, - natural, - )?; + let record = crate::sort_record(val, sort_by_value, reverse, insensitive, natural)?; Value::record(record, span) } value @ Value::List { .. } => { diff --git a/crates/nu-command/src/filters/uniq.rs b/crates/nu-command/src/filters/uniq.rs index 8042963d75..2220703c1f 100644 --- a/crates/nu-command/src/filters/uniq.rs +++ b/crates/nu-command/src/filters/uniq.rs @@ -194,7 +194,6 @@ fn sort_attributes(val: Value) -> Value { Value::Record { val, .. } => { // TODO: sort inplace let sorted = val - .into_owned() .into_iter() .sorted_by(|a, b| a.0.cmp(&b.0)) .collect_vec(); diff --git a/crates/nu-command/src/filters/values.rs b/crates/nu-command/src/filters/values.rs index 5039938055..2fb415fc34 100644 --- a/crates/nu-command/src/filters/values.rs +++ b/crates/nu-command/src/filters/values.rs @@ -106,7 +106,7 @@ pub fn get_values<'a>( for item in input { match item { Value::Record { val, .. } => { - for (k, v) in &**val { + for (k, v) in val { if let Some(vec) = output.get_mut(k) { vec.push(v.clone()); } else { diff --git a/crates/nu-command/src/formats/to/json.rs b/crates/nu-command/src/formats/to/json.rs index 7eb749977f..8dfd624482 100644 --- a/crates/nu-command/src/formats/to/json.rs +++ b/crates/nu-command/src/formats/to/json.rs @@ -168,7 +168,7 @@ pub fn value_to_json_value( } Value::Record { val, .. } => { let mut m = nu_json::Map::new(); - for (k, v) in &**val { + for (k, v) in val { m.insert( k.clone(), value_to_json_value(engine_state, v, serialize_types)?, diff --git a/crates/nu-command/src/formats/to/text.rs b/crates/nu-command/src/formats/to/text.rs index 858c5fce29..b178d5702a 100644 --- a/crates/nu-command/src/formats/to/text.rs +++ b/crates/nu-command/src/formats/to/text.rs @@ -178,7 +178,6 @@ fn local_into_string( .collect::>() .join(separator), Value::Record { val, .. } => val - .into_owned() .into_iter() .map(|(x, y)| { format!( diff --git a/crates/nu-command/src/formats/to/toml.rs b/crates/nu-command/src/formats/to/toml.rs index 11630e4522..84c2a01371 100644 --- a/crates/nu-command/src/formats/to/toml.rs +++ b/crates/nu-command/src/formats/to/toml.rs @@ -65,7 +65,7 @@ fn helper( Value::String { val, .. } | Value::Glob { val, .. } => toml::Value::String(val.clone()), Value::Record { val, .. } => { 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)?); } toml::Value::Table(m) diff --git a/crates/nu-command/src/formats/to/xml.rs b/crates/nu-command/src/formats/to/xml.rs index 4476f654c2..1c87f52ab5 100644 --- a/crates/nu-command/src/formats/to/xml.rs +++ b/crates/nu-command/src/formats/to/xml.rs @@ -329,7 +329,7 @@ impl Job { // alternatives like {tag: a attributes: {} content: []}, {tag: a attribbutes: null // content: null}, {tag: a}. See to_xml_entry for more let attrs = match attrs { - Value::Record { val, .. } => val.into_owned(), + Value::Record { val, .. } => val, Value::Nothing { .. } => Record::new(), _ => { return Err(ShellError::CantConvert { diff --git a/crates/nu-command/src/formats/to/yaml.rs b/crates/nu-command/src/formats/to/yaml.rs index 7b6f99bb67..f2697c943d 100644 --- a/crates/nu-command/src/formats/to/yaml.rs +++ b/crates/nu-command/src/formats/to/yaml.rs @@ -113,7 +113,7 @@ pub fn value_to_yaml_value( } Value::Record { val, .. } => { let mut m = serde_yaml::Mapping::new(); - for (k, v) in &**val { + for (k, v) in val { m.insert( serde_yaml::Value::String(k.clone()), value_to_yaml_value(engine_state, v, serialize_types)?, diff --git a/crates/nu-command/src/generators/generate.rs b/crates/nu-command/src/generators/generate.rs index 05a34dcbeb..f08fc2953b 100644 --- a/crates/nu-command/src/generators/generate.rs +++ b/crates/nu-command/src/generators/generate.rs @@ -202,12 +202,11 @@ fn parse_closure_result( match value { // {out: ..., next: ...} -> output and continue Value::Record { val, .. } => { - let iter = val.into_owned().into_iter(); let mut out = None; let mut next = None; let mut err = None; - for (k, v) in iter { + for (k, v) in val { if k.eq_ignore_ascii_case("out") { out = Some(v); } else if k.eq_ignore_ascii_case("next") { diff --git a/crates/nu-command/src/help/help_.rs b/crates/nu-command/src/help/help_.rs index 3cdd27a672..f21e591761 100644 --- a/crates/nu-command/src/help/help_.rs +++ b/crates/nu-command/src/help/help_.rs @@ -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, |acc: bool, (col, val)| -> Result { if !searched_cols.contains(&col.as_str()) { diff --git a/crates/nu-command/src/math/utils.rs b/crates/nu-command/src/math/utils.rs index 62f96ea073..e64667567d 100644 --- a/crates/nu-command/src/math/utils.rs +++ b/crates/nu-command/src/math/utils.rs @@ -27,7 +27,7 @@ fn helper_for_tables( for val in values { match val { Value::Record { val, .. } => { - for (key, value) in &**val { + for (key, value) in val { column_values .entry(key.clone()) .and_modify(|v: &mut Vec| v.push(value.clone())) @@ -80,15 +80,13 @@ pub fn calculate( ), _ => mf(vals, span, name), }, - PipelineData::Value(Value::Record { val, .. }, ..) => { - let mut record = val.into_owned(); - record - .iter_mut() + PipelineData::Value(Value::Record { mut val, .. }, ..) => { + val.iter_mut() .try_for_each(|(_, val)| -> Result<(), ShellError> { *val = mf(slice::from_ref(val), span, name)?; Ok(()) })?; - Ok(Value::record(record, span)) + Ok(Value::record(val, span)) } PipelineData::Value(Value::Range { val, .. }, ..) => { let new_vals: Result, ShellError> = val diff --git a/crates/nu-command/src/network/http/client.rs b/crates/nu-command/src/network/http/client.rs index 28a8bc8207..047f8f2d39 100644 --- a/crates/nu-command/src/network/http/client.rs +++ b/crates/nu-command/src/network/http/client.rs @@ -347,7 +347,7 @@ fn send_form_request( Value::Record { val, .. } => { 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()?)) } @@ -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 { let headers = [ "Content-Type: application/octet-stream".to_string(), @@ -580,7 +580,7 @@ pub fn request_add_custom_headers( match &headers { Value::Record { val, .. } => { - for (k, v) in &**val { + for (k, v) in val { custom_headers.insert(k.to_string(), v.clone()); } } @@ -590,7 +590,7 @@ pub fn request_add_custom_headers( // single row([key1 key2]; [val1 val2]) match &table[0] { Value::Record { val, .. } => { - for (k, v) in &**val { + for (k, v) in val { custom_headers.insert(k.to_string(), v.clone()); } } diff --git a/crates/nu-command/src/network/url/join.rs b/crates/nu-command/src/network/url/join.rs index b8e120134a..41d419bf89 100644 --- a/crates/nu-command/src/network/url/join.rs +++ b/crates/nu-command/src/network/url/join.rs @@ -109,7 +109,6 @@ impl Command for SubCommand { match value { Value::Record { val, .. } => { let url_components = val - .into_owned() .into_iter() .try_fold(UrlComponents::new(), |url, (k, v)| { url.add_component(k, v, head, engine_state) diff --git a/crates/nu-command/src/stor/insert.rs b/crates/nu-command/src/stor/insert.rs index 1d0f5ae6c0..5457c3a0a9 100644 --- a/crates/nu-command/src/stor/insert.rs +++ b/crates/nu-command/src/stor/insert.rs @@ -138,7 +138,7 @@ fn handle( values .into_iter() .map(|val| match val { - Value::Record { val, .. } => Ok(val.into_owned()), + Value::Record { val, .. } => Ok(val), other => Err(ShellError::OnlySupportsThisInputType { exp_input_type: "record".into(), wrong_type: other.get_type().to_string(), diff --git a/crates/nu-command/src/stor/update.rs b/crates/nu-command/src/stor/update.rs index 98652bae2a..40fc193806 100644 --- a/crates/nu-command/src/stor/update.rs +++ b/crates/nu-command/src/stor/update.rs @@ -120,7 +120,7 @@ fn handle( }); } match value { - Value::Record { val, .. } => Ok(val.into_owned()), + Value::Record { val, .. } => Ok(val), val => Err(ShellError::OnlySupportsThisInputType { exp_input_type: "record".into(), wrong_type: val.get_type().to_string(), diff --git a/crates/nu-command/src/viewers/griddle.rs b/crates/nu-command/src/viewers/griddle.rs index 56c1cb1132..a715523868 100644 --- a/crates/nu-command/src/viewers/griddle.rs +++ b/crates/nu-command/src/viewers/griddle.rs @@ -117,7 +117,7 @@ prints out the list properly."# // dbg!("value::record"); 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))) } diff --git a/crates/nu-command/src/viewers/table.rs b/crates/nu-command/src/viewers/table.rs index 147555558e..923c24d7bd 100644 --- a/crates/nu-command/src/viewers/table.rs +++ b/crates/nu-command/src/viewers/table.rs @@ -450,7 +450,7 @@ fn handle_table_command(mut input: CmdInput<'_>) -> ShellResult { } PipelineData::Value(Value::Record { val, .. }, ..) => { input.data = PipelineData::Empty; - handle_record(input, val.into_owned()) + handle_record(input, val) } PipelineData::Value(Value::Error { error, .. }, ..) => { // Propagate this error outward, so that it goes to stderr @@ -688,7 +688,7 @@ fn handle_row_stream( stream.map(move |mut value| { if let Value::Record { val: record, .. } = &mut value { // 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(); if let Value::String { val, .. } = value { if let Some(val) = @@ -709,7 +709,7 @@ fn handle_row_stream( }) => { stream.map(|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 if rec_col != "name" { continue; diff --git a/crates/nu-engine/src/documentation.rs b/crates/nu-engine/src/documentation.rs index 78c11bc4d0..e537677ee6 100644 --- a/crates/nu-engine/src/documentation.rs +++ b/crates/nu-engine/src/documentation.rs @@ -380,7 +380,7 @@ fn get_argument_for_color_value( ) -> Option { match color { Value::Record { val, .. } => { - let record_exp: Vec = (**val) + let record_exp: Vec = val .iter() .map(|(k, v)| { RecordItem::Pair( diff --git a/crates/nu-explore/src/nu_common/value.rs b/crates/nu-explore/src/nu_common/value.rs index 3b5751c11a..e6a38b826d 100644 --- a/crates/nu-explore/src/nu_common/value.rs +++ b/crates/nu-explore/src/nu_common/value.rs @@ -92,7 +92,7 @@ pub fn collect_input(value: Value) -> Result<(Vec, Vec>)> { let span = value.span(); match value { Value::Record { val: record, .. } => { - let (key, val): (_, Vec) = record.into_owned().into_iter().unzip(); + let (key, val): (_, Vec) = record.into_iter().unzip(); Ok(( key, diff --git a/crates/nu-plugin-test-support/src/plugin_test.rs b/crates/nu-plugin-test-support/src/plugin_test.rs index 1d66674824..8c07634127 100644 --- a/crates/nu-plugin-test-support/src/plugin_test.rs +++ b/crates/nu-plugin-test-support/src/plugin_test.rs @@ -335,8 +335,8 @@ impl PluginTest { // 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 mut a_rec = a_rec.clone().into_owned(); - let mut b_rec = b_rec.clone().into_owned(); + let mut a_rec = a_rec.clone(); + let mut b_rec = b_rec.clone(); a_rec.sort_cols(); b_rec.sort_cols(); diff --git a/crates/nu-protocol/Cargo.toml b/crates/nu-protocol/Cargo.toml index 9050037cd3..89a739775f 100644 --- a/crates/nu-protocol/Cargo.toml +++ b/crates/nu-protocol/Cargo.toml @@ -23,14 +23,18 @@ nu-derive-value = { path = "../nu-derive-value", version = "0.102.1" } brotli = { workspace = true, optional = 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 } dirs = { workspace = true } fancy-regex = { workspace = true } heck = { workspace = true } indexmap = { workspace = true } lru = { workspace = true } -miette = { workspace = true, features = ["fancy-no-backtrace"]} +miette = { workspace = true, features = ["fancy-no-backtrace"] } num-format = { workspace = true } rmp-serde = { workspace = true, optional = true } serde = { workspace = true } @@ -39,6 +43,7 @@ strum = { workspace = true } strum_macros = { workspace = true } thiserror = "2.0" typetag = "0.2" +ecow = { version = "0.2.2", features = ["serde"] } os_pipe = { workspace = true, optional = true, features = ["io_safety"] } log = { workspace = true } web-time = { workspace = true } @@ -53,16 +58,9 @@ windows-sys = { workspace = true } [features] default = ["os"] -os = [ - "nu-utils/os", - "os_pipe", -] +os = ["nu-utils/os", "os_pipe"] -plugin = [ - "brotli", - "os", - "rmp-serde", -] +plugin = ["brotli", "os", "rmp-serde"] [dev-dependencies] serde_json = { workspace = true } diff --git a/crates/nu-protocol/src/engine/pattern_match.rs b/crates/nu-protocol/src/engine/pattern_match.rs index bac2fd8d17..75c43df497 100644 --- a/crates/nu-protocol/src/engine/pattern_match.rs +++ b/crates/nu-protocol/src/engine/pattern_match.rs @@ -23,7 +23,7 @@ impl Matcher for Pattern { Pattern::Record(field_patterns) => match value { Value::Record { val, .. } => { 'top: for field_pattern in field_patterns { - for (col, val) in &**val { + for (col, val) in val { if col == &field_pattern.0 { // We have found the field let result = field_pattern.1.match_value(val, matches); diff --git a/crates/nu-protocol/src/eval_base.rs b/crates/nu-protocol/src/eval_base.rs index 15fc5105d3..c9e06f3740 100644 --- a/crates/nu-protocol/src/eval_base.rs +++ b/crates/nu-protocol/src/eval_base.rs @@ -87,7 +87,7 @@ pub trait Eval { let inner_span = inner.span(&state); match Self::eval::(state, mut_state, inner)? { 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) { return Err(ShellError::ColumnDefinedTwice { col_name, diff --git a/crates/nu-protocol/src/value/from_value.rs b/crates/nu-protocol/src/value/from_value.rs index fb6fc26be5..644742c8ee 100644 --- a/crates/nu-protocol/src/value/from_value.rs +++ b/crates/nu-protocol/src/value/from_value.rs @@ -698,7 +698,7 @@ impl FromValue for Range { impl FromValue for Record { fn from_value(v: Value) -> Result { match v { - Value::Record { val, .. } => Ok(val.into_owned()), + Value::Record { val, .. } => Ok(val), v => Err(ShellError::CantConvert { to_type: Self::expected_type().to_string(), from_type: v.get_type().to_string(), diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index fb189daca8..432884c234 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -31,7 +31,7 @@ use fancy_regex::Regex; use nu_utils::{ contains_emoji, locale::{get_system_locale_string, LOCALE_OVERRIDE_ENV_VAR}, - IgnoreCaseExt, SharedCow, + IgnoreCaseExt, }; use serde::{Deserialize, Serialize}; use std::{ @@ -112,7 +112,7 @@ pub enum Value { internal_span: Span, }, Record { - val: SharedCow, + val: Record, /// note: spans are being refactored out of Value /// please use .span() instead of matching this span value #[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::() <= 48); + impl Value { fn cant_convert_to(&self, typ: &str) -> Result { 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 pub fn into_record(self) -> Result { if let Value::Record { val, .. } = self { - Ok(val.into_owned()) + Ok(val) } else { self.cant_convert_to("record") } @@ -1180,7 +1185,7 @@ impl Value { match current { Value::Record { mut val, .. } => { // 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 { x.0.eq_ignore_case(column_name) } else { @@ -1215,15 +1220,13 @@ impl Value { let val_span = val.span(); match val { Value::Record { mut val, .. } => { - if let Some(found) = - val.to_mut().iter_mut().rev().find(|x| { - if insensitive { - x.0.eq_ignore_case(column_name) - } else { - x.0 == column_name - } - }) - { + if let Some(found) = val.iter_mut().rev().find(|x| { + if insensitive { + x.0.eq_ignore_case(column_name) + } else { + x.0 == column_name + } + }) { Ok(std::mem::take(found.1)) } else if *optional { Ok(Value::nothing(*origin_span)) @@ -1329,7 +1332,6 @@ impl Value { for val in vals.iter_mut() { match val { Value::Record { val: record, .. } => { - let record = record.to_mut(); if let Some(val) = record.get_mut(col_name) { val.upsert_data_at_cell_path(path, new_val.clone())?; } else { @@ -1350,7 +1352,6 @@ impl Value { } } Value::Record { val: record, .. } => { - let record = record.to_mut(); if let Some(val) = record.get_mut(col_name) { val.upsert_data_at_cell_path(path, new_val)?; } else { @@ -1432,7 +1433,7 @@ impl Value { let v_span = val.span(); match val { 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())?; } else { return Err(ShellError::CantFindColumn { @@ -1454,7 +1455,7 @@ impl Value { } } 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)?; } else { return Err(ShellError::CantFindColumn { @@ -1519,7 +1520,7 @@ impl Value { let v_span = val.span(); match val { 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 { col_name: col_name.clone(), span: Some(*span), @@ -1539,7 +1540,7 @@ impl Value { Ok(()) } 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 { col_name: col_name.clone(), span: Some(*span), @@ -1594,7 +1595,7 @@ impl Value { let v_span = val.span(); match val { 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)?; } else if !optional { return Err(ShellError::CantFindColumn { @@ -1616,7 +1617,7 @@ impl Value { Ok(()) } 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)?; } else if !optional { return Err(ShellError::CantFindColumn { @@ -1680,7 +1681,6 @@ impl Value { let v_span = val.span(); match val { Value::Record { val: record, .. } => { - let record = record.to_mut(); if let Some(val) = record.get_mut(col_name) { if path.is_empty() { return Err(ShellError::ColumnAlreadyExists { @@ -1714,7 +1714,6 @@ impl Value { } } Value::Record { val: record, .. } => { - let record = record.to_mut(); if let Some(val) = record.get_mut(col_name) { if path.is_empty() { return Err(ShellError::ColumnAlreadyExists { @@ -1818,7 +1817,6 @@ impl Value { // Check for contained values match self { Value::Record { ref mut val, .. } => val - .to_mut() .iter_mut() .try_for_each(|(_, rec_value)| rec_value.recurse_mut(f)), Value::List { ref mut vals, .. } => vals @@ -1953,7 +1951,7 @@ impl Value { pub fn record(val: Record, span: Span) -> Value { Value::Record { - val: SharedCow::new(val), + val, internal_span: span, } } @@ -2350,8 +2348,8 @@ impl PartialOrd for Value { // 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 mut lhs = lhs.clone().into_owned(); - let mut rhs = rhs.clone().into_owned(); + let mut lhs = lhs.clone(); + let mut rhs = rhs.clone(); lhs.sort_cols(); rhs.sort_cols(); diff --git a/crates/nu-protocol/src/value/record.rs b/crates/nu-protocol/src/value/record.rs index 2b667a7cf0..96d45ee71a 100644 --- a/crates/nu-protocol/src/value/record.rs +++ b/crates/nu-protocol/src/value/record.rs @@ -1,13 +1,12 @@ //! Our insertion ordered map-type [`Record`] -use std::{iter::FusedIterator, ops::RangeBounds}; - use crate::{ShellError, Span, Value}; - +use ecow::EcoVec; use serde::{de::Visitor, ser::SerializeMap, Deserialize, Serialize}; +use std::iter::FusedIterator; #[derive(Debug, Clone, Default)] pub struct Record { - inner: Vec<(String, Value)>, + inner: EcoVec<(String, Value)>, } impl Record { @@ -17,7 +16,7 @@ impl Record { pub fn with_capacity(capacity: usize) -> 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) -> Option<&mut Value> { self.inner + .make_mut() .iter_mut() .find_map(|(k, v)| if k == col.as_ref() { Some(v) } else { None }) } 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 @@ -152,7 +152,7 @@ impl Record { /// /// fn remove_foo_recursively(val: &mut Value) { /// 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 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. @@ -235,35 +235,35 @@ impl Record { } } - /// Obtain an iterator to remove elements in `range` - /// - /// Elements not consumed from the iterator will be dropped - /// - /// ```rust - /// use nu_protocol::{record, Value}; - /// - /// let mut rec = record!( - /// "a" => Value::test_nothing(), - /// "b" => Value::test_int(42), - /// "c" => Value::test_string("foo"), - /// ); - /// { - /// let mut drainer = rec.drain(1..); - /// assert_eq!(drainer.next(), Some(("b".into(), Value::test_int(42)))); - /// // Dropping the `Drain` - /// } - /// let mut rec_iter = rec.into_iter(); - /// assert_eq!(rec_iter.next(), Some(("a".into(), Value::test_nothing()))); - /// assert_eq!(rec_iter.next(), None); - /// ``` - pub fn drain(&mut self, range: R) -> Drain - where - R: RangeBounds + Clone, - { - Drain { - iter: self.inner.drain(range), - } - } + // /// Obtain an iterator to remove elements in `range` + // /// + // /// Elements not consumed from the iterator will be dropped + // /// + // /// ```rust + // /// use nu_protocol::{record, Value}; + // /// + // /// let mut rec = record!( + // /// "a" => Value::test_nothing(), + // /// "b" => Value::test_int(42), + // /// "c" => Value::test_string("foo"), + // /// ); + // /// { + // /// let mut drainer = rec.drain(1..); + // /// assert_eq!(drainer.next(), Some(("b".into(), Value::test_int(42)))); + // /// // Dropping the `Drain` + // /// } + // /// let mut rec_iter = rec.into_iter(); + // /// assert_eq!(rec_iter.next(), Some(("a".into(), Value::test_nothing()))); + // /// assert_eq!(rec_iter.next(), None); + // /// ``` + // pub fn drain(&mut self, range: R) -> Drain + // where + // R: RangeBounds + Clone, + // { + // Drain { + // iter: self.inner.drain(range) + // } + // } /// Sort the record by its columns. /// @@ -288,7 +288,7 @@ impl Record { /// ); /// ``` 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 { - iter: std::vec::IntoIter<(String, Value)>, + iter: ecow::vec::IntoIter<(String, Value)>, } impl Iterator for IntoIter { @@ -503,7 +503,7 @@ impl<'a> IntoIterator for &'a mut Record { fn into_iter(self) -> Self::IntoIter { 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<'_> {} pub struct IntoColumns { - iter: std::vec::IntoIter<(String, Value)>, + iter: ecow::vec::IntoIter<(String, Value)>, } impl Iterator for IntoColumns { @@ -599,7 +599,7 @@ impl ExactSizeIterator for Values<'_> { impl FusedIterator for Values<'_> {} pub struct IntoValues { - iter: std::vec::IntoIter<(String, Value)>, + iter: ecow::vec::IntoIter<(String, Value)>, } impl Iterator for IntoValues { @@ -628,35 +628,39 @@ impl ExactSizeIterator for IntoValues { impl FusedIterator for IntoValues {} -pub struct Drain<'a> { - iter: std::vec::Drain<'a, (String, Value)>, -} +// pub struct Drain<'a> { +// iter: std::slice::Iter<'a, (String, Value)>, +// } -impl Iterator for Drain<'_> { - type Item = (String, Value); +// impl Iterator for Drain<'_> { +// type Item = (String, Value); - fn next(&mut self) -> Option { - self.iter.next() - } +// fn next(&mut self) -> Option { +// self.iter +// .next() +// .map(|(col, val)| (col.clone(), val.clone())) +// } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} +// fn size_hint(&self) -> (usize, Option) { +// self.iter.size_hint() +// } +// } -impl DoubleEndedIterator for Drain<'_> { - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} +// impl DoubleEndedIterator for Drain<'_> { +// fn next_back(&mut self) -> Option { +// self.iter +// .next_back() +// .map(|(col, val)| (col.clone(), val.clone())) +// } +// } -impl ExactSizeIterator for Drain<'_> { - fn len(&self) -> usize { - self.iter.len() - } -} +// impl ExactSizeIterator for Drain<'_> { +// fn len(&self) -> usize { +// self.iter.len() +// } +// } -impl FusedIterator for Drain<'_> {} +// impl FusedIterator for Drain<'_> {} #[macro_export] macro_rules! record { diff --git a/crates/nu-table/src/types/collapse.rs b/crates/nu-table/src/types/collapse.rs index 4b4ebed172..c30777961e 100644 --- a/crates/nu-table/src/types/collapse.rs +++ b/crates/nu-table/src/types/collapse.rs @@ -1,7 +1,6 @@ use nu_ansi_term::Style; use nu_color_config::StyleComputer; use nu_protocol::{Config, Value}; -use nu_utils::SharedCow; use crate::{ 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 // We do this to have owned keys through `.into_iter` let record = std::mem::take(val); - *val = SharedCow::new( - record - .into_owned() - .into_iter() - .map(|(mut header, mut val)| { - colorize_value(&mut val, config, style_computer); - header = colorize_text(&header, style.color_style).unwrap_or(header); - - (header, val) - }) - .collect(), - ); + *val = record + .into_iter() + .map(|(header, mut val)| { + colorize_value(&mut val, config, style_computer); + let header = colorize_text(&header, style.color_style).unwrap_or(header); + (header, val) + }) + .collect(); } Value::List { vals, .. } => { for val in vals { diff --git a/crates/nu-table/src/unstructured_table.rs b/crates/nu-table/src/unstructured_table.rs index be722c5e51..d81d0564dc 100644 --- a/crates/nu-table/src/unstructured_table.rs +++ b/crates/nu-table/src/unstructured_table.rs @@ -71,7 +71,7 @@ fn build_table( fn convert_nu_value_to_table_value(value: Value, config: &Config) -> TableValue { match value { - Value::Record { val, .. } => build_vertical_map(val.into_owned(), 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 { @@ -174,7 +174,7 @@ fn build_map_from_record(vals: Vec, config: &Config) -> TableValue { for val in vals { 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 list = get_table_value_column_mut(&mut list[i]); @@ -194,7 +194,7 @@ fn get_table_value_column_mut(val: &mut TableValue) -> &mut Vec { } } -fn get_as_record(val: Value) -> nu_utils::SharedCow { +fn get_as_record(val: Value) -> Record { match val { Value::Record { val, .. } => val, _ => unreachable!(), diff --git a/crates/nu_plugin_polars/src/dataframe/values/nu_dataframe/mod.rs b/crates/nu_plugin_polars/src/dataframe/values/nu_dataframe/mod.rs index de21abafdd..106b1c94cf 100644 --- a/crates/nu_plugin_polars/src/dataframe/values/nu_dataframe/mod.rs +++ b/crates/nu_plugin_polars/src/dataframe/values/nu_dataframe/mod.rs @@ -183,11 +183,9 @@ impl NuDataFrame { conversion::insert_record(&mut column_values, record, &maybe_schema)? } - Value::Record { val: record, .. } => conversion::insert_record( - &mut column_values, - record.into_owned(), - &maybe_schema, - )?, + Value::Record { val: record, .. } => { + conversion::insert_record(&mut column_values, record, &maybe_schema)? + } _ => { let key = "0".to_string(); conversion::insert_value(value, key.into(), &mut column_values, &maybe_schema)? diff --git a/crates/nuon/src/to.rs b/crates/nuon/src/to.rs index 332dcaf98e..643c0650a3 100644 --- a/crates/nuon/src/to.rs +++ b/crates/nuon/src/to.rs @@ -209,7 +209,7 @@ fn value_to_string( }, Value::Record { val, .. } => { let mut collection = vec![]; - for (col, val) in &**val { + for (col, val) in val { let col = if needs_quoting(col) { &escape_quote_string(col) } else {