feat: Add sensitive flag to get, fix #4295 (#5685)

* feat: Add insensitive flag to get, fix #4295

* add get insensitive example

* Fix get flags

* Update get examples
This commit is contained in:
Justin Ma 2022-06-01 21:34:42 +08:00 committed by GitHub
parent b79abdb2a5
commit d44059c36b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 78 additions and 43 deletions

View file

@ -52,7 +52,7 @@ impl CustomValue for ExprDb {
fn follow_path_int(&self, count: usize, span: Span) -> Result<Value, ShellError> { fn follow_path_int(&self, count: usize, span: Span) -> Result<Value, ShellError> {
let path = PathMember::Int { val: count, span }; 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<Value, ShellError> { fn follow_path_string(&self, column_name: String, span: Span) -> Result<Value, ShellError> {
@ -61,7 +61,7 @@ impl CustomValue for ExprDb {
span, 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 { fn typetag_name(&self) -> &'static str {

View file

@ -56,7 +56,7 @@ impl CustomValue for SelectDb {
fn follow_path_int(&self, count: usize, span: Span) -> Result<Value, ShellError> { fn follow_path_int(&self, count: usize, span: Span) -> Result<Value, ShellError> {
let path = PathMember::Int { val: count, span }; 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<Value, ShellError> { fn follow_path_string(&self, column_name: String, span: Span) -> Result<Value, ShellError> {
@ -64,7 +64,7 @@ impl CustomValue for SelectDb {
val: column_name, val: column_name,
span, 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 { fn typetag_name(&self) -> &'static str {

View file

@ -101,7 +101,7 @@ fn dropcol(
let mut vals = vec![]; let mut vals = vec![];
for path in &keep_columns { 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()); cols.push(path.into_string());
vals.push(fetcher); vals.push(fetcher);
} }
@ -125,7 +125,7 @@ fn dropcol(
let mut vals = vec![]; let mut vals = vec![];
for path in &keep_columns { 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()); cols.push(path.into_string());
vals.push(fetcher); vals.push(fetcher);
} }
@ -141,7 +141,7 @@ fn dropcol(
let mut vals = vec![]; let mut vals = vec![];
for cell_path in &keep_columns { 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()); cols.push(cell_path.into_string());
vals.push(result); vals.push(result);

View file

@ -81,7 +81,7 @@ fn empty(
for val in input { for val in input {
for column in &columns { for column in &columns {
let val = val.clone(); let val = val.clone();
match val.follow_cell_path(&column.members) { match val.follow_cell_path(&column.members, false) {
Ok(Value::Nothing { .. }) => {} Ok(Value::Nothing { .. }) => {}
Ok(_) => { Ok(_) => {
return Ok(Value::Bool { return Ok(Value::Bool {

View file

@ -31,6 +31,11 @@ impl Command for Get {
"return nothing if path can't be found", "return nothing if path can't be found",
Some('i'), Some('i'),
) )
.switch(
"sensitive",
"get path in a case sensitive manner",
Some('s'),
)
.category(Category::Filters) .category(Category::Filters)
} }
@ -44,13 +49,14 @@ impl Command for Get {
let span = call.head; let span = call.head;
let cell_path: CellPath = call.req(engine_state, stack, 0)?; let cell_path: CellPath = call.req(engine_state, stack, 0)?;
let rest: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let rest: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let sensitive = call.has_flag("sensitive");
let ignore_errors = call.has_flag("ignore-errors"); let ignore_errors = call.has_flag("ignore-errors");
let ctrlc = engine_state.ctrlc.clone(); let ctrlc = engine_state.ctrlc.clone();
let metadata = input.metadata(); let metadata = input.metadata();
if rest.is_empty() { if rest.is_empty() {
let output = input 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()); .map(|x| x.into_pipeline_data());
if ignore_errors { if ignore_errors {
@ -69,7 +75,7 @@ impl Command for Get {
let input = input.into_value(span); let input = input.into_value(span);
for path in paths { 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 ignore_errors {
if let Ok(val) = val { if let Ok(val) = val {
@ -106,6 +112,16 @@ impl Command for Get {
example: "sys | get cpu", example: "sys | get cpu",
result: None, 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,
},
] ]
} }
} }

View file

@ -94,7 +94,7 @@ fn reject(
let mut vals = vec![]; let mut vals = vec![];
for path in &keep_columns { 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 { if let Ok(value) = fetcher {
cols.push(path.into_string()); cols.push(path.into_string());
@ -135,7 +135,7 @@ fn reject(
let mut vals = vec![]; let mut vals = vec![];
for path in &keep_columns { 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()); cols.push(path.into_string());
vals.push(fetcher); vals.push(fetcher);
} }
@ -151,7 +151,7 @@ fn reject(
let mut vals = vec![]; let mut vals = vec![];
for cell_path in &keep_columns { 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()); cols.push(cell_path.into_string());
vals.push(result); vals.push(result);

View file

@ -124,7 +124,7 @@ fn select(
let mut vals = vec![]; let mut vals = vec![];
for path in &columns { for path in &columns {
//FIXME: improve implementation to not clone //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('.', "_")); cols.push(path.into_string().replace('.', "_"));
vals.push(fetcher); vals.push(fetcher);
@ -148,7 +148,7 @@ fn select(
let mut vals = vec![]; let mut vals = vec![];
for path in &columns { for path in &columns {
//FIXME: improve implementation to not clone //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) => { Ok(value) => {
cols.push(path.into_string().replace('.', "_")); cols.push(path.into_string().replace('.', "_"));
vals.push(value); vals.push(value);
@ -173,7 +173,7 @@ fn select(
for cell_path in columns { for cell_path in columns {
// FIXME: remove clone // 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('.', "_")); cols.push(cell_path.into_string().replace('.', "_"));
vals.push(result); vals.push(result);

View file

@ -261,7 +261,7 @@ fn format_record(
span: *span, span: *span,
}) })
.collect(); .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) => { Ok(value_at_column) => {
output.push_str(value_at_column.into_string(", ", config).as_str()) output.push_str(value_at_column.into_string(", ", config).as_str())
} }

View file

@ -310,12 +310,13 @@ fn convert_to_list(
} else { } else {
for header in headers.iter().skip(1) { for header in headers.iter().skip(1) {
let result = match item { let result = match item {
Value::Record { .. } => { Value::Record { .. } => item.clone().follow_cell_path(
item.clone().follow_cell_path(&[PathMember::String { &[PathMember::String {
val: header.into(), val: header.into(),
span: head, span: head,
}]) }],
} false,
),
_ => Ok(item.clone()), _ => Ok(item.clone()),
}; };

View file

@ -386,12 +386,13 @@ fn convert_to_table(
let skip_num = if !disable_index { 1 } else { 0 }; let skip_num = if !disable_index { 1 } else { 0 };
for header in headers.iter().skip(skip_num) { for header in headers.iter().skip(skip_num) {
let result = match item { let result = match item {
Value::Record { .. } => { Value::Record { .. } => item.clone().follow_cell_path(
item.clone().follow_cell_path(&[PathMember::String { &[PathMember::String {
val: header.into(), val: header.into(),
span: head, span: head,
}]) }],
} false,
),
_ => Ok(item.clone()), _ => Ok(item.clone()),
}; };

View file

@ -262,7 +262,7 @@ fn get_converted_value(
val: block_id, val: block_id,
span: from_span, 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); let block = engine_state.get_block(block_id);

View file

@ -295,7 +295,7 @@ pub fn eval_expression(
Expr::FullCellPath(cell_path) => { Expr::FullCellPath(cell_path) => {
let value = eval_expression(engine_state, stack, &cell_path.head)?; 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::ImportPattern(_) => Ok(Value::Nothing { span: expr.span }),
Expr::Call(call) => { Expr::Call(call) => {

View file

@ -217,6 +217,7 @@ impl PipelineData {
self, self,
cell_path: &[PathMember], cell_path: &[PathMember],
head: Span, head: Span,
insensitive: bool,
) -> Result<Value, ShellError> { ) -> Result<Value, ShellError> {
match self { match self {
// FIXME: there are probably better ways of doing this // FIXME: there are probably better ways of doing this
@ -224,8 +225,8 @@ impl PipelineData {
vals: stream.collect(), vals: stream.collect(),
span: head, span: head,
} }
.follow_cell_path(cell_path), .follow_cell_path(cell_path, insensitive),
PipelineData::Value(v, ..) => v.follow_cell_path(cell_path), PipelineData::Value(v, ..) => v.follow_cell_path(cell_path, insensitive),
_ => Err(ShellError::IOError("can't follow stream paths".into())), _ => Err(ShellError::IOError("can't follow stream paths".into())),
} }
} }

View file

@ -605,7 +605,11 @@ impl Value {
} }
/// Follow a given column path into the value: for example accessing select elements in a stream or list /// 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<Value, ShellError> { pub fn follow_cell_path(
self,
cell_path: &[PathMember],
insensitive: bool,
) -> Result<Value, ShellError> {
let mut current = self; let mut current = self;
for member in cell_path { for member in cell_path {
// FIXME: this uses a few extra clones for simplicity, but there may be a way // FIXME: this uses a few extra clones for simplicity, but there may be a way
@ -661,12 +665,13 @@ impl Value {
let span = *span; let span = *span;
// Make reverse iterate to avoid duplicate column leads to first value, actuall last value is expected. // Make reverse iterate to avoid duplicate column leads to first value, actuall last value is expected.
if let Some(found) = cols if let Some(found) = cols.iter().zip(vals.iter()).rev().find(|x| {
.iter() if insensitive {
.zip(vals.iter()) x.0.to_lowercase() == column_name.to_lowercase()
.rev() } else {
.find(|x| x.0 == column_name) x.0 == column_name
{ }
}) {
current = found.1.clone(); current = found.1.clone();
} else if let Some(suggestion) = did_you_mean(&cols, column_name) { } else if let Some(suggestion) = did_you_mean(&cols, column_name) {
return Err(ShellError::DidYouMean(suggestion, *origin_span)); return Err(ShellError::DidYouMean(suggestion, *origin_span));
@ -679,10 +684,13 @@ impl Value {
let mut hasvalue = false; let mut hasvalue = false;
let mut temp: Result<Value, ShellError> = Err(ShellError::NotFound(*span)); let mut temp: Result<Value, ShellError> = Err(ShellError::NotFound(*span));
for val in vals { for val in vals {
temp = val.clone().follow_cell_path(&[PathMember::String { temp = val.clone().follow_cell_path(
&[PathMember::String {
val: column_name.clone(), val: column_name.clone(),
span: *origin_span, span: *origin_span,
}]); }],
insensitive,
);
if let Ok(result) = temp.clone() { if let Ok(result) = temp.clone() {
hasvalue = true; hasvalue = true;
output.push(result); output.push(result);
@ -723,7 +731,7 @@ impl Value {
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
let orig = self.clone(); 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 { match new_val {
Value::Error { error } => Err(error), Value::Error { error } => Err(error),
@ -834,7 +842,7 @@ impl Value {
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
let orig = self.clone(); 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 { match new_val {
Value::Error { error } => Err(error), Value::Error { error } => Err(error),

View file

@ -86,7 +86,7 @@ impl Inc {
pub fn inc(&self, head: Span, value: &Value) -> Result<Value, LabeledError> { pub fn inc(&self, head: Span, value: &Value) -> Result<Value, LabeledError> {
if let Some(cell_path) = &self.cell_path { if let Some(cell_path) = &self.cell_path {
let working_value = value.clone(); 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)?; let cell_value = self.inc_value(head, &cell_value)?;

View file

@ -260,3 +260,11 @@ fn length_defaulted_columns() -> TestResult {
fn get_fuzzy() -> TestResult { fn get_fuzzy() -> TestResult {
run_test("(ls | get -i foo) == $nothing", "true") 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",
)
}