mirror of
https://github.com/nushell/nushell.git
synced 2025-06-30 14:40:06 +02:00
# Description This closes #7498, as well as fixes an issue reported in https://github.com/nushell/nushell/pull/7002#issuecomment-1368340773 BEFORE: ``` 〉[{foo: 'bar'} {}] | get foo Error: nu:🐚:column_not_found (link) × Cannot find column ╭─[entry #5:1:1] 1 │ [{foo: 'bar'} {}] | get foo · ────────┬──────── ─┬─ · │ ╰── value originates here · ╰── cannot find column 'Empty cell' ╰──── 〉[{foo: 'bar'} {}].foo ╭───┬─────╮ │ 0 │ bar │ │ 1 │ │ ╰───┴─────╯ ``` AFTER: ``` 〉[{foo: 'bar'} {}] | get foo Error: nu:🐚:column_not_found (link) × Cannot find column ╭─[entry #1:1:1] 1 │ [{foo: 'bar'} {}] | get foo · ─┬ ─┬─ · │ ╰── cannot find column 'foo' · ╰── value originates here ╰──── 〉[{foo: 'bar'} {}].foo Error: nu:🐚:column_not_found (link) × Cannot find column ╭─[entry #3:1:1] 1 │ [{foo: 'bar'} {}].foo · ─┬ ─┬─ · │ ╰── cannot find column 'foo' · ╰── value originates here ╰──── ``` EDIT: This also changes the semantics of `get`/`select` `-i` somewhat. I've decided to leave it like this because it works more intuitively with `default` and `compact`. BEFORE: ``` 〉[{a:1} {b:2} {a:3}] | select -i foo | to nuon null ``` AFTER: ``` 〉[{a:1} {b:2} {a:3}] | select -i foo | to nuon [[foo]; [null], [null], [null]] ``` # User-Facing Changes See above. EDIT: the issue with holes in cases like ` [{foo: 'bar'} {}].foo.0` versus ` [{foo: 'bar'} {}].0.foo` has been resolved. # Tests + Formatting Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass # After Submitting If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date.
This commit is contained in:
@ -109,7 +109,10 @@ fn dropcol(
|
||||
let mut vals = vec![];
|
||||
|
||||
for path in &keep_columns {
|
||||
let fetcher = input_val.clone().follow_cell_path(&path.members, false)?;
|
||||
let fetcher =
|
||||
input_val
|
||||
.clone()
|
||||
.follow_cell_path(&path.members, false, false)?;
|
||||
cols.push(path.into_string());
|
||||
vals.push(fetcher);
|
||||
}
|
||||
@ -133,7 +136,10 @@ fn dropcol(
|
||||
let mut vals = vec![];
|
||||
|
||||
for path in &keep_columns {
|
||||
let fetcher = input_val.clone().follow_cell_path(&path.members, false)?;
|
||||
let fetcher =
|
||||
input_val
|
||||
.clone()
|
||||
.follow_cell_path(&path.members, false, false)?;
|
||||
cols.push(path.into_string());
|
||||
vals.push(fetcher);
|
||||
}
|
||||
@ -149,7 +155,9 @@ fn dropcol(
|
||||
let mut vals = vec![];
|
||||
|
||||
for cell_path in &keep_columns {
|
||||
let result = v.clone().follow_cell_path(&cell_path.members, false)?;
|
||||
let result = v
|
||||
.clone()
|
||||
.follow_cell_path(&cell_path.members, false, false)?;
|
||||
|
||||
cols.push(cell_path.into_string());
|
||||
vals.push(result);
|
||||
|
@ -73,7 +73,7 @@ fn empty(
|
||||
for val in input {
|
||||
for column in &columns {
|
||||
let val = val.clone();
|
||||
match val.follow_cell_path(&column.members, false) {
|
||||
match val.follow_cell_path(&column.members, false, false) {
|
||||
Ok(Value::Nothing { .. }) => {}
|
||||
Ok(_) => return Ok(Value::boolean(false, head).into_pipeline_data()),
|
||||
Err(err) => return Err(err),
|
||||
|
@ -64,40 +64,9 @@ impl Command for Get {
|
||||
let metadata = input.metadata();
|
||||
|
||||
if rest.is_empty() {
|
||||
let output = input
|
||||
.follow_cell_path(&cell_path.members, call.head, !sensitive)
|
||||
.map(|x| x.into_pipeline_data());
|
||||
|
||||
if ignore_errors {
|
||||
match output {
|
||||
Ok(output) => Ok(output),
|
||||
Err(_) => Ok(Value::Nothing { span: call.head }.into_pipeline_data()),
|
||||
}
|
||||
} else {
|
||||
match output {
|
||||
Ok(val) => {
|
||||
let val_check = val.into_value(span);
|
||||
match val_check {
|
||||
Value::List {
|
||||
ref vals,
|
||||
span: spanchild,
|
||||
} => {
|
||||
if vals.iter().any(|unit| unit.is_empty()) {
|
||||
Err(nu_protocol::ShellError::CantFindColumn(
|
||||
"Empty cell".to_string(),
|
||||
spanchild,
|
||||
span,
|
||||
))
|
||||
} else {
|
||||
Ok(val_check.into_pipeline_data())
|
||||
}
|
||||
}
|
||||
val => Ok(val.into_pipeline_data()),
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
input
|
||||
.follow_cell_path(&cell_path.members, call.head, !sensitive, ignore_errors)
|
||||
.map(|x| x.into_pipeline_data())
|
||||
} else {
|
||||
let mut output = vec![];
|
||||
|
||||
@ -106,25 +75,11 @@ impl Command for Get {
|
||||
let input = input.into_value(span);
|
||||
|
||||
for path in paths {
|
||||
let val = input.clone().follow_cell_path(&path.members, !sensitive);
|
||||
let val = input
|
||||
.clone()
|
||||
.follow_cell_path(&path.members, !sensitive, false);
|
||||
|
||||
if ignore_errors {
|
||||
match val {
|
||||
Ok(Value::Nothing { span: spanchild }) => {
|
||||
return Err(nu_protocol::ShellError::CantFindColumn(
|
||||
"Nothing".to_string(),
|
||||
spanchild,
|
||||
span,
|
||||
));
|
||||
}
|
||||
Ok(val) => {
|
||||
output.push(val);
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
} else {
|
||||
output.push(val?);
|
||||
}
|
||||
output.push(val?);
|
||||
}
|
||||
|
||||
Ok(output.into_iter().into_pipeline_data(ctrlc))
|
||||
@ -152,17 +107,13 @@ impl Command for Get {
|
||||
result: Some(Value::test_string("A0")),
|
||||
},
|
||||
Example {
|
||||
description: "Extract the name of files as a list",
|
||||
example: "ls | get name",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Extract the name of the 3rd entry of a file list",
|
||||
description:
|
||||
"Extract the name of the 3rd record in a list (same as `ls | $in.name`)",
|
||||
example: "ls | get name.2",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Extract the name of the 3rd entry of a file list (alternative)",
|
||||
description: "Extract the name of the 3rd record in a list",
|
||||
example: "ls | get 2.name",
|
||||
result: None,
|
||||
},
|
||||
|
@ -81,7 +81,17 @@ fn length_row(call: &Call, input: PipelineData) -> Result<PipelineData, ShellErr
|
||||
PipelineData::Value(Value::Nothing { .. }, ..) => {
|
||||
Ok(Value::int(0, call.head).into_pipeline_data())
|
||||
}
|
||||
_ => Ok(Value::int(input.into_iter().count() as i64, call.head).into_pipeline_data()),
|
||||
_ => {
|
||||
let mut count: i64 = 0;
|
||||
// Check for and propagate errors
|
||||
for value in input.into_iter() {
|
||||
if let Value::Error { error } = value {
|
||||
return Err(error);
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
Ok(Value::int(count, call.head).into_pipeline_data())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,11 @@ fn select(
|
||||
let mut vals = vec![];
|
||||
for path in &columns {
|
||||
//FIXME: improve implementation to not clone
|
||||
match input_val.clone().follow_cell_path(&path.members, false) {
|
||||
match input_val.clone().follow_cell_path(
|
||||
&path.members,
|
||||
false,
|
||||
ignore_errors,
|
||||
) {
|
||||
Ok(fetcher) => {
|
||||
allempty = false;
|
||||
cols.push(path.into_string().replace('.', "_"));
|
||||
@ -166,12 +170,7 @@ fn select(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if ignore_errors {
|
||||
cols.push(path.into_string().replace('.', "_"));
|
||||
vals.push(Value::Nothing { span })
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -199,17 +198,15 @@ fn select(
|
||||
let mut vals = vec![];
|
||||
for path in &columns {
|
||||
//FIXME: improve implementation to not clone
|
||||
match x.clone().follow_cell_path(&path.members, false) {
|
||||
match x
|
||||
.clone()
|
||||
.follow_cell_path(&path.members, false, ignore_errors)
|
||||
{
|
||||
Ok(value) => {
|
||||
cols.push(path.into_string().replace('.', "_"));
|
||||
vals.push(value);
|
||||
}
|
||||
Err(e) => {
|
||||
if ignore_errors {
|
||||
return Ok(Value::nothing(call_span).into_pipeline_data());
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
values.push(Value::Record {
|
||||
@ -233,18 +230,15 @@ fn select(
|
||||
|
||||
for cell_path in columns {
|
||||
// FIXME: remove clone
|
||||
match v.clone().follow_cell_path(&cell_path.members, false) {
|
||||
match v
|
||||
.clone()
|
||||
.follow_cell_path(&cell_path.members, false, ignore_errors)
|
||||
{
|
||||
Ok(result) => {
|
||||
cols.push(cell_path.into_string().replace('.', "_"));
|
||||
vals.push(result);
|
||||
}
|
||||
Err(e) => {
|
||||
if ignore_errors {
|
||||
return Ok(Value::nothing(call_span).into_pipeline_data());
|
||||
}
|
||||
|
||||
return Err(e);
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,7 +282,10 @@ fn format_record(
|
||||
span: *span,
|
||||
})
|
||||
.collect();
|
||||
match data_as_value.clone().follow_cell_path(&path_members, false) {
|
||||
match data_as_value
|
||||
.clone()
|
||||
.follow_cell_path(&path_members, false, false)
|
||||
{
|
||||
Ok(value_at_column) => {
|
||||
output.push_str(value_at_column.into_string(", ", config).as_str())
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ prints out the list properly."#
|
||||
match input {
|
||||
PipelineData::Value(Value::List { vals, .. }, ..) => {
|
||||
// dbg!("value::list");
|
||||
let data = convert_to_list(vals, config, call.head);
|
||||
let data = convert_to_list(vals, config, call.head)?;
|
||||
if let Some(items) = data {
|
||||
Ok(create_grid_output(
|
||||
items,
|
||||
@ -93,7 +93,7 @@ prints out the list properly."#
|
||||
}
|
||||
PipelineData::ListStream(stream, ..) => {
|
||||
// dbg!("value::stream");
|
||||
let data = convert_to_list(stream, config, call.head);
|
||||
let data = convert_to_list(stream, config, call.head)?;
|
||||
if let Some(items) = data {
|
||||
Ok(create_grid_output(
|
||||
items,
|
||||
@ -247,11 +247,12 @@ fn create_grid_output(
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn convert_to_list(
|
||||
iter: impl IntoIterator<Item = Value>,
|
||||
config: &Config,
|
||||
head: Span,
|
||||
) -> Option<Vec<(usize, String, String)>> {
|
||||
) -> Result<Option<Vec<(usize, String, String)>>, ShellError> {
|
||||
let mut iter = iter.into_iter().peekable();
|
||||
|
||||
if let Some(first) = iter.peek() {
|
||||
@ -267,7 +268,7 @@ fn convert_to_list(
|
||||
let mut row = vec![row_num.to_string()];
|
||||
|
||||
if headers.is_empty() {
|
||||
row.push(item.into_string(", ", config))
|
||||
row.push(item.nonerror_into_string(", ", config)?)
|
||||
} else {
|
||||
for header in headers.iter().skip(1) {
|
||||
let result = match item {
|
||||
@ -277,12 +278,13 @@ fn convert_to_list(
|
||||
span: head,
|
||||
}],
|
||||
false,
|
||||
false,
|
||||
),
|
||||
_ => Ok(item.clone()),
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(value) => row.push(value.into_string(", ", config)),
|
||||
Ok(value) => row.push(value.nonerror_into_string(", ", config)?),
|
||||
Err(_) => row.push(String::new()),
|
||||
}
|
||||
}
|
||||
@ -315,9 +317,9 @@ fn convert_to_list(
|
||||
}
|
||||
}
|
||||
|
||||
Some(interleaved)
|
||||
Ok(Some(interleaved))
|
||||
} else {
|
||||
None
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,10 +5,10 @@ use nu_engine::{column::get_columns, env_to_string, CallExt};
|
||||
use nu_protocol::TrimStrategy;
|
||||
use nu_protocol::{
|
||||
ast::{Call, PathMember},
|
||||
engine::{Command, EngineState, Stack, StateWorkingSet},
|
||||
format_error, Category, Config, DataSource, Example, FooterMode, IntoPipelineData, ListStream,
|
||||
PipelineData, PipelineMetadata, RawStream, ShellError, Signature, Span, SyntaxShape,
|
||||
TableIndexMode, Type, Value,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Config, DataSource, Example, FooterMode, IntoPipelineData, ListStream, PipelineData,
|
||||
PipelineMetadata, RawStream, ShellError, Signature, Span, SyntaxShape, TableIndexMode, Type,
|
||||
Value,
|
||||
};
|
||||
use nu_table::{string_width, Table as NuTable, TableConfig, TableTheme};
|
||||
use nu_utils::get_ls_colors;
|
||||
@ -331,12 +331,9 @@ fn handle_table_command(
|
||||
Ok(val.into_pipeline_data())
|
||||
}
|
||||
PipelineData::Value(Value::Error { error }, ..) => {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
Ok(Value::String {
|
||||
val: format_error(&working_set, &error),
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
// Propagate this error outward, so that it goes to stderr
|
||||
// instead of stdout.
|
||||
Err(error)
|
||||
}
|
||||
PipelineData::Value(Value::CustomValue { val, span }, ..) => {
|
||||
let base_pipeline = val.to_base_value(span)?.into_pipeline_data();
|
||||
@ -903,7 +900,7 @@ fn convert_to_table(
|
||||
val: text.clone(),
|
||||
span: head,
|
||||
};
|
||||
let val = item.clone().follow_cell_path(&[path], false);
|
||||
let val = item.clone().follow_cell_path(&[path], false, false);
|
||||
|
||||
match val {
|
||||
Ok(val) => DeferredStyleComputation::Value { value: val },
|
||||
@ -1264,7 +1261,7 @@ fn create_table2_entry_basic(
|
||||
Value::Record { .. } => {
|
||||
let val = header.to_owned();
|
||||
let path = PathMember::String { val, span: head };
|
||||
let val = item.clone().follow_cell_path(&[path], false);
|
||||
let val = item.clone().follow_cell_path(&[path], false, false);
|
||||
|
||||
match val {
|
||||
Ok(val) => value_to_styled_string(&val, config, style_computer),
|
||||
@ -1292,7 +1289,7 @@ fn create_table2_entry(
|
||||
Value::Record { .. } => {
|
||||
let val = header.to_owned();
|
||||
let path = PathMember::String { val, span: head };
|
||||
let val = item.clone().follow_cell_path(&[path], false);
|
||||
let val = item.clone().follow_cell_path(&[path], false, false);
|
||||
|
||||
match val {
|
||||
Ok(val) => convert_to_table2_entry(
|
||||
|
Reference in New Issue
Block a user