Fix error message for headers (#11809)

# Description
Fixes #11780: `headers`: incorrect error
This commit is contained in:
Ian Manske 2024-02-14 00:14:23 +00:00 committed by GitHub
parent d77eeeb5dd
commit 779a3c075e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,8 +1,8 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
record, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, record, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
Value, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -69,66 +69,38 @@ impl Command for Headers {
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let config = engine_state.get_config(); let config = engine_state.get_config();
let metadata = input.metadata(); let metadata = input.metadata();
let value = input.into_value(call.head); let span = input.span().unwrap_or(call.head);
let (old_headers, new_headers) = extract_headers(&value, config)?; let value = input.into_value(span);
let new_headers = replace_headers(value, &old_headers, &new_headers)?; 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<Value, ShellError> {
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::<Result<Vec<Value>, 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( fn extract_headers(
value: &Value, table: &[Value],
span: Span,
config: &Config, config: &Config,
) -> Result<(Vec<String>, Vec<String>), ShellError> { ) -> Result<(Vec<String>, Vec<String>), ShellError> {
let span = value.span(); table
match value { .first()
Value::Record { val: record, .. } => { .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() { for v in record.values() {
if !is_valid_header(v) { if !is_valid_header(v) {
return Err(ShellError::TypeMismatch { return Err(ShellError::TypeMismatch {
@ -151,26 +123,57 @@ fn extract_headers(
col col
} }
}) })
.collect::<Vec<String>>(); .collect();
Ok((old_headers, new_headers)) Ok((old_headers, new_headers))
} })
Value::List { vals, .. } => vals }
.iter()
.map(|value| extract_headers(value, config)) fn is_valid_header(value: &Value) -> bool {
.next() matches!(
.ok_or_else(|| ShellError::GenericError { value,
error: "Found empty list".into(), Value::Nothing { .. }
msg: "unable to extract headers".into(), | Value::String { val: _, .. }
span: Some(span), | Value::Bool { val: _, .. }
help: None, | Value::Float { val: _, .. }
inner: vec![], | Value::Int { val: _, .. }
})?, )
_ => Err(ShellError::TypeMismatch { }
err_message: "record".to_string(),
span: value.span(), fn replace_headers(
}), rows: Vec<Value>,
} span: Span,
old_headers: &[String],
new_headers: &[String],
) -> Result<Value, ShellError> {
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::<Result<_, _>>()
.map(|rows| Value::list(rows, span))
} }
#[cfg(test)] #[cfg(test)]