From 779a3c075e2d7df26d0660b5ed4b8337723f795f Mon Sep 17 00:00:00 2001 From: Ian Manske Date: Wed, 14 Feb 2024 00:14:23 +0000 Subject: [PATCH] Fix error message for `headers` (#11809) # Description Fixes #11780: `headers`: incorrect error --- crates/nu-command/src/filters/headers.rs | 149 ++++++++++++----------- 1 file changed, 76 insertions(+), 73 deletions(-) diff --git a/crates/nu-command/src/filters/headers.rs b/crates/nu-command/src/filters/headers.rs index df024d4da4..2f0eaf1838 100644 --- a/crates/nu-command/src/filters/headers.rs +++ b/crates/nu-command/src/filters/headers.rs @@ -1,8 +1,8 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - record, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, - Value, + record, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, + Type, Value, }; #[derive(Clone)] @@ -69,66 +69,38 @@ impl Command for Headers { ) -> Result { let config = engine_state.get_config(); let metadata = input.metadata(); - let value = input.into_value(call.head); - let (old_headers, new_headers) = extract_headers(&value, config)?; - let new_headers = replace_headers(value, &old_headers, &new_headers)?; + let span = input.span().unwrap_or(call.head); + let value = input.into_value(span); + let Value::List { vals: table, .. } = value else { + return Err(ShellError::TypeMismatch { + err_message: "not a table".to_string(), + span, + }); + }; - Ok(new_headers.into_pipeline_data_with_metadata(metadata)) + let (old_headers, new_headers) = extract_headers(&table, span, config)?; + let value = replace_headers(table, span, &old_headers, &new_headers)?; + + Ok(value.into_pipeline_data_with_metadata(metadata)) } } -fn replace_headers( - value: Value, - old_headers: &[String], - new_headers: &[String], -) -> Result { - let span = value.span(); - match value { - Value::Record { val, .. } => Ok(Value::record( - val.into_iter() - .filter_map(|(col, val)| { - old_headers - .iter() - .position(|c| c == &col) - .map(|i| (new_headers[i].clone(), val)) - }) - .collect(), - span, - )), - Value::List { vals, .. } => { - let vals = vals - .into_iter() - .skip(1) - .map(|value| replace_headers(value, old_headers, new_headers)) - .collect::, ShellError>>()?; - - Ok(Value::list(vals, span)) - } - _ => Err(ShellError::TypeMismatch { - err_message: "record".to_string(), - span: value.span(), - }), - } -} - -fn is_valid_header(value: &Value) -> bool { - matches!( - value, - Value::Nothing { .. } - | Value::String { val: _, .. } - | Value::Bool { val: _, .. } - | Value::Float { val: _, .. } - | Value::Int { val: _, .. } - ) -} - fn extract_headers( - value: &Value, + table: &[Value], + span: Span, config: &Config, ) -> Result<(Vec, Vec), ShellError> { - let span = value.span(); - match value { - Value::Record { val: record, .. } => { + table + .first() + .ok_or_else(|| ShellError::GenericError { + error: "Found empty list".into(), + msg: "unable to extract headers".into(), + span: Some(span), + help: None, + inner: vec![], + }) + .and_then(Value::as_record) + .and_then(|record| { for v in record.values() { if !is_valid_header(v) { return Err(ShellError::TypeMismatch { @@ -151,26 +123,57 @@ fn extract_headers( col } }) - .collect::>(); + .collect(); Ok((old_headers, new_headers)) - } - Value::List { vals, .. } => vals - .iter() - .map(|value| extract_headers(value, config)) - .next() - .ok_or_else(|| ShellError::GenericError { - error: "Found empty list".into(), - msg: "unable to extract headers".into(), - span: Some(span), - help: None, - inner: vec![], - })?, - _ => Err(ShellError::TypeMismatch { - err_message: "record".to_string(), - span: value.span(), - }), - } + }) +} + +fn is_valid_header(value: &Value) -> bool { + matches!( + value, + Value::Nothing { .. } + | Value::String { val: _, .. } + | Value::Bool { val: _, .. } + | Value::Float { val: _, .. } + | Value::Int { val: _, .. } + ) +} + +fn replace_headers( + rows: Vec, + span: Span, + old_headers: &[String], + new_headers: &[String], +) -> Result { + rows.into_iter() + .skip(1) + .map(|value| { + let span = value.span(); + if let Value::Record { val: record, .. } = value { + Ok(Value::record( + record + .into_iter() + .filter_map(|(col, val)| { + old_headers + .iter() + .position(|c| c == &col) + .map(|i| (new_headers[i].clone(), val)) + }) + .collect(), + span, + )) + } else { + Err(ShellError::CantConvert { + to_type: "record".into(), + from_type: value.get_type().to_string(), + span, + help: None, + }) + } + }) + .collect::>() + .map(|rows| Value::list(rows, span)) } #[cfg(test)]