Merge pull request #848 from andrasio/column_path-inc

Inc plugin increments appropiately given a table containing a version.
This commit is contained in:
Jonathan Turner 2019-10-20 07:27:47 +13:00 committed by GitHub
commit c45ddc8f22
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 164 additions and 33 deletions

View file

@ -8,6 +8,7 @@ use crate::Text;
use chrono::{DateTime, Utc};
use chrono_humanize::Humanize;
use derive_new::new;
use indexmap::IndexMap;
use log::trace;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -452,7 +453,7 @@ impl Value {
match self {
Value::Primitive(p) => p.type_name(),
Value::Row(_) => format!("row"),
Value::Table(_) => format!("list"),
Value::Table(_) => format!("table"),
Value::Block(_) => format!("block"),
Value::Error(_) => format!("error"),
}
@ -684,6 +685,15 @@ impl Value {
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,
}
}
@ -769,6 +779,21 @@ impl Value {
}
}
#[allow(unused)]
pub fn row(entries: IndexMap<String, Tagged<Value>>) -> Value {
Value::Row(entries.into())
}
pub fn table(list: &Vec<Tagged<Value>>) -> Value {
let mut out = vec![];
for v in list {
out.push(v.clone());
}
Value::Table(out)
}
pub fn string(s: impl Into<String>) -> Value {
Value::Primitive(Primitive::String(s.into()))
}
@ -927,3 +952,106 @@ fn coerce_compare_primitive(
_ => return Err((left.type_name(), right.type_name())),
})
}
#[cfg(test)]
mod tests {
use crate::data::meta::*;
use crate::Value;
use indexmap::IndexMap;
fn string(input: impl Into<String>) -> Tagged<Value> {
Value::string(input.into()).tagged_unknown()
}
fn row(entries: IndexMap<String, Tagged<Value>>) -> Tagged<Value> {
Value::row(entries).tagged_unknown()
}
fn table(list: &Vec<Tagged<Value>>) -> Tagged<Value> {
Value::table(list).tagged_unknown()
}
fn column_path(paths: &Vec<Tagged<Value>>) -> Tagged<Vec<Tagged<String>>> {
let paths = paths
.iter()
.map(|p| string(p.as_string().unwrap()))
.collect();
let table = table(&paths);
table.as_column_path().unwrap()
}
#[test]
fn gets_the_matching_field_from_a_row() {
let field = "amigos";
let row = Value::row(indexmap! {
field.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()
);
}
#[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")}),
]);
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! {
"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())
}
#[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 (name, tag) = string("Andrés N. Robalino").into_parts();
let row = 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"),
})
});
assert_eq!(name, **row.get_data_by_column_path(tag, &field).unwrap())
}
}

View file

@ -1,5 +1,9 @@
#![recursion_limit = "1024"]
#[cfg(test)]
#[macro_use]
extern crate indexmap;
#[macro_use]
mod prelude;

View file

@ -1,6 +1,9 @@
#[macro_use]
extern crate indexmap;
use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
SyntaxShape, Tag, Tagged, TaggedDictBuilder, Value,
SyntaxShape, Tag, Tagged, TaggedItem, Value,
};
struct Embed {
@ -16,22 +19,8 @@ impl Embed {
}
fn embed(&mut self, value: Tagged<Value>) -> Result<(), ShellError> {
match value {
Tagged { item, tag } => match &self.field {
Some(_) => {
self.values.push(Tagged {
item: item,
tag: tag,
});
Ok(())
}
None => Err(ShellError::labeled_error(
"embed needs a field when embedding a value",
"original value",
&tag,
)),
},
}
self.values.push(value);
Ok(())
}
}
@ -39,8 +28,7 @@ impl Plugin for Embed {
fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("embed")
.desc("Embeds a new field to the table.")
.required("Field", SyntaxShape::String)
.rest(SyntaxShape::String)
.optional("field", SyntaxShape::String)
.filter())
}
@ -67,15 +55,15 @@ impl Plugin for Embed {
}
fn end_filter(&mut self) -> Result<Vec<ReturnValue>, ShellError> {
let mut root = TaggedDictBuilder::new(Tag::unknown());
root.insert_tagged(
self.field.as_ref().unwrap(),
Tagged {
item: Value::Table(self.values.clone()),
tag: Tag::unknown(),
},
);
Ok(vec![ReturnSuccess::value(root.into_tagged_value())])
let row = Value::row(indexmap! {
match &self.field {
Some(key) => key.clone(),
None => "root".into(),
} => Value::table(&self.values).tagged(Tag::unknown()),
})
.tagged(Tag::unknown());
Ok(vec![ReturnSuccess::value(row)])
}
}

View file

@ -14,7 +14,7 @@ pub enum SemVerAction {
Patch,
}
pub type ColumnPath = Tagged<Vec<Tagged<String>>>;
pub type ColumnPath = Vec<Tagged<String>>;
struct Inc {
field: Option<ColumnPath>,
@ -83,6 +83,16 @@ impl Inc {
Ok(Value::bytes(b + 1 as u64).tagged(value.tag()))
}
Value::Primitive(Primitive::String(ref s)) => Ok(self.apply(&s)?.tagged(value.tag())),
Value::Table(values) => {
if values.len() == 1 {
return Ok(Value::Table(vec![self.inc(values[0].clone())?]).tagged(value.tag()));
} else {
return Err(ShellError::type_error(
"incrementable value",
value.tagged_type_name(),
));
}
}
Value::Row(_) => match self.field {
Some(ref f) => {
let replacement = match value.item.get_data_by_column_path(value.tag(), f) {
@ -91,10 +101,11 @@ impl Inc {
return Err(ShellError::labeled_error(
"inc could not find field to replace",
"column name",
&f.tag,
value.tag(),
))
}
};
match value.item.replace_data_at_column_path(
value.tag(),
f,
@ -105,7 +116,7 @@ impl Inc {
return Err(ShellError::labeled_error(
"inc could not find field to replace",
"column name",
&f.tag,
value.tag(),
))
}
}
@ -151,7 +162,7 @@ impl Plugin for Inc {
item: Value::Table(_),
..
} => {
self.field = Some(table.as_column_path()?);
self.field = Some(table.as_column_path()?.item().to_vec());
}
value => return Err(ShellError::type_error("table", value.tagged_type_name())),
}