diff --git a/crates/nu-command/src/database/values/dsl/expression.rs b/crates/nu-command/src/database/values/dsl/expression.rs index e2e57f9a49..3fbbd99fa8 100644 --- a/crates/nu-command/src/database/values/dsl/expression.rs +++ b/crates/nu-command/src/database/values/dsl/expression.rs @@ -52,7 +52,7 @@ impl CustomValue for ExprDb { fn follow_path_int(&self, count: usize, span: Span) -> Result { let path = PathMember::Int { val: count, span }; - ExprDb::expr_to_value(self.as_ref(), span).follow_cell_path(&[path]) + ExprDb::expr_to_value(self.as_ref(), span).follow_cell_path(&[path], false) } fn follow_path_string(&self, column_name: String, span: Span) -> Result { @@ -61,7 +61,7 @@ impl CustomValue for ExprDb { span, }; - ExprDb::expr_to_value(self.as_ref(), span).follow_cell_path(&[path]) + ExprDb::expr_to_value(self.as_ref(), span).follow_cell_path(&[path], false) } fn typetag_name(&self) -> &'static str { diff --git a/crates/nu-command/src/database/values/dsl/select_item.rs b/crates/nu-command/src/database/values/dsl/select_item.rs index 55ad7a5f06..e140f379cb 100644 --- a/crates/nu-command/src/database/values/dsl/select_item.rs +++ b/crates/nu-command/src/database/values/dsl/select_item.rs @@ -56,7 +56,7 @@ impl CustomValue for SelectDb { fn follow_path_int(&self, count: usize, span: Span) -> Result { let path = PathMember::Int { val: count, span }; - SelectDb::select_to_value(self.as_ref(), span).follow_cell_path(&[path]) + SelectDb::select_to_value(self.as_ref(), span).follow_cell_path(&[path], false) } fn follow_path_string(&self, column_name: String, span: Span) -> Result { @@ -64,7 +64,7 @@ impl CustomValue for SelectDb { val: column_name, span, }; - SelectDb::select_to_value(self.as_ref(), span).follow_cell_path(&[path]) + SelectDb::select_to_value(self.as_ref(), span).follow_cell_path(&[path], false) } fn typetag_name(&self) -> &'static str { diff --git a/crates/nu-command/src/filters/drop/column.rs b/crates/nu-command/src/filters/drop/column.rs index 2437ac1756..ae9a32179e 100644 --- a/crates/nu-command/src/filters/drop/column.rs +++ b/crates/nu-command/src/filters/drop/column.rs @@ -101,7 +101,7 @@ fn dropcol( let mut vals = vec![]; for path in &keep_columns { - let fetcher = input_val.clone().follow_cell_path(&path.members)?; + let fetcher = input_val.clone().follow_cell_path(&path.members, false)?; cols.push(path.into_string()); vals.push(fetcher); } @@ -125,7 +125,7 @@ fn dropcol( let mut vals = vec![]; for path in &keep_columns { - let fetcher = input_val.clone().follow_cell_path(&path.members)?; + let fetcher = input_val.clone().follow_cell_path(&path.members, false)?; cols.push(path.into_string()); vals.push(fetcher); } @@ -141,7 +141,7 @@ fn dropcol( let mut vals = vec![]; for cell_path in &keep_columns { - let result = v.clone().follow_cell_path(&cell_path.members)?; + let result = v.clone().follow_cell_path(&cell_path.members, false)?; cols.push(cell_path.into_string()); vals.push(result); diff --git a/crates/nu-command/src/filters/empty.rs b/crates/nu-command/src/filters/empty.rs index 007ff77d65..5b5866887a 100644 --- a/crates/nu-command/src/filters/empty.rs +++ b/crates/nu-command/src/filters/empty.rs @@ -81,7 +81,7 @@ fn empty( for val in input { for column in &columns { let val = val.clone(); - match val.follow_cell_path(&column.members) { + match val.follow_cell_path(&column.members, false) { Ok(Value::Nothing { .. }) => {} Ok(_) => { return Ok(Value::Bool { diff --git a/crates/nu-command/src/filters/get.rs b/crates/nu-command/src/filters/get.rs index 2c43318a0d..fd7f52675c 100644 --- a/crates/nu-command/src/filters/get.rs +++ b/crates/nu-command/src/filters/get.rs @@ -31,6 +31,11 @@ impl Command for Get { "return nothing if path can't be found", Some('i'), ) + .switch( + "sensitive", + "get path in a case sensitive manner", + Some('s'), + ) .category(Category::Filters) } @@ -44,13 +49,14 @@ impl Command for Get { let span = call.head; let cell_path: CellPath = call.req(engine_state, stack, 0)?; let rest: Vec = call.rest(engine_state, stack, 1)?; + let sensitive = call.has_flag("sensitive"); let ignore_errors = call.has_flag("ignore-errors"); let ctrlc = engine_state.ctrlc.clone(); let metadata = input.metadata(); if rest.is_empty() { let output = input - .follow_cell_path(&cell_path.members, call.head) + .follow_cell_path(&cell_path.members, call.head, !sensitive) .map(|x| x.into_pipeline_data()); if ignore_errors { @@ -69,7 +75,7 @@ impl Command for Get { let input = input.into_value(span); for path in paths { - let val = input.clone().follow_cell_path(&path.members); + let val = input.clone().follow_cell_path(&path.members, !sensitive); if ignore_errors { if let Ok(val) = val { @@ -106,6 +112,16 @@ impl Command for Get { example: "sys | get cpu", result: None, }, + Example { + description: "Getting Path/PATH in a case insensitive way", + example: "$env | get paTH", + result: None, + }, + Example { + description: "Getting Path in a case sensitive way, won't work for 'PATH'", + example: "$env | get -s Path", + result: None, + }, ] } } diff --git a/crates/nu-command/src/filters/reject.rs b/crates/nu-command/src/filters/reject.rs index f36a193128..f86818a181 100644 --- a/crates/nu-command/src/filters/reject.rs +++ b/crates/nu-command/src/filters/reject.rs @@ -94,7 +94,7 @@ fn reject( let mut vals = vec![]; for path in &keep_columns { - let fetcher = input_val.clone().follow_cell_path(&path.members); + let fetcher = input_val.clone().follow_cell_path(&path.members, false); if let Ok(value) = fetcher { cols.push(path.into_string()); @@ -135,7 +135,7 @@ fn reject( let mut vals = vec![]; for path in &keep_columns { - let fetcher = input_val.clone().follow_cell_path(&path.members)?; + let fetcher = input_val.clone().follow_cell_path(&path.members, false)?; cols.push(path.into_string()); vals.push(fetcher); } @@ -151,7 +151,7 @@ fn reject( let mut vals = vec![]; for cell_path in &keep_columns { - let result = v.clone().follow_cell_path(&cell_path.members)?; + let result = v.clone().follow_cell_path(&cell_path.members, false)?; cols.push(cell_path.into_string()); vals.push(result); diff --git a/crates/nu-command/src/filters/select.rs b/crates/nu-command/src/filters/select.rs index f19d437686..a7d786ce73 100644 --- a/crates/nu-command/src/filters/select.rs +++ b/crates/nu-command/src/filters/select.rs @@ -124,7 +124,7 @@ fn select( let mut vals = vec![]; for path in &columns { //FIXME: improve implementation to not clone - let fetcher = input_val.clone().follow_cell_path(&path.members)?; + let fetcher = input_val.clone().follow_cell_path(&path.members, false)?; cols.push(path.into_string().replace('.', "_")); vals.push(fetcher); @@ -148,7 +148,7 @@ fn select( let mut vals = vec![]; for path in &columns { //FIXME: improve implementation to not clone - match x.clone().follow_cell_path(&path.members) { + match x.clone().follow_cell_path(&path.members, false) { Ok(value) => { cols.push(path.into_string().replace('.', "_")); vals.push(value); @@ -173,7 +173,7 @@ fn select( for cell_path in columns { // FIXME: remove clone - let result = v.clone().follow_cell_path(&cell_path.members)?; + let result = v.clone().follow_cell_path(&cell_path.members, false)?; cols.push(cell_path.into_string().replace('.', "_")); vals.push(result); diff --git a/crates/nu-command/src/strings/format/command.rs b/crates/nu-command/src/strings/format/command.rs index d7de9454b2..df72c095fc 100644 --- a/crates/nu-command/src/strings/format/command.rs +++ b/crates/nu-command/src/strings/format/command.rs @@ -261,7 +261,7 @@ fn format_record( span: *span, }) .collect(); - match data_as_value.clone().follow_cell_path(&path_members) { + match data_as_value.clone().follow_cell_path(&path_members, false) { Ok(value_at_column) => { output.push_str(value_at_column.into_string(", ", config).as_str()) } diff --git a/crates/nu-command/src/viewers/griddle.rs b/crates/nu-command/src/viewers/griddle.rs index bc7f3be6cb..0b92cf01f0 100644 --- a/crates/nu-command/src/viewers/griddle.rs +++ b/crates/nu-command/src/viewers/griddle.rs @@ -310,12 +310,13 @@ fn convert_to_list( } else { for header in headers.iter().skip(1) { let result = match item { - Value::Record { .. } => { - item.clone().follow_cell_path(&[PathMember::String { + Value::Record { .. } => item.clone().follow_cell_path( + &[PathMember::String { val: header.into(), span: head, - }]) - } + }], + false, + ), _ => Ok(item.clone()), }; diff --git a/crates/nu-command/src/viewers/table.rs b/crates/nu-command/src/viewers/table.rs index e8120841b2..26f640db3b 100644 --- a/crates/nu-command/src/viewers/table.rs +++ b/crates/nu-command/src/viewers/table.rs @@ -386,12 +386,13 @@ fn convert_to_table( let skip_num = if !disable_index { 1 } else { 0 }; for header in headers.iter().skip(skip_num) { let result = match item { - Value::Record { .. } => { - item.clone().follow_cell_path(&[PathMember::String { + Value::Record { .. } => item.clone().follow_cell_path( + &[PathMember::String { val: header.into(), span: head, - }]) - } + }], + false, + ), _ => Ok(item.clone()), }; diff --git a/crates/nu-engine/src/env.rs b/crates/nu-engine/src/env.rs index 62e89abbd0..48667b6966 100644 --- a/crates/nu-engine/src/env.rs +++ b/crates/nu-engine/src/env.rs @@ -262,7 +262,7 @@ fn get_converted_value( val: block_id, span: from_span, .. - }) = env_conversions.follow_cell_path(path_members) + }) = env_conversions.follow_cell_path(path_members, false) { let block = engine_state.get_block(block_id); diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 1eaa758143..0fe2be81e0 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -295,7 +295,7 @@ pub fn eval_expression( Expr::FullCellPath(cell_path) => { let value = eval_expression(engine_state, stack, &cell_path.head)?; - value.follow_cell_path(&cell_path.tail) + value.follow_cell_path(&cell_path.tail, false) } Expr::ImportPattern(_) => Ok(Value::Nothing { span: expr.span }), Expr::Call(call) => { diff --git a/crates/nu-protocol/src/pipeline_data.rs b/crates/nu-protocol/src/pipeline_data.rs index db16540d7c..3dba1628a3 100644 --- a/crates/nu-protocol/src/pipeline_data.rs +++ b/crates/nu-protocol/src/pipeline_data.rs @@ -217,6 +217,7 @@ impl PipelineData { self, cell_path: &[PathMember], head: Span, + insensitive: bool, ) -> Result { match self { // FIXME: there are probably better ways of doing this @@ -224,8 +225,8 @@ impl PipelineData { vals: stream.collect(), span: head, } - .follow_cell_path(cell_path), - PipelineData::Value(v, ..) => v.follow_cell_path(cell_path), + .follow_cell_path(cell_path, insensitive), + PipelineData::Value(v, ..) => v.follow_cell_path(cell_path, insensitive), _ => Err(ShellError::IOError("can't follow stream paths".into())), } } diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index f36815d149..303fb3d1cb 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -605,7 +605,11 @@ impl Value { } /// Follow a given column path into the value: for example accessing select elements in a stream or list - pub fn follow_cell_path(self, cell_path: &[PathMember]) -> Result { + pub fn follow_cell_path( + self, + cell_path: &[PathMember], + insensitive: bool, + ) -> Result { let mut current = self; for member in cell_path { // FIXME: this uses a few extra clones for simplicity, but there may be a way @@ -661,12 +665,13 @@ impl Value { let span = *span; // Make reverse iterate to avoid duplicate column leads to first value, actuall last value is expected. - if let Some(found) = cols - .iter() - .zip(vals.iter()) - .rev() - .find(|x| x.0 == column_name) - { + if let Some(found) = cols.iter().zip(vals.iter()).rev().find(|x| { + if insensitive { + x.0.to_lowercase() == column_name.to_lowercase() + } else { + x.0 == column_name + } + }) { current = found.1.clone(); } else if let Some(suggestion) = did_you_mean(&cols, column_name) { return Err(ShellError::DidYouMean(suggestion, *origin_span)); @@ -679,10 +684,13 @@ impl Value { let mut hasvalue = false; let mut temp: Result = Err(ShellError::NotFound(*span)); for val in vals { - temp = val.clone().follow_cell_path(&[PathMember::String { - val: column_name.clone(), - span: *origin_span, - }]); + temp = val.clone().follow_cell_path( + &[PathMember::String { + val: column_name.clone(), + span: *origin_span, + }], + insensitive, + ); if let Ok(result) = temp.clone() { hasvalue = true; output.push(result); @@ -723,7 +731,7 @@ impl Value { ) -> Result<(), ShellError> { let orig = self.clone(); - let new_val = callback(&orig.follow_cell_path(cell_path)?); + let new_val = callback(&orig.follow_cell_path(cell_path, false)?); match new_val { Value::Error { error } => Err(error), @@ -834,7 +842,7 @@ impl Value { ) -> Result<(), ShellError> { let orig = self.clone(); - let new_val = callback(&orig.follow_cell_path(cell_path)?); + let new_val = callback(&orig.follow_cell_path(cell_path, false)?); match new_val { Value::Error { error } => Err(error), diff --git a/crates/nu_plugin_inc/src/inc.rs b/crates/nu_plugin_inc/src/inc.rs index 1162b9cab0..d42e667647 100644 --- a/crates/nu_plugin_inc/src/inc.rs +++ b/crates/nu_plugin_inc/src/inc.rs @@ -86,7 +86,7 @@ impl Inc { pub fn inc(&self, head: Span, value: &Value) -> Result { if let Some(cell_path) = &self.cell_path { let working_value = value.clone(); - let cell_value = working_value.follow_cell_path(&cell_path.members)?; + let cell_value = working_value.follow_cell_path(&cell_path.members, false)?; let cell_value = self.inc_value(head, &cell_value)?; diff --git a/src/tests/test_table_operations.rs b/src/tests/test_table_operations.rs index 661bb6c40a..ce590eafd7 100644 --- a/src/tests/test_table_operations.rs +++ b/src/tests/test_table_operations.rs @@ -260,3 +260,11 @@ fn length_defaulted_columns() -> TestResult { fn get_fuzzy() -> TestResult { run_test("(ls | get -i foo) == $nothing", "true") } + +#[test] +fn get_insensitive() -> TestResult { + run_test( + r#"[[name, age]; [a, 1] [b, 2]] | get NAmE | select 0 | get 0"#, + "a", + ) +}