Make Record.cols private (#12317)

# Description
Makes the `cols` field in `Record` private and fixes the implementation
of `rename` to account for this change.
This commit is contained in:
Ian Manske 2024-03-28 20:18:43 +00:00 committed by GitHub
parent e97368433b
commit 442faa5576
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 73 additions and 65 deletions

View File

@ -1,7 +1,6 @@
use indexmap::IndexMap; use indexmap::IndexMap;
use nu_engine::{command_prelude::*, get_eval_block_with_early_return}; use nu_engine::{command_prelude::*, get_eval_block_with_early_return};
use nu_protocol::engine::Closure; use nu_protocol::engine::Closure;
use std::collections::HashSet;
#[derive(Clone)] #[derive(Clone)]
pub struct Rename; pub struct Rename;
@ -157,72 +156,85 @@ fn rename(
move |item| { move |item| {
let span = item.span(); let span = item.span();
match item { match item {
Value::Record { Value::Record { val: record, .. } => {
val: mut record, .. let record =
} => { if let Some((engine_state, block, mut stack, env_vars, env_hidden)) =
if let Some((engine_state, block, mut stack, env_vars, env_hidden)) = block_info.clone()
block_info.clone() {
{ record
for c in &mut record.cols { .into_iter()
stack.with_env(&env_vars, &env_hidden); .map(|(col, val)| {
stack.with_env(&env_vars, &env_hidden);
if let Some(var) = block.signature.get_positional(0) { if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id { if let Some(var_id) = &var.var_id {
stack.add_var(*var_id, Value::string(c.clone(), span)) stack.add_var(
} *var_id,
} Value::string(col.clone(), span),
)
}
}
let eval_result = eval_block_with_early_return( eval_block_with_early_return(
&engine_state, &engine_state,
&mut stack, &mut stack,
&block, &block,
Value::string(c.clone(), span).into_pipeline_data(), Value::string(col, span).into_pipeline_data(),
); )
.and_then(|data| data.collect_string_strict(span))
.map(|(col, _, _)| (col, val))
})
.collect::<Result<Record, _>>()
} else {
match &specified_column {
Some(columns) => {
// 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_iter().map(|(col, val)| {
let col = if let Some(col) = columns.get(&col) {
renamed += 1;
col.clone()
} else {
col
};
match eval_result { (col, val)
Err(e) => return Value::error(e, span), }).collect::<Record>();
Ok(res) => match res.collect_string_strict(span) {
Err(e) => return Value::error(e, span), let missing_column = if renamed < columns.len() {
Ok(new_c) => *c = new_c.0, columns.iter().find_map(|(col, new_col)| {
}, (!record.contains(new_col)).then_some(col)
} })
} } else {
} else { None
match &specified_column { };
Some(c) => {
let mut column_to_rename: HashSet<String> = HashSet::from_iter(c.keys().cloned()); if let Some(missing) = missing_column {
for val in record.cols.iter_mut() { Err(ShellError::UnsupportedInput {
if c.contains_key(val) { msg: format!("The column '{missing}' does not exist in the input"),
column_to_rename.remove(val); input: "value originated from here".into(),
*val = c.get(val).expect("already check exists").to_owned(); msg_span: head_span,
input_span: span,
})
} else {
Ok(record)
} }
} }
if !column_to_rename.is_empty() { None => Ok(record
let not_exists_column = .into_iter()
column_to_rename.into_iter().next().expect( .enumerate()
"already checked column to rename still exists", .map(|(i, (col, val))| {
); (columns.get(i).cloned().unwrap_or(col), val)
return Value::error( })
ShellError::UnsupportedInput { msg: format!( .collect()),
"The column '{not_exists_column}' does not exist in the input",
), input: "value originated from here".into(), msg_span: head_span, input_span: span },
span,
);
}
} }
None => { };
for (idx, val) in columns.iter().enumerate() {
if idx >= record.len() { match record {
// skip extra new columns names if we already reached the final column Ok(record) => Value::record(record, span),
break; Err(err) => Value::error(err, span),
}
record.cols[idx] = val.clone();
}
}
}
} }
Value::record(*record, span)
} }
// Propagate errors by explicitly matching them before the final case. // Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => item.clone(), Value::Error { .. } => item.clone(),

View File

@ -6,11 +6,7 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Record { pub struct Record {
/// Don't use this field publicly! cols: Vec<String>,
///
/// Only public as command `rename` is not reimplemented in a sane way yet
/// Using it or making `vals` public will draw shaming by @sholderbach
pub cols: Vec<String>,
vals: Vec<Value>, vals: Vec<Value>,
} }