Improvements to Value mutable operations.

This commit is contained in:
Andrés N. Robalino 2019-10-20 06:55:56 -05:00
parent f3c41bbdf1
commit f24bc5c826
2 changed files with 210 additions and 137 deletions

View file

@ -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(
&self,
tag: Tag,
@ -513,18 +535,6 @@ impl Value {
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(
&self,
tag: Tag,
@ -629,41 +639,6 @@ impl Value {
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(
&self,
tag: Tag,
@ -671,34 +646,20 @@ impl Value {
replaced_value: Value,
) -> Option<Tagged<Value>> {
let mut new_obj = self.clone();
let mut current = &mut new_obj;
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].item) {
match current.get_mut_data_by_key(&split_path[idx].item) {
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;
}
Value::Table(ref mut l) => match l.get_mut(0) {
Some(Tagged {
item: Value::Row(ref mut dict),
..
}) => {
current = dict;
}
_ => return None,
},
_ => return None,
current = &mut next.item;
}
}
}
_ => return None,
None => {
return None;
}
}
}
@ -785,13 +746,7 @@ impl Value {
}
pub fn table(list: &Vec<Tagged<Value>>) -> Value {
let mut out = vec![];
for v in list {
out.push(v.clone());
}
Value::Table(out)
Value::Table(list.to_vec())
}
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>>> {
let paths = paths
table(
&paths
.iter()
.map(|p| string(p.as_string().unwrap()))
.collect();
let table = table(&paths);
table.as_column_path().unwrap()
.collect(),
)
.as_column_path()
.unwrap()
}
#[test]
fn gets_the_matching_field_from_a_row() {
let field = "amigos";
fn gets_matching_field_from_a_row() {
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! {
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("jonathan"),
string("yehuda"),
]),
});
assert_eq!(
table(&vec![
string("andres"),
string("jonathan"),
string("yehuda")
]),
*row.get_data_by_key(field).unwrap()
);
let (replacement, tag) = string("jonas").into_parts();
let actual = sample
.replace_data_at_column_path(tag, &field_path, replacement)
.unwrap();
assert_eq!(actual, row(indexmap! {"amigos".into() => string("jonas")}));
}
#[test]
fn gets_the_first_row_with_matching_field_from_rows_inside_a_table() {
let field = "name";
let table = Value::table(&vec![
row(indexmap! {field.into() => string("andres")}),
row(indexmap! {field.into() => string("jonathan")}),
row(indexmap! {field.into() => string("yehuda")}),
fn replaces_matching_field_from_nested_rows_inside_a_row() {
let field_path = column_path(&vec![
string("package"),
string("authors"),
string("los.3.caballeros"),
]);
assert_eq!(string("andres"), *table.get_data_by_key(field).unwrap());
}
#[test]
fn gets_the_matching_field_from_nested_rows_inside_a_row() {
let _field = "package.version";
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! {
let sample = Value::row(indexmap! {
"package".into() => row(indexmap! {
"name".into() => string("nu"),
"version".into() => string("0.4.0"),
"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() => table(&vec![string("andres::yehuda::jonathan")])
})
})
});
assert_eq!(version, **row.get_data_by_column_path(tag, &field).unwrap())
}
let (replacement, tag) = table(&vec![string("yehuda::jonathan::andres")]).into_parts();
#[test]
fn gets_the_first_row_with_matching_field_from_nested_rows_inside_a_table() {
let _field = "package.authors.name";
let field = vec![string("package"), string("authors"), string("name")];
let field = column_path(&field);
let actual = sample
.replace_data_at_column_path(tag.clone(), &field_path, replacement.clone())
.unwrap();
let (name, tag) = string("Andrés N. Robalino").into_parts();
let row = Value::row(indexmap! {
assert_eq!(
actual,
Value::row(indexmap! {
"package".into() => row(indexmap! {
"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")})
]),
"name".into() => string("nu"),
"version".into() => string("0.4.0"),
"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]
fn replaces_matching_field_from_rows_inside_a_table() {
let field_path = column_path(&vec![
string("shell_policy"),
string("releases"),
string("nu.version.arepa"),
]);
let sample = Value::row(indexmap! {
"shell_policy".into() => row(indexmap! {
"releases".into() => table(&vec![
row(indexmap! {
"nu.version.arepa".into() => row(indexmap! {
"code".into() => string("0.4.0"), "tag_line".into() => string("GitHub-era")
})
}),
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")
})
})
])
})
});
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)
);
}
}

View file

@ -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 {
let mut debug = f.debug_struct("Dictionary");