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> {
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> {
@ -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 {

View file

@ -56,7 +56,7 @@ impl CustomValue for SelectDb {
fn follow_path_int(&self, count: usize, span: Span) -> Result<Value, ShellError> {
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> {
@ -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 {

View file

@ -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);

View file

@ -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 {

View file

@ -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<CellPath> = 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,
},
]
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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())
}

View file

@ -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()),
};

View file

@ -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()),
};

View file

@ -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);

View file

@ -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) => {

View file

@ -217,6 +217,7 @@ impl PipelineData {
self,
cell_path: &[PathMember],
head: Span,
insensitive: bool,
) -> Result<Value, ShellError> {
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())),
}
}

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
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;
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<Value, ShellError> = 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),

View file

@ -86,7 +86,7 @@ impl Inc {
pub fn inc(&self, head: Span, value: &Value) -> Result<Value, LabeledError> {
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)?;

View file

@ -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",
)
}