mirror of
https://github.com/nushell/nushell
synced 2024-11-11 15:37:07 +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_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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#![recursion_limit = "1024"]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate indexmap;
|
||||
|
||||
#[macro_use]
|
||||
mod prelude;
|
||||
|
||||
|
|
|
@ -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)])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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())),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue