mirror of
https://github.com/nushell/nushell
synced 2024-12-28 05:53:09 +00:00
Merge pull request #856 from andrasio/value-improvements
Improvements to Value mutable operations.
This commit is contained in:
commit
8923e91e39
2 changed files with 210 additions and 137 deletions
336
src/data/base.rs
336
src/data/base.rs
|
@ -497,6 +497,28 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_mut_data_by_key(&mut self, name: &str) -> Option<&mut Tagged<Value>> {
|
||||||
|
match self {
|
||||||
|
Value::Row(ref mut o) => o.get_mut_data_by_key(name),
|
||||||
|
Value::Table(ref mut l) => {
|
||||||
|
for item in l {
|
||||||
|
match item {
|
||||||
|
Tagged {
|
||||||
|
item: Value::Row(ref mut o),
|
||||||
|
..
|
||||||
|
} => match o.get_mut_data_by_key(name) {
|
||||||
|
Some(v) => return Some(v),
|
||||||
|
None => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_data_by_column_path(
|
pub fn get_data_by_column_path(
|
||||||
&self,
|
&self,
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
|
@ -513,18 +535,6 @@ impl Value {
|
||||||
Some(current.tagged(tag))
|
Some(current.tagged(tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_data_by_path(&self, tag: Tag, path: &str) -> Option<Tagged<&Value>> {
|
|
||||||
let mut current = self;
|
|
||||||
for p in path.split(".") {
|
|
||||||
match current.get_data_by_key(p) {
|
|
||||||
Some(v) => current = v,
|
|
||||||
None => return None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(current.tagged(tag))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_data_at_path(
|
pub fn insert_data_at_path(
|
||||||
&self,
|
&self,
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
|
@ -629,41 +639,6 @@ impl Value {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_data_at_path(
|
|
||||||
&self,
|
|
||||||
tag: Tag,
|
|
||||||
path: &str,
|
|
||||||
replaced_value: Value,
|
|
||||||
) -> Option<Tagged<Value>> {
|
|
||||||
let mut new_obj = self.clone();
|
|
||||||
|
|
||||||
let split_path: Vec<_> = path.split(".").collect();
|
|
||||||
|
|
||||||
if let Value::Row(ref mut o) = new_obj {
|
|
||||||
let mut current = o;
|
|
||||||
for idx in 0..split_path.len() {
|
|
||||||
match current.entries.get_mut(split_path[idx]) {
|
|
||||||
Some(next) => {
|
|
||||||
if idx == (split_path.len() - 1) {
|
|
||||||
*next = replaced_value.tagged(&tag);
|
|
||||||
return Some(new_obj.tagged(&tag));
|
|
||||||
} else {
|
|
||||||
match next.item {
|
|
||||||
Value::Row(ref mut o) => {
|
|
||||||
current = o;
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn replace_data_at_column_path(
|
pub fn replace_data_at_column_path(
|
||||||
&self,
|
&self,
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
|
@ -671,34 +646,20 @@ impl Value {
|
||||||
replaced_value: Value,
|
replaced_value: Value,
|
||||||
) -> Option<Tagged<Value>> {
|
) -> Option<Tagged<Value>> {
|
||||||
let mut new_obj = self.clone();
|
let mut new_obj = self.clone();
|
||||||
|
let mut current = &mut new_obj;
|
||||||
|
|
||||||
if let Value::Row(ref mut o) = new_obj {
|
for idx in 0..split_path.len() {
|
||||||
let mut current = o;
|
match current.get_mut_data_by_key(&split_path[idx].item) {
|
||||||
for idx in 0..split_path.len() {
|
Some(next) => {
|
||||||
match current.entries.get_mut(&split_path[idx].item) {
|
if idx == (split_path.len() - 1) {
|
||||||
Some(next) => {
|
*next = replaced_value.tagged(&tag);
|
||||||
if idx == (split_path.len() - 1) {
|
return Some(new_obj.tagged(&tag));
|
||||||
*next = replaced_value.tagged(&tag);
|
} else {
|
||||||
return Some(new_obj.tagged(&tag));
|
current = &mut next.item;
|
||||||
} else {
|
|
||||||
match next.item {
|
|
||||||
Value::Row(ref mut o) => {
|
|
||||||
current = o;
|
|
||||||
}
|
|
||||||
Value::Table(ref mut l) => match l.get_mut(0) {
|
|
||||||
Some(Tagged {
|
|
||||||
item: Value::Row(ref mut dict),
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
current = dict;
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
},
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => return None,
|
}
|
||||||
|
None => {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -785,13 +746,7 @@ impl Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn table(list: &Vec<Tagged<Value>>) -> Value {
|
pub fn table(list: &Vec<Tagged<Value>>) -> Value {
|
||||||
let mut out = vec![];
|
Value::Table(list.to_vec())
|
||||||
|
|
||||||
for v in list {
|
|
||||||
out.push(v.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Table(out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn string(s: impl Into<String>) -> Value {
|
pub fn string(s: impl Into<String>) -> Value {
|
||||||
|
@ -972,86 +927,193 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn column_path(paths: &Vec<Tagged<Value>>) -> Tagged<Vec<Tagged<String>>> {
|
fn column_path(paths: &Vec<Tagged<Value>>) -> Tagged<Vec<Tagged<String>>> {
|
||||||
let paths = paths
|
table(
|
||||||
.iter()
|
&paths
|
||||||
.map(|p| string(p.as_string().unwrap()))
|
.iter()
|
||||||
.collect();
|
.map(|p| string(p.as_string().unwrap()))
|
||||||
let table = table(&paths);
|
.collect(),
|
||||||
table.as_column_path().unwrap()
|
)
|
||||||
|
.as_column_path()
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gets_the_matching_field_from_a_row() {
|
fn gets_matching_field_from_a_row() {
|
||||||
let field = "amigos";
|
let row = Value::row(indexmap! {
|
||||||
|
"amigos".into() => table(&vec![string("andres"),string("jonathan"),string("yehuda")])
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
*row.get_data_by_key("amigos").unwrap(),
|
||||||
|
table(&vec![
|
||||||
|
string("andres"),
|
||||||
|
string("jonathan"),
|
||||||
|
string("yehuda")
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gets_matching_field_from_nested_rows_inside_a_row() {
|
||||||
|
let field_path = column_path(&vec![string("package"), string("version")]);
|
||||||
|
|
||||||
|
let (version, tag) = string("0.4.0").into_parts();
|
||||||
|
|
||||||
let row = Value::row(indexmap! {
|
let row = Value::row(indexmap! {
|
||||||
field.into() => table(&vec![
|
"package".into() =>
|
||||||
|
row(indexmap! {
|
||||||
|
"name".into() => string("nu"),
|
||||||
|
"version".into() => string("0.4.0")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
**row.get_data_by_column_path(tag, &field_path).unwrap(),
|
||||||
|
version
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gets_first_matching_field_from_rows_with_same_field_inside_a_table() {
|
||||||
|
let field_path = column_path(&vec![string("package"), string("authors"), string("name")]);
|
||||||
|
|
||||||
|
let (name, tag) = string("Andrés N. Robalino").into_parts();
|
||||||
|
|
||||||
|
let row = Value::row(indexmap! {
|
||||||
|
"package".into() => row(indexmap! {
|
||||||
|
"name".into() => string("nu"),
|
||||||
|
"version".into() => string("0.4.0"),
|
||||||
|
"authors".into() => table(&vec![
|
||||||
|
row(indexmap!{"name".into() => string("Andrés N. Robalino")}),
|
||||||
|
row(indexmap!{"name".into() => string("Jonathan Turner")}),
|
||||||
|
row(indexmap!{"name".into() => string("Yehuda Katz")})
|
||||||
|
])
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
**row.get_data_by_column_path(tag, &field_path).unwrap(),
|
||||||
|
name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn replaces_matching_field_from_a_row() {
|
||||||
|
let field_path = column_path(&vec![string("amigos")]);
|
||||||
|
|
||||||
|
let sample = Value::row(indexmap! {
|
||||||
|
"amigos".into() => table(&vec![
|
||||||
string("andres"),
|
string("andres"),
|
||||||
string("jonathan"),
|
string("jonathan"),
|
||||||
string("yehuda"),
|
string("yehuda"),
|
||||||
]),
|
]),
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
let (replacement, tag) = string("jonas").into_parts();
|
||||||
table(&vec![
|
|
||||||
string("andres"),
|
let actual = sample
|
||||||
string("jonathan"),
|
.replace_data_at_column_path(tag, &field_path, replacement)
|
||||||
string("yehuda")
|
.unwrap();
|
||||||
]),
|
|
||||||
*row.get_data_by_key(field).unwrap()
|
assert_eq!(actual, row(indexmap! {"amigos".into() => string("jonas")}));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gets_the_first_row_with_matching_field_from_rows_inside_a_table() {
|
fn replaces_matching_field_from_nested_rows_inside_a_row() {
|
||||||
let field = "name";
|
let field_path = column_path(&vec![
|
||||||
|
string("package"),
|
||||||
let table = Value::table(&vec![
|
string("authors"),
|
||||||
row(indexmap! {field.into() => string("andres")}),
|
string("los.3.caballeros"),
|
||||||
row(indexmap! {field.into() => string("jonathan")}),
|
|
||||||
row(indexmap! {field.into() => string("yehuda")}),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_eq!(string("andres"), *table.get_data_by_key(field).unwrap());
|
let sample = Value::row(indexmap! {
|
||||||
}
|
"package".into() => row(indexmap! {
|
||||||
|
"authors".into() => row(indexmap! {
|
||||||
#[test]
|
"los.3.mosqueteros".into() => table(&vec![string("andres::yehuda::jonathan")]),
|
||||||
fn gets_the_matching_field_from_nested_rows_inside_a_row() {
|
"los.3.amigos".into() => table(&vec![string("andres::yehuda::jonathan")]),
|
||||||
let _field = "package.version";
|
"los.3.caballeros".into() => table(&vec![string("andres::yehuda::jonathan")])
|
||||||
let field = vec![string("package"), string("version")];
|
})
|
||||||
let field = column_path(&field);
|
|
||||||
|
|
||||||
let (version, tag) = string("0.4.0").into_parts();
|
|
||||||
|
|
||||||
let row = Value::row(indexmap! {
|
|
||||||
"package".into() => row(indexmap!{
|
|
||||||
"name".into() => string("nu"),
|
|
||||||
"version".into() => string("0.4.0"),
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(version, **row.get_data_by_column_path(tag, &field).unwrap())
|
let (replacement, tag) = table(&vec![string("yehuda::jonathan::andres")]).into_parts();
|
||||||
|
|
||||||
|
let actual = sample
|
||||||
|
.replace_data_at_column_path(tag.clone(), &field_path, replacement.clone())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
actual,
|
||||||
|
Value::row(indexmap! {
|
||||||
|
"package".into() => row(indexmap! {
|
||||||
|
"authors".into() => row(indexmap! {
|
||||||
|
"los.3.mosqueteros".into() => table(&vec![string("andres::yehuda::jonathan")]),
|
||||||
|
"los.3.amigos".into() => table(&vec![string("andres::yehuda::jonathan")]),
|
||||||
|
"los.3.caballeros".into() => replacement.tagged(&tag)})})})
|
||||||
|
.tagged(tag)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gets_the_first_row_with_matching_field_from_nested_rows_inside_a_table() {
|
fn replaces_matching_field_from_rows_inside_a_table() {
|
||||||
let _field = "package.authors.name";
|
let field_path = column_path(&vec![
|
||||||
let field = vec![string("package"), string("authors"), string("name")];
|
string("shell_policy"),
|
||||||
let field = column_path(&field);
|
string("releases"),
|
||||||
|
string("nu.version.arepa"),
|
||||||
|
]);
|
||||||
|
|
||||||
let (name, tag) = string("Andrés N. Robalino").into_parts();
|
let sample = Value::row(indexmap! {
|
||||||
|
"shell_policy".into() => row(indexmap! {
|
||||||
let row = Value::row(indexmap! {
|
"releases".into() => table(&vec![
|
||||||
"package".into() => row(indexmap!{
|
row(indexmap! {
|
||||||
"authors".into() => table(&vec![
|
"nu.version.arepa".into() => row(indexmap! {
|
||||||
row(indexmap!{"name".into()=> string("Andrés N. Robalino")}),
|
"code".into() => string("0.4.0"), "tag_line".into() => string("GitHub-era")
|
||||||
row(indexmap!{"name".into()=> string("Jonathan Turner")}),
|
})
|
||||||
row(indexmap!{"name".into() => string("Yehuda Katz")})
|
}),
|
||||||
]),
|
row(indexmap! {
|
||||||
"name".into() => string("nu"),
|
"nu.version.taco".into() => row(indexmap! {
|
||||||
"version".into() => string("0.4.0"),
|
"code".into() => string("0.3.0"), "tag_line".into() => string("GitHub-era")
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
row(indexmap! {
|
||||||
|
"nu.version.stable".into() => row(indexmap! {
|
||||||
|
"code".into() => string("0.2.0"), "tag_line".into() => string("GitHub-era")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
])
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(name, **row.get_data_by_column_path(tag, &field).unwrap())
|
let (replacement, tag) = row(indexmap! {
|
||||||
|
"code".into() => string("0.5.0"),
|
||||||
|
"tag_line".into() => string("CABALLEROS")
|
||||||
|
})
|
||||||
|
.into_parts();
|
||||||
|
|
||||||
|
let actual = sample
|
||||||
|
.replace_data_at_column_path(tag.clone(), &field_path, replacement.clone())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
actual,
|
||||||
|
Value::row(indexmap! {
|
||||||
|
"shell_policy".into() => row(indexmap! {
|
||||||
|
"releases".into() => table(&vec![
|
||||||
|
row(indexmap! {
|
||||||
|
"nu.version.arepa".into() => replacement.tagged(&tag)
|
||||||
|
}),
|
||||||
|
row(indexmap! {
|
||||||
|
"nu.version.taco".into() => row(indexmap! {
|
||||||
|
"code".into() => string("0.3.0"), "tag_line".into() => string("GitHub-era")
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
row(indexmap! {
|
||||||
|
"nu.version.stable".into() => row(indexmap! {
|
||||||
|
"code".into() => string("0.2.0"), "tag_line".into() => string("GitHub-era")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
])
|
||||||
|
})
|
||||||
|
}).tagged(&tag)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,17 @@ impl Dictionary {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_mut_data_by_key(&mut self, name: &str) -> Option<&mut Tagged<Value>> {
|
||||||
|
match self
|
||||||
|
.entries
|
||||||
|
.iter_mut()
|
||||||
|
.find(|(desc_name, _)| *desc_name == name)
|
||||||
|
{
|
||||||
|
Some((_, v)) => Some(v),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let mut debug = f.debug_struct("Dictionary");
|
let mut debug = f.debug_struct("Dictionary");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue