mirror of
https://github.com/nushell/nushell.git
synced 2025-05-08 20:14:26 +02:00
feat(record): Add CasedRecord, Record wrapper that affects case sensitivity
Make case sensitivity explicit in various places.
This commit is contained in:
parent
ea31bd3918
commit
e8b6783529
@ -31,7 +31,7 @@ use fancy_regex::Regex;
|
||||
use nu_utils::{
|
||||
contains_emoji,
|
||||
locale::{get_system_locale_string, LOCALE_OVERRIDE_ENV_VAR},
|
||||
IgnoreCaseExt, SharedCow,
|
||||
SharedCow,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
@ -1182,6 +1182,7 @@ impl Value {
|
||||
PathMember::String {
|
||||
val: col_name,
|
||||
span,
|
||||
insensitive,
|
||||
..
|
||||
} => match self {
|
||||
Value::List { vals, .. } => {
|
||||
@ -1189,7 +1190,9 @@ impl Value {
|
||||
match val {
|
||||
Value::Record { val: record, .. } => {
|
||||
let record = record.to_mut();
|
||||
if let Some(val) = record.get_mut(col_name) {
|
||||
if let Some(val) =
|
||||
record.cased_mut(*insensitive).get_mut(col_name)
|
||||
{
|
||||
val.upsert_data_at_cell_path(path, new_val.clone())?;
|
||||
} else {
|
||||
let new_col =
|
||||
@ -1210,7 +1213,7 @@ impl Value {
|
||||
}
|
||||
Value::Record { val: record, .. } => {
|
||||
let record = record.to_mut();
|
||||
if let Some(val) = record.get_mut(col_name) {
|
||||
if let Some(val) = record.cased_mut(*insensitive).get_mut(col_name) {
|
||||
val.upsert_data_at_cell_path(path, new_val)?;
|
||||
} else {
|
||||
let new_col = Value::with_data_at_cell_path(path, new_val.clone())?;
|
||||
@ -1282,6 +1285,7 @@ impl Value {
|
||||
PathMember::String {
|
||||
val: col_name,
|
||||
span,
|
||||
insensitive,
|
||||
..
|
||||
} => match self {
|
||||
Value::List { vals, .. } => {
|
||||
@ -1289,7 +1293,9 @@ impl Value {
|
||||
let v_span = val.span();
|
||||
match val {
|
||||
Value::Record { val: record, .. } => {
|
||||
if let Some(val) = record.to_mut().get_mut(col_name) {
|
||||
if let Some(val) =
|
||||
record.to_mut().cased_mut(*insensitive).get_mut(col_name)
|
||||
{
|
||||
val.update_data_at_cell_path(path, new_val.clone())?;
|
||||
} else {
|
||||
return Err(ShellError::CantFindColumn {
|
||||
@ -1311,7 +1317,8 @@ impl Value {
|
||||
}
|
||||
}
|
||||
Value::Record { val: record, .. } => {
|
||||
if let Some(val) = record.to_mut().get_mut(col_name) {
|
||||
if let Some(val) = record.to_mut().cased_mut(*insensitive).get_mut(col_name)
|
||||
{
|
||||
val.update_data_at_cell_path(path, new_val)?;
|
||||
} else {
|
||||
return Err(ShellError::CantFindColumn {
|
||||
@ -1377,7 +1384,11 @@ impl Value {
|
||||
let v_span = val.span();
|
||||
match val {
|
||||
Value::Record { val: record, .. } => {
|
||||
if record.to_mut().remove(col_name).is_none() && !optional {
|
||||
let value = record
|
||||
.to_mut()
|
||||
.cased_mut(*insensitive)
|
||||
.remove(col_name);
|
||||
if value.is_none() && !optional {
|
||||
return Err(ShellError::CantFindColumn {
|
||||
col_name: col_name.clone(),
|
||||
span: Some(*span),
|
||||
@ -1397,7 +1408,13 @@ impl Value {
|
||||
Ok(())
|
||||
}
|
||||
Value::Record { val: record, .. } => {
|
||||
if record.to_mut().remove(col_name).is_none() && !optional {
|
||||
if record
|
||||
.to_mut()
|
||||
.cased_mut(*insensitive)
|
||||
.remove(col_name)
|
||||
.is_none()
|
||||
&& !optional
|
||||
{
|
||||
return Err(ShellError::CantFindColumn {
|
||||
col_name: col_name.clone(),
|
||||
span: Some(*span),
|
||||
@ -1453,7 +1470,11 @@ impl Value {
|
||||
let v_span = val.span();
|
||||
match val {
|
||||
Value::Record { val: record, .. } => {
|
||||
if let Some(val) = record.to_mut().get_mut(col_name) {
|
||||
let val = record
|
||||
.to_mut()
|
||||
.cased_mut(*insensitive)
|
||||
.get_mut(col_name);
|
||||
if let Some(val) = val {
|
||||
val.remove_data_at_cell_path(path)?;
|
||||
} else if !optional {
|
||||
return Err(ShellError::CantFindColumn {
|
||||
@ -1475,7 +1496,9 @@ impl Value {
|
||||
Ok(())
|
||||
}
|
||||
Value::Record { val: record, .. } => {
|
||||
if let Some(val) = record.to_mut().get_mut(col_name) {
|
||||
if let Some(val) =
|
||||
record.to_mut().cased_mut(*insensitive).get_mut(col_name)
|
||||
{
|
||||
val.remove_data_at_cell_path(path)?;
|
||||
} else if !optional {
|
||||
return Err(ShellError::CantFindColumn {
|
||||
@ -1532,6 +1555,7 @@ impl Value {
|
||||
PathMember::String {
|
||||
val: col_name,
|
||||
span,
|
||||
insensitive,
|
||||
..
|
||||
} => match self {
|
||||
Value::List { vals, .. } => {
|
||||
@ -1540,7 +1564,9 @@ impl Value {
|
||||
match val {
|
||||
Value::Record { val: record, .. } => {
|
||||
let record = record.to_mut();
|
||||
if let Some(val) = record.get_mut(col_name) {
|
||||
if let Some(val) =
|
||||
record.cased_mut(*insensitive).get_mut(col_name)
|
||||
{
|
||||
if path.is_empty() {
|
||||
return Err(ShellError::ColumnAlreadyExists {
|
||||
col_name: col_name.clone(),
|
||||
@ -1574,7 +1600,7 @@ impl Value {
|
||||
}
|
||||
Value::Record { val: record, .. } => {
|
||||
let record = record.to_mut();
|
||||
if let Some(val) = record.get_mut(col_name) {
|
||||
if let Some(val) = record.cased_mut(*insensitive).get_mut(col_name) {
|
||||
if path.is_empty() {
|
||||
return Err(ShellError::ColumnAlreadyExists {
|
||||
col_name: col_name.clone(),
|
||||
@ -2095,14 +2121,9 @@ fn get_value_member<'a>(
|
||||
let span = current.span();
|
||||
match current {
|
||||
Value::Record { val, .. } => {
|
||||
if let Some(found) = val.iter().rev().find(|x| {
|
||||
if *insensitive {
|
||||
x.0.eq_ignore_case(column_name)
|
||||
} else {
|
||||
x.0 == column_name
|
||||
}
|
||||
}) {
|
||||
Ok(ControlFlow::Continue(Cow::Borrowed(found.1)))
|
||||
let found = val.cased(*insensitive).get(column_name);
|
||||
if let Some(found) = found {
|
||||
Ok(ControlFlow::Continue(Cow::Borrowed(found)))
|
||||
} else if *optional {
|
||||
Ok(ControlFlow::Break(*origin_span))
|
||||
// short-circuit
|
||||
@ -2129,14 +2150,9 @@ fn get_value_member<'a>(
|
||||
let val_span = val.span();
|
||||
match val {
|
||||
Value::Record { val, .. } => {
|
||||
if let Some(found) = val.iter().rev().find(|x| {
|
||||
if *insensitive {
|
||||
x.0.eq_ignore_case(column_name)
|
||||
} else {
|
||||
x.0 == column_name
|
||||
}
|
||||
}) {
|
||||
Ok(found.1.clone())
|
||||
let found = val.cased(*insensitive).get(column_name);
|
||||
if let Some(found) = found {
|
||||
Ok(found.clone())
|
||||
} else if *optional {
|
||||
Ok(Value::nothing(*origin_span))
|
||||
} else if let Some(suggestion) =
|
||||
|
@ -3,6 +3,7 @@ use std::{iter::FusedIterator, ops::RangeBounds};
|
||||
|
||||
use crate::{ShellError, Span, Value};
|
||||
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
use serde::{de::Visitor, ser::SerializeMap, Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@ -10,6 +11,60 @@ pub struct Record {
|
||||
inner: Vec<(String, Value)>,
|
||||
}
|
||||
|
||||
pub struct CasedRecord<R> {
|
||||
record: R,
|
||||
insensitive: bool,
|
||||
}
|
||||
|
||||
impl<R> CasedRecord<R> {
|
||||
fn cmp(&self, lhs: &str, rhs: &str) -> bool {
|
||||
if self.insensitive {
|
||||
lhs.eq_ignore_case(rhs)
|
||||
} else {
|
||||
lhs == rhs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CasedRecord<&'a Record> {
|
||||
pub fn contains(&self, col: impl AsRef<str>) -> bool {
|
||||
self.record.columns().any(|k| self.cmp(k, col.as_ref()))
|
||||
}
|
||||
|
||||
pub fn index_of(&self, col: impl AsRef<str>) -> Option<usize> {
|
||||
self.record
|
||||
.columns()
|
||||
.rposition(|k| self.cmp(k, col.as_ref()))
|
||||
}
|
||||
|
||||
pub fn get(self, col: impl AsRef<str>) -> Option<&'a Value> {
|
||||
let idx = self.index_of(col)?;
|
||||
let (_, value) = self.record.get_index(idx)?;
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CasedRecord<&'a mut Record> {
|
||||
fn shared(&'a self) -> CasedRecord<&'a Record> {
|
||||
CasedRecord {
|
||||
record: &*self.record,
|
||||
insensitive: self.insensitive,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut(self, col: impl AsRef<str>) -> Option<&'a mut Value> {
|
||||
let idx = self.shared().index_of(col)?;
|
||||
let (_, value) = self.record.get_index_mut(idx)?;
|
||||
Some(value)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, col: impl AsRef<str>) -> Option<Value> {
|
||||
let idx = self.shared().index_of(col)?;
|
||||
let (_, val) = self.record.inner.remove(idx);
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl Record {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
@ -21,6 +76,20 @@ impl Record {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cased(&self, insensitive: bool) -> CasedRecord<&Record> {
|
||||
CasedRecord {
|
||||
record: self,
|
||||
insensitive,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cased_mut(&mut self, insensitive: bool) -> CasedRecord<&mut Record> {
|
||||
CasedRecord {
|
||||
record: self,
|
||||
insensitive,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`Record`] from a `Vec` of columns and a `Vec` of [`Value`]s
|
||||
///
|
||||
/// Returns an error if `cols` and `vals` have different lengths.
|
||||
@ -108,6 +177,12 @@ impl Record {
|
||||
self.inner.get(idx).map(|(col, val): &(_, _)| (col, val))
|
||||
}
|
||||
|
||||
pub fn get_index_mut(&mut self, idx: usize) -> Option<(&mut String, &mut Value)> {
|
||||
self.inner
|
||||
.get_mut(idx)
|
||||
.map(|(col, val): &mut (_, _)| (col, val))
|
||||
}
|
||||
|
||||
/// Remove single value by key
|
||||
///
|
||||
/// Returns `None` if key not found
|
||||
|
Loading…
Reference in New Issue
Block a user