mirror of
https://github.com/nushell/nushell.git
synced 2025-08-15 09:53:13 +02:00
feat(cell-path): opt-in case insensitivity for cell-path members
- Add `CellPath::String::insensitive: bool`, represented with an exclamation mark in `Display` - Update `into cell-path` and `split cell-path` to take case insensitivity into account while constructing and splitting cell-paths
This commit is contained in:
@ -17,7 +17,12 @@ impl Command for IntoCellPath {
|
|||||||
(Type::List(Box::new(Type::Any)), Type::CellPath),
|
(Type::List(Box::new(Type::Any)), Type::CellPath),
|
||||||
(
|
(
|
||||||
Type::List(Box::new(Type::Record(
|
Type::List(Box::new(Type::Record(
|
||||||
[("value".into(), Type::Any), ("optional".into(), Type::Bool)].into(),
|
[
|
||||||
|
("value".into(), Type::Any),
|
||||||
|
("optional".into(), Type::Bool),
|
||||||
|
("insensitive".into(), Type::Bool),
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
))),
|
))),
|
||||||
Type::CellPath,
|
Type::CellPath,
|
||||||
),
|
),
|
||||||
@ -69,8 +74,8 @@ impl Command for IntoCellPath {
|
|||||||
example: "'some.path' | split row '.' | into cell-path",
|
example: "'some.path' | split row '.' | into cell-path",
|
||||||
result: Some(Value::test_cell_path(CellPath {
|
result: Some(Value::test_cell_path(CellPath {
|
||||||
members: vec![
|
members: vec![
|
||||||
PathMember::test_string("some".into(), false),
|
PathMember::test_string("some".into(), false, false),
|
||||||
PathMember::test_string("path".into(), false),
|
PathMember::test_string("path".into(), false, false),
|
||||||
],
|
],
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
@ -80,9 +85,9 @@ impl Command for IntoCellPath {
|
|||||||
result: Some(Value::test_cell_path(CellPath {
|
result: Some(Value::test_cell_path(CellPath {
|
||||||
members: vec![
|
members: vec![
|
||||||
PathMember::test_int(5, false),
|
PathMember::test_int(5, false),
|
||||||
PathMember::test_string("c".into(), false),
|
PathMember::test_string("c".into(), false, false),
|
||||||
PathMember::test_int(7, false),
|
PathMember::test_int(7, false),
|
||||||
PathMember::test_string("h".into(), false),
|
PathMember::test_string("h".into(), false, false),
|
||||||
],
|
],
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
@ -92,7 +97,7 @@ impl Command for IntoCellPath {
|
|||||||
result: Some(Value::test_cell_path(CellPath {
|
result: Some(Value::test_cell_path(CellPath {
|
||||||
members: vec![
|
members: vec![
|
||||||
PathMember::test_int(5, true),
|
PathMember::test_int(5, true),
|
||||||
PathMember::test_string("c".into(), false),
|
PathMember::test_string("c".into(), false, false),
|
||||||
],
|
],
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
@ -175,6 +180,12 @@ fn record_to_path_member(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(insensitive) = record.get("insensitive") {
|
||||||
|
if insensitive.as_bool()? {
|
||||||
|
member.make_insensitive();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(member)
|
Ok(member)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +207,7 @@ fn value_to_path_member(val: &Value, span: Span) -> Result<PathMember, ShellErro
|
|||||||
let val_span = val.span();
|
let val_span = val.span();
|
||||||
let member = match val {
|
let member = match val {
|
||||||
Value::Int { val, .. } => int_to_path_member(*val, val_span)?,
|
Value::Int { val, .. } => int_to_path_member(*val, val_span)?,
|
||||||
Value::String { val, .. } => PathMember::string(val.into(), false, val_span),
|
Value::String { val, .. } => PathMember::string(val.into(), false, false, val_span),
|
||||||
Value::Record { val, .. } => record_to_path_member(val, val_span, span)?,
|
Value::Record { val, .. } => record_to_path_member(val, val_span, span)?,
|
||||||
other => {
|
other => {
|
||||||
return Err(ShellError::CantConvert {
|
return Err(ShellError::CantConvert {
|
||||||
|
@ -16,7 +16,12 @@ impl Command for SplitCellPath {
|
|||||||
(
|
(
|
||||||
Type::CellPath,
|
Type::CellPath,
|
||||||
Type::List(Box::new(Type::Record(
|
Type::List(Box::new(Type::Record(
|
||||||
[("value".into(), Type::Any), ("optional".into(), Type::Bool)].into(),
|
[
|
||||||
|
("value".into(), Type::Any),
|
||||||
|
("optional".into(), Type::Bool),
|
||||||
|
("insensitive".into(), Type::Bool),
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
))),
|
))),
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
@ -114,19 +119,29 @@ fn split_cell_path(val: CellPath, span: Span) -> Result<Value, ShellError> {
|
|||||||
struct PathMemberRecord {
|
struct PathMemberRecord {
|
||||||
value: Value,
|
value: Value,
|
||||||
optional: bool,
|
optional: bool,
|
||||||
|
insensitive: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PathMemberRecord {
|
impl PathMemberRecord {
|
||||||
fn from_path_member(pm: PathMember) -> Self {
|
fn from_path_member(pm: PathMember) -> Self {
|
||||||
let (optional, internal_span) = match pm {
|
let (optional, insensitive, internal_span) = match pm {
|
||||||
PathMember::String { optional, span, .. }
|
PathMember::String {
|
||||||
| PathMember::Int { optional, span, .. } => (optional, span),
|
optional,
|
||||||
|
insensitive,
|
||||||
|
span,
|
||||||
|
..
|
||||||
|
} => (optional, insensitive, span),
|
||||||
|
PathMember::Int { optional, span, .. } => (optional, false, span),
|
||||||
};
|
};
|
||||||
let value = match pm {
|
let value = match pm {
|
||||||
PathMember::String { val, .. } => Value::string(val, internal_span),
|
PathMember::String { val, .. } => Value::string(val, internal_span),
|
||||||
PathMember::Int { val, .. } => Value::int(val as i64, internal_span),
|
PathMember::Int { val, .. } => Value::int(val as i64, internal_span),
|
||||||
};
|
};
|
||||||
Self { value, optional }
|
Self {
|
||||||
|
value,
|
||||||
|
optional,
|
||||||
|
insensitive,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ pub fn empty(
|
|||||||
if !columns.is_empty() {
|
if !columns.is_empty() {
|
||||||
for val in input {
|
for val in input {
|
||||||
for column in &columns {
|
for column in &columns {
|
||||||
if !val.follow_cell_path(&column.members, false)?.is_nothing() {
|
if !val.follow_cell_path(&column.members)?.is_nothing() {
|
||||||
return Ok(Value::bool(negate, head).into_pipeline_data());
|
return Ok(Value::bool(negate, head).into_pipeline_data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@ pub enum PathMember {
|
|||||||
/// If marked as optional don't throw an error if not found but perform default handling
|
/// If marked as optional don't throw an error if not found but perform default handling
|
||||||
/// (e.g. return `Value::Nothing`)
|
/// (e.g. return `Value::Nothing`)
|
||||||
optional: bool,
|
optional: bool,
|
||||||
|
/// If marked as insensitive, column lookup happens case insensitively
|
||||||
|
insensitive: bool,
|
||||||
},
|
},
|
||||||
/// Accessing a member by index (i.e. row of a table or item in a list)
|
/// Accessing a member by index (i.e. row of a table or item in a list)
|
||||||
Int {
|
Int {
|
||||||
@ -34,11 +36,12 @@ impl PathMember {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn string(val: String, optional: bool, span: Span) -> Self {
|
pub fn string(val: String, optional: bool, insensitive: bool, span: Span) -> Self {
|
||||||
PathMember::String {
|
PathMember::String {
|
||||||
val,
|
val,
|
||||||
span,
|
span,
|
||||||
optional,
|
optional,
|
||||||
|
insensitive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,10 +53,11 @@ impl PathMember {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_string(val: String, optional: bool) -> Self {
|
pub fn test_string(val: String, optional: bool, insensitive: bool) -> Self {
|
||||||
PathMember::String {
|
PathMember::String {
|
||||||
val,
|
val,
|
||||||
optional,
|
optional,
|
||||||
|
insensitive,
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,6 +73,16 @@ impl PathMember {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_insensitive(&mut self) {
|
||||||
|
match self {
|
||||||
|
PathMember::String {
|
||||||
|
ref mut insensitive,
|
||||||
|
..
|
||||||
|
} => *insensitive = true,
|
||||||
|
PathMember::Int { .. } => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn span(&self) -> Span {
|
pub fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
PathMember::String { span, .. } => *span,
|
PathMember::String { span, .. } => *span,
|
||||||
@ -182,6 +196,12 @@ impl CellPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_insensitive(&mut self) {
|
||||||
|
for member in &mut self.members {
|
||||||
|
member.make_insensitive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Formats the cell-path as a column name, i.e. without quoting and optional markers ('?').
|
// Formats the cell-path as a column name, i.e. without quoting and optional markers ('?').
|
||||||
pub fn to_column_name(&self) -> String {
|
pub fn to_column_name(&self) -> String {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
@ -213,14 +233,20 @@ impl Display for CellPath {
|
|||||||
let question_mark = if *optional { "?" } else { "" };
|
let question_mark = if *optional { "?" } else { "" };
|
||||||
write!(f, ".{val}{question_mark}")?
|
write!(f, ".{val}{question_mark}")?
|
||||||
}
|
}
|
||||||
PathMember::String { val, optional, .. } => {
|
PathMember::String {
|
||||||
|
val,
|
||||||
|
optional,
|
||||||
|
insensitive,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let question_mark = if *optional { "?" } else { "" };
|
let question_mark = if *optional { "?" } else { "" };
|
||||||
|
let exclamation_mark = if *insensitive { "!" } else { "" };
|
||||||
let val = if needs_quoting(val) {
|
let val = if needs_quoting(val) {
|
||||||
&escape_quote_string(val)
|
&escape_quote_string(val)
|
||||||
} else {
|
} else {
|
||||||
val
|
val
|
||||||
};
|
};
|
||||||
write!(f, ".{val}{question_mark}")?
|
write!(f, ".{val}{exclamation_mark}{question_mark}")?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,7 +269,11 @@ mod test {
|
|||||||
fn path_member_partial_ord() {
|
fn path_member_partial_ord() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Greater),
|
Some(Greater),
|
||||||
PathMember::test_int(5, true).partial_cmp(&PathMember::test_string("e".into(), true))
|
PathMember::test_int(5, true).partial_cmp(&PathMember::test_string(
|
||||||
|
"e".into(),
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -258,14 +288,20 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Greater),
|
Some(Greater),
|
||||||
PathMember::test_string("e".into(), true)
|
PathMember::test_string("e".into(), true, false).partial_cmp(&PathMember::test_string(
|
||||||
.partial_cmp(&PathMember::test_string("e".into(), false))
|
"e".into(),
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Greater),
|
Some(Greater),
|
||||||
PathMember::test_string("f".into(), true)
|
PathMember::test_string("f".into(), true, false).partial_cmp(&PathMember::test_string(
|
||||||
.partial_cmp(&PathMember::test_string("e".into(), true))
|
"e".into(),
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user