mirror of
https://github.com/nushell/nushell
synced 2025-01-13 21:55:07 +00:00
Add and edit functionality
This commit is contained in:
parent
12a785f2a2
commit
ec7d49b0d2
8 changed files with 270 additions and 32 deletions
|
@ -90,6 +90,14 @@ path = "src/lib.rs"
|
||||||
name = "nu_plugin_inc"
|
name = "nu_plugin_inc"
|
||||||
path = "src/plugins/inc.rs"
|
path = "src/plugins/inc.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "nu_plugin_add"
|
||||||
|
path = "src/plugins/add.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "nu_plugin_edit"
|
||||||
|
path = "src/plugins/edit.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "nu_plugin_skip"
|
name = "nu_plugin_skip"
|
||||||
path = "src/plugins/skip.rs"
|
path = "src/plugins/skip.rs"
|
||||||
|
|
|
@ -136,6 +136,8 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat
|
||||||
| sort-by ...columns | Sort by the given columns |
|
| sort-by ...columns | Sort by the given columns |
|
||||||
| where condition | Filter table to match the condition |
|
| where condition | Filter table to match the condition |
|
||||||
| inc (field) | Increment a value or version. Optional use the field of a table |
|
| inc (field) | Increment a value or version. Optional use the field of a table |
|
||||||
|
| add field value | Add a new field to the table |
|
||||||
|
| edit field value | Edit an existing field to have a new value |
|
||||||
| skip amount | Skip a number of rows |
|
| skip amount | Skip a number of rows |
|
||||||
| first amount | Show only the first number of rows |
|
| first amount | Show only the first number of rows |
|
||||||
| to-array | Collapse rows into a single list |
|
| to-array | Collapse rows into a single list |
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::object::{Primitive, Value};
|
use crate::object::{Primitive, Value};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use log::trace;
|
|
||||||
|
|
||||||
pub fn value_to_json_value(v: &Value) -> serde_json::Value {
|
pub fn value_to_json_value(v: &Value) -> serde_json::Value {
|
||||||
match v {
|
match v {
|
||||||
|
|
|
@ -36,36 +36,17 @@ pub fn to_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
|
||||||
Ok(out
|
Ok(out
|
||||||
.values
|
.values
|
||||||
.map(move |a| {
|
.map(move |a| match toml::to_string(&value_to_toml_value(&a)) {
|
||||||
match toml::to_string(&value_to_toml_value(&a)) {
|
Ok(val) => {
|
||||||
Ok(val) => {
|
return ReturnSuccess::value(
|
||||||
return ReturnSuccess::value(
|
Value::Primitive(Primitive::String(val)).spanned(name_span),
|
||||||
Value::Primitive(Primitive::String(val)).spanned(name_span),
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(err) => Err(ShellError::type_error(
|
|
||||||
"serializable to toml",
|
|
||||||
format!("{:?} - {:?}", a.type_name(), err).spanned(name_span),
|
|
||||||
)), // toml::Value::String(String) => {
|
|
||||||
// return ReturnSuccess::value(
|
|
||||||
// Value::Primitive(Primitive::String(x)).spanned(name_span),
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// toml::Value::Integer(i64) => "Integer",
|
|
||||||
// toml::Value::Float(f64) => "Decimal",
|
|
||||||
// toml::Value::Boolean(bool) => "Boolean",
|
|
||||||
// toml::Value::Datetime(Datetime) => "Date",
|
|
||||||
// toml::Value::Array(Array) => "Array",
|
|
||||||
// toml::Value::Table(Table) => "Table",
|
|
||||||
}
|
}
|
||||||
// return Err(ShellError::type_error("String", ty.spanned(name_span)));
|
|
||||||
|
|
||||||
// Err(_) => Err(ShellError::maybe_labeled_error(
|
Err(err) => Err(ShellError::type_error(
|
||||||
// "Can not convert to TOML string",
|
"Can not convert to a TOML string",
|
||||||
// "can not convert piped data to TOML string",
|
format!("{:?} - {:?}", a.type_name(), err).spanned(name_span),
|
||||||
// name_span,
|
)),
|
||||||
// )),
|
|
||||||
})
|
})
|
||||||
.to_output_stream())
|
.to_output_stream())
|
||||||
}
|
}
|
||||||
|
|
|
@ -362,6 +362,56 @@ impl Value {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert_data_at_path(
|
||||||
|
&'a self,
|
||||||
|
span: Span,
|
||||||
|
path: &str,
|
||||||
|
new_value: Value,
|
||||||
|
) -> Option<Spanned<Value>> {
|
||||||
|
let mut new_obj = self.clone();
|
||||||
|
|
||||||
|
let split_path: Vec<_> = path.split(".").collect();
|
||||||
|
|
||||||
|
if let Value::Object(ref mut o) = new_obj {
|
||||||
|
let mut current = o;
|
||||||
|
for idx in 0..split_path.len() - 1 {
|
||||||
|
match current.entries.get_mut(split_path[idx]) {
|
||||||
|
Some(next) => {
|
||||||
|
if idx == (split_path.len() - 2) {
|
||||||
|
match &mut next.item {
|
||||||
|
Value::Object(o) => {
|
||||||
|
o.entries.insert(
|
||||||
|
split_path[idx + 1].to_string(),
|
||||||
|
Spanned {
|
||||||
|
item: new_value,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(Spanned {
|
||||||
|
item: new_obj,
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
match next.item {
|
||||||
|
Value::Object(ref mut o) => {
|
||||||
|
current = o;
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub fn replace_data_at_path(
|
pub fn replace_data_at_path(
|
||||||
&'a self,
|
&'a self,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
89
src/plugins/add.rs
Normal file
89
src/plugins/add.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use nu::{
|
||||||
|
serve_plugin, CallInfo, CommandConfig, Plugin, PositionalType, Primitive, ReturnSuccess,
|
||||||
|
ReturnValue, ShellError, Spanned, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Add {
|
||||||
|
field: Option<String>,
|
||||||
|
value: Option<Value>,
|
||||||
|
}
|
||||||
|
impl Add {
|
||||||
|
fn new() -> Add {
|
||||||
|
Add {
|
||||||
|
field: None,
|
||||||
|
value: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(&self, value: Spanned<Value>) -> Result<Spanned<Value>, ShellError> {
|
||||||
|
match (value.item, self.value.clone()) {
|
||||||
|
(obj @ Value::Object(_), Some(v)) => match &self.field {
|
||||||
|
Some(f) => match obj.insert_data_at_path(value.span, &f, v) {
|
||||||
|
Some(v) => return Ok(v),
|
||||||
|
None => {
|
||||||
|
return Err(ShellError::string(
|
||||||
|
"add could not find place to insert field",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => Err(ShellError::string(
|
||||||
|
"add needs a field when adding a value to an object",
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
x => Err(ShellError::string(format!(
|
||||||
|
"Unrecognized type in stream: {:?}",
|
||||||
|
x
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin for Add {
|
||||||
|
fn config(&mut self) -> Result<CommandConfig, ShellError> {
|
||||||
|
Ok(CommandConfig {
|
||||||
|
name: "add".to_string(),
|
||||||
|
positional: vec![
|
||||||
|
PositionalType::mandatory_any("Field"),
|
||||||
|
PositionalType::mandatory_any("Value"),
|
||||||
|
],
|
||||||
|
is_filter: true,
|
||||||
|
is_sink: false,
|
||||||
|
named: IndexMap::new(),
|
||||||
|
rest_positional: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
|
||||||
|
if let Some(args) = call_info.args.positional {
|
||||||
|
match &args[0] {
|
||||||
|
Spanned {
|
||||||
|
item: Value::Primitive(Primitive::String(s)),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
self.field = Some(s.clone());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(ShellError::string(format!(
|
||||||
|
"Unrecognized type in params: {:?}",
|
||||||
|
args[0]
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &args[1] {
|
||||||
|
Spanned { item: v, .. } => {
|
||||||
|
self.value = Some(v.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter(&mut self, input: Spanned<Value>) -> Result<Vec<ReturnValue>, ShellError> {
|
||||||
|
Ok(vec![ReturnSuccess::value(self.add(input)?)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
serve_plugin(&mut Add::new());
|
||||||
|
}
|
89
src/plugins/edit.rs
Normal file
89
src/plugins/edit.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use nu::{
|
||||||
|
serve_plugin, CallInfo, CommandConfig, Plugin, PositionalType, Primitive, ReturnSuccess,
|
||||||
|
ReturnValue, ShellError, Spanned, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Edit {
|
||||||
|
field: Option<String>,
|
||||||
|
value: Option<Value>,
|
||||||
|
}
|
||||||
|
impl Edit {
|
||||||
|
fn new() -> Edit {
|
||||||
|
Edit {
|
||||||
|
field: None,
|
||||||
|
value: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edit(&self, value: Spanned<Value>) -> Result<Spanned<Value>, ShellError> {
|
||||||
|
match (value.item, self.value.clone()) {
|
||||||
|
(obj @ Value::Object(_), Some(v)) => match &self.field {
|
||||||
|
Some(f) => match obj.replace_data_at_path(value.span, &f, v) {
|
||||||
|
Some(v) => return Ok(v),
|
||||||
|
None => {
|
||||||
|
return Err(ShellError::string(
|
||||||
|
"edit could not find place to insert field",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => Err(ShellError::string(
|
||||||
|
"edit needs a field when adding a value to an object",
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
x => Err(ShellError::string(format!(
|
||||||
|
"Unrecognized type in stream: {:?}",
|
||||||
|
x
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin for Edit {
|
||||||
|
fn config(&mut self) -> Result<CommandConfig, ShellError> {
|
||||||
|
Ok(CommandConfig {
|
||||||
|
name: "edit".to_string(),
|
||||||
|
positional: vec![
|
||||||
|
PositionalType::mandatory_any("Field"),
|
||||||
|
PositionalType::mandatory_any("Value"),
|
||||||
|
],
|
||||||
|
is_filter: true,
|
||||||
|
is_sink: false,
|
||||||
|
named: IndexMap::new(),
|
||||||
|
rest_positional: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
|
||||||
|
if let Some(args) = call_info.args.positional {
|
||||||
|
match &args[0] {
|
||||||
|
Spanned {
|
||||||
|
item: Value::Primitive(Primitive::String(s)),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
self.field = Some(s.clone());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(ShellError::string(format!(
|
||||||
|
"Unrecognized type in params: {:?}",
|
||||||
|
args[0]
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &args[1] {
|
||||||
|
Spanned { item: v, .. } => {
|
||||||
|
self.value = Some(v.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter(&mut self, input: Spanned<Value>) -> Result<Vec<ReturnValue>, ShellError> {
|
||||||
|
Ok(vec![ReturnSuccess::value(self.edit(input)?)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
serve_plugin(&mut Edit::new());
|
||||||
|
}
|
|
@ -4,10 +4,12 @@ use helpers::in_directory as cwd;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn external_num() {
|
fn external_num() {
|
||||||
nu!(output,
|
nu!(
|
||||||
|
output,
|
||||||
cwd("tests/fixtures/formats"),
|
cwd("tests/fixtures/formats"),
|
||||||
"open sgml_description.json | get glossary.GlossDiv.GlossList.GlossEntry.Height | echo $it");
|
"open sgml_description.json | get glossary.GlossDiv.GlossList.GlossEntry.Height | echo $it"
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(output, "10");
|
assert_eq!(output, "10");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,3 +21,21 @@ fn inc_plugin() {
|
||||||
|
|
||||||
assert_eq!(output, "11");
|
assert_eq!(output, "11");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_plugin() {
|
||||||
|
nu!(output,
|
||||||
|
cwd("tests/fixtures/formats"),
|
||||||
|
"open cargo_sample.toml | add dev-dependencies.newdep \"1\" | get dev-dependencies.newdep | echo $it");
|
||||||
|
|
||||||
|
assert_eq!(output, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn edit_plugin() {
|
||||||
|
nu!(output,
|
||||||
|
cwd("tests/fixtures/formats"),
|
||||||
|
"open cargo_sample.toml | edit dev-dependencies.pretty_assertions \"7\" | get dev-dependencies.pretty_assertions | echo $it");
|
||||||
|
|
||||||
|
assert_eq!(output, "7");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue