mirror of
https://github.com/nushell/nushell
synced 2025-01-15 06:34:15 +00:00
Merge pull request #848 from andrasio/column_path-inc
Inc plugin increments appropiately given a table containing a version.
This commit is contained in:
commit
c45ddc8f22
4 changed files with 164 additions and 33 deletions
130
src/data/base.rs
130
src/data/base.rs
|
@ -8,6 +8,7 @@ use crate::Text;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use chrono_humanize::Humanize;
|
use chrono_humanize::Humanize;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
use indexmap::IndexMap;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -452,7 +453,7 @@ impl Value {
|
||||||
match self {
|
match self {
|
||||||
Value::Primitive(p) => p.type_name(),
|
Value::Primitive(p) => p.type_name(),
|
||||||
Value::Row(_) => format!("row"),
|
Value::Row(_) => format!("row"),
|
||||||
Value::Table(_) => format!("list"),
|
Value::Table(_) => format!("table"),
|
||||||
Value::Block(_) => format!("block"),
|
Value::Block(_) => format!("block"),
|
||||||
Value::Error(_) => format!("error"),
|
Value::Error(_) => format!("error"),
|
||||||
}
|
}
|
||||||
|
@ -684,6 +685,15 @@ impl Value {
|
||||||
Value::Row(ref mut o) => {
|
Value::Row(ref mut o) => {
|
||||||
current = 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
pub fn string(s: impl Into<String>) -> Value {
|
||||||
Value::Primitive(Primitive::String(s.into()))
|
Value::Primitive(Primitive::String(s.into()))
|
||||||
}
|
}
|
||||||
|
@ -927,3 +952,106 @@ fn coerce_compare_primitive(
|
||||||
_ => return Err((left.type_name(), right.type_name())),
|
_ => 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#![recursion_limit = "1024"]
|
#![recursion_limit = "1024"]
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate indexmap;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod prelude;
|
mod prelude;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate indexmap;
|
||||||
|
|
||||||
use nu::{
|
use nu::{
|
||||||
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
|
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
|
||||||
SyntaxShape, Tag, Tagged, TaggedDictBuilder, Value,
|
SyntaxShape, Tag, Tagged, TaggedItem, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Embed {
|
struct Embed {
|
||||||
|
@ -16,22 +19,8 @@ impl Embed {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn embed(&mut self, value: Tagged<Value>) -> Result<(), ShellError> {
|
fn embed(&mut self, value: Tagged<Value>) -> Result<(), ShellError> {
|
||||||
match value {
|
self.values.push(value);
|
||||||
Tagged { item, tag } => match &self.field {
|
Ok(())
|
||||||
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,
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,8 +28,7 @@ impl Plugin for Embed {
|
||||||
fn config(&mut self) -> Result<Signature, ShellError> {
|
fn config(&mut self) -> Result<Signature, ShellError> {
|
||||||
Ok(Signature::build("embed")
|
Ok(Signature::build("embed")
|
||||||
.desc("Embeds a new field to the table.")
|
.desc("Embeds a new field to the table.")
|
||||||
.required("Field", SyntaxShape::String)
|
.optional("field", SyntaxShape::String)
|
||||||
.rest(SyntaxShape::String)
|
|
||||||
.filter())
|
.filter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,15 +55,15 @@ impl Plugin for Embed {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end_filter(&mut self) -> Result<Vec<ReturnValue>, ShellError> {
|
fn end_filter(&mut self) -> Result<Vec<ReturnValue>, ShellError> {
|
||||||
let mut root = TaggedDictBuilder::new(Tag::unknown());
|
let row = Value::row(indexmap! {
|
||||||
root.insert_tagged(
|
match &self.field {
|
||||||
self.field.as_ref().unwrap(),
|
Some(key) => key.clone(),
|
||||||
Tagged {
|
None => "root".into(),
|
||||||
item: Value::Table(self.values.clone()),
|
} => Value::table(&self.values).tagged(Tag::unknown()),
|
||||||
tag: Tag::unknown(),
|
})
|
||||||
},
|
.tagged(Tag::unknown());
|
||||||
);
|
|
||||||
Ok(vec![ReturnSuccess::value(root.into_tagged_value())])
|
Ok(vec![ReturnSuccess::value(row)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub enum SemVerAction {
|
||||||
Patch,
|
Patch,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ColumnPath = Tagged<Vec<Tagged<String>>>;
|
pub type ColumnPath = Vec<Tagged<String>>;
|
||||||
|
|
||||||
struct Inc {
|
struct Inc {
|
||||||
field: Option<ColumnPath>,
|
field: Option<ColumnPath>,
|
||||||
|
@ -83,6 +83,16 @@ impl Inc {
|
||||||
Ok(Value::bytes(b + 1 as u64).tagged(value.tag()))
|
Ok(Value::bytes(b + 1 as u64).tagged(value.tag()))
|
||||||
}
|
}
|
||||||
Value::Primitive(Primitive::String(ref s)) => Ok(self.apply(&s)?.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 {
|
Value::Row(_) => match self.field {
|
||||||
Some(ref f) => {
|
Some(ref f) => {
|
||||||
let replacement = match value.item.get_data_by_column_path(value.tag(), 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(
|
return Err(ShellError::labeled_error(
|
||||||
"inc could not find field to replace",
|
"inc could not find field to replace",
|
||||||
"column name",
|
"column name",
|
||||||
&f.tag,
|
value.tag(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match value.item.replace_data_at_column_path(
|
match value.item.replace_data_at_column_path(
|
||||||
value.tag(),
|
value.tag(),
|
||||||
f,
|
f,
|
||||||
|
@ -105,7 +116,7 @@ impl Inc {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"inc could not find field to replace",
|
"inc could not find field to replace",
|
||||||
"column name",
|
"column name",
|
||||||
&f.tag,
|
value.tag(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,7 +162,7 @@ impl Plugin for Inc {
|
||||||
item: Value::Table(_),
|
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())),
|
value => return Err(ShellError::type_error("table", value.tagged_type_name())),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue