Add insert and update back (#4864)

This commit is contained in:
JT
2022-03-18 06:55:02 +13:00
committed by GitHub
parent 6e69d40bb9
commit 0986eefb64
14 changed files with 624 additions and 56 deletions

View File

@ -226,7 +226,7 @@ impl PipelineData {
}
}
pub fn update_cell_path(
pub fn upsert_cell_path(
&mut self,
cell_path: &[PathMember],
callback: Box<dyn FnOnce(&Value) -> Value>,
@ -238,8 +238,8 @@ impl PipelineData {
vals: stream.collect(),
span: head,
}
.update_cell_path(cell_path, callback),
PipelineData::Value(v, ..) => v.update_cell_path(cell_path, callback),
.upsert_cell_path(cell_path, callback),
PipelineData::Value(v, ..) => v.upsert_cell_path(cell_path, callback),
_ => Ok(()),
}
}

View File

@ -150,6 +150,13 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE
#[label = "value originates here"] Span,
),
#[error("Column already exists")]
#[diagnostic(code(nu::shell::column_already_exists), url(docsrs))]
ColumnAlreadyExists(
#[label = "column already exists"] Span,
#[label = "value originates here"] Span,
),
#[error("Not a list value")]
#[diagnostic(code(nu::shell::not_a_list), url(docsrs))]
NotAList(

View File

@ -682,7 +682,7 @@ impl Value {
}
/// Follow a given column path into the value: for example accessing select elements in a stream or list
pub fn update_cell_path(
pub fn upsert_cell_path(
&mut self,
cell_path: &[PathMember],
callback: Box<dyn FnOnce(&Value) -> Value>,
@ -693,11 +693,11 @@ impl Value {
match new_val {
Value::Error { error } => Err(error),
new_val => self.replace_data_at_cell_path(cell_path, new_val),
new_val => self.upsert_data_at_cell_path(cell_path, new_val),
}
}
pub fn replace_data_at_cell_path(
pub fn upsert_data_at_cell_path(
&mut self,
cell_path: &[PathMember],
new_val: Value,
@ -716,7 +716,7 @@ impl Value {
for col in cols.iter().zip(vals.iter_mut()) {
if col.0 == col_name {
found = true;
col.1.replace_data_at_cell_path(
col.1.upsert_data_at_cell_path(
&cell_path[1..],
new_val.clone(),
)?
@ -733,7 +733,7 @@ impl Value {
vals: vec![],
span: new_val.span()?,
};
new_col.replace_data_at_cell_path(
new_col.upsert_data_at_cell_path(
&cell_path[1..],
new_val,
)?;
@ -754,7 +754,7 @@ impl Value {
found = true;
col.1
.replace_data_at_cell_path(&cell_path[1..], new_val.clone())?
.upsert_data_at_cell_path(&cell_path[1..], new_val.clone())?
}
}
if !found {
@ -767,7 +767,7 @@ impl Value {
vals: vec![],
span: new_val.span()?,
};
new_col.replace_data_at_cell_path(&cell_path[1..], new_val)?;
new_col.upsert_data_at_cell_path(&cell_path[1..], new_val)?;
vals.push(new_col);
}
}
@ -777,7 +777,190 @@ impl Value {
PathMember::Int { val: row_num, span } => match self {
Value::List { vals, .. } => {
if let Some(v) = vals.get_mut(*row_num) {
v.replace_data_at_cell_path(&cell_path[1..], new_val)?
v.upsert_data_at_cell_path(&cell_path[1..], new_val)?
} else {
return Err(ShellError::AccessBeyondEnd(vals.len(), *span));
}
}
v => return Err(ShellError::NotAList(*span, v.span()?)),
},
},
None => {
*self = new_val;
}
}
Ok(())
}
/// Follow a given column path into the value: for example accessing select elements in a stream or list
pub fn update_cell_path(
&mut self,
cell_path: &[PathMember],
callback: Box<dyn FnOnce(&Value) -> Value>,
) -> Result<(), ShellError> {
let orig = self.clone();
let new_val = callback(&orig.follow_cell_path(cell_path)?);
match new_val {
Value::Error { error } => Err(error),
new_val => self.update_data_at_cell_path(cell_path, new_val),
}
}
pub fn update_data_at_cell_path(
&mut self,
cell_path: &[PathMember],
new_val: Value,
) -> Result<(), ShellError> {
match cell_path.first() {
Some(path_member) => match path_member {
PathMember::String {
val: col_name,
span,
} => match self {
Value::List { vals, .. } => {
for val in vals.iter_mut() {
match val {
Value::Record {
cols,
vals,
span: v_span,
} => {
let mut found = false;
for col in cols.iter().zip(vals.iter_mut()) {
if col.0 == col_name {
found = true;
col.1.update_data_at_cell_path(
&cell_path[1..],
new_val.clone(),
)?
}
}
if !found {
return Err(ShellError::CantFindColumn(*span, *v_span));
}
}
v => return Err(ShellError::CantFindColumn(*span, v.span()?)),
}
}
}
Value::Record {
cols,
vals,
span: v_span,
} => {
let mut found = false;
for col in cols.iter().zip(vals.iter_mut()) {
if col.0 == col_name {
found = true;
col.1
.update_data_at_cell_path(&cell_path[1..], new_val.clone())?
}
}
if !found {
return Err(ShellError::CantFindColumn(*span, *v_span));
}
}
v => return Err(ShellError::CantFindColumn(*span, v.span()?)),
},
PathMember::Int { val: row_num, span } => match self {
Value::List { vals, .. } => {
if let Some(v) = vals.get_mut(*row_num) {
v.update_data_at_cell_path(&cell_path[1..], new_val)?
} else {
return Err(ShellError::AccessBeyondEnd(vals.len(), *span));
}
}
v => return Err(ShellError::NotAList(*span, v.span()?)),
},
},
None => {
*self = new_val;
}
}
Ok(())
}
pub fn insert_data_at_cell_path(
&mut self,
cell_path: &[PathMember],
new_val: Value,
) -> Result<(), ShellError> {
match cell_path.first() {
Some(path_member) => match path_member {
PathMember::String {
val: col_name,
span,
} => match self {
Value::List { vals, .. } => {
for val in vals.iter_mut() {
match val {
Value::Record {
cols,
vals,
span: v_span,
} => {
for col in cols.iter().zip(vals.iter_mut()) {
if col.0 == col_name {
if cell_path.len() == 1 {
return Err(ShellError::ColumnAlreadyExists(
*span, *v_span,
));
} else {
return col.1.insert_data_at_cell_path(
&cell_path[1..],
new_val,
);
}
}
}
cols.push(col_name.clone());
vals.push(new_val.clone());
}
_ => {
return Err(ShellError::UnsupportedInput(
"table or record".into(),
*span,
))
}
}
}
}
Value::Record {
cols,
vals,
span: v_span,
} => {
for col in cols.iter().zip(vals.iter_mut()) {
if col.0 == col_name {
if cell_path.len() == 1 {
return Err(ShellError::ColumnAlreadyExists(*span, *v_span));
} else {
return col
.1
.insert_data_at_cell_path(&cell_path[1..], new_val);
}
}
}
cols.push(col_name.clone());
vals.push(new_val);
}
_ => {
return Err(ShellError::UnsupportedInput(
"table or record".into(),
*span,
))
}
},
PathMember::Int { val: row_num, span } => match self {
Value::List { vals, .. } => {
if let Some(v) = vals.get_mut(*row_num) {
v.insert_data_at_cell_path(&cell_path[1..], new_val)?
} else {
return Err(ShellError::AccessBeyondEnd(vals.len(), *span));
}