Add 'from json'

This commit is contained in:
JT
2021-10-01 18:11:49 +13:00
parent d34e083976
commit 3e232a5db8
37 changed files with 4722 additions and 42 deletions

View File

@ -6,8 +6,8 @@ use nu_protocol::{
};
use crate::{
Alias, Benchmark, BuildString, Def, Do, Each, External, For, Git, GitCheckout, If, Length, Let,
LetEnv, Lines, ListGitBranches, Ls, Module, Table, Use, Where,
Alias, Benchmark, BuildString, Def, Do, Each, External, For, From, FromJson, Git, GitCheckout,
If, Length, Let, LetEnv, Lines, ListGitBranches, Ls, Module, Table, Use, Where,
};
pub fn create_default_context() -> Rc<RefCell<EngineState>> {
@ -20,41 +20,26 @@ pub fn create_default_context() -> Rc<RefCell<EngineState>> {
Signature::build("where").required("cond", SyntaxShape::RowCondition, "condition");
working_set.add_decl(sig.predeclare());
working_set.add_decl(Box::new(If));
working_set.add_decl(Box::new(Let));
working_set.add_decl(Box::new(LetEnv));
working_set.add_decl(Box::new(Alias));
working_set.add_decl(Box::new(BuildString));
working_set.add_decl(Box::new(Def));
working_set.add_decl(Box::new(For));
working_set.add_decl(Box::new(Each));
working_set.add_decl(Box::new(Where));
working_set.add_decl(Box::new(Do));
working_set.add_decl(Box::new(Benchmark));
working_set.add_decl(Box::new(Length));
working_set.add_decl(Box::new(Ls));
working_set.add_decl(Box::new(Module));
working_set.add_decl(Box::new(Use));
working_set.add_decl(Box::new(Table));
working_set.add_decl(Box::new(BuildString));
working_set.add_decl(Box::new(Def));
working_set.add_decl(Box::new(Do));
working_set.add_decl(Box::new(Each));
working_set.add_decl(Box::new(External));
working_set.add_decl(Box::new(For));
working_set.add_decl(Box::new(From));
working_set.add_decl(Box::new(FromJson));
working_set.add_decl(Box::new(If));
working_set.add_decl(Box::new(Length));
working_set.add_decl(Box::new(Let));
working_set.add_decl(Box::new(LetEnv));
working_set.add_decl(Box::new(Lines));
working_set.add_decl(Box::new(Ls));
working_set.add_decl(Box::new(Module));
working_set.add_decl(Box::new(Table));
working_set.add_decl(Box::new(Use));
working_set.add_decl(Box::new(Where));
// This is a WIP proof of concept
working_set.add_decl(Box::new(ListGitBranches));

View File

@ -0,0 +1,28 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{ShellError, Signature, Value};
pub struct From;
impl Command for From {
fn name(&self) -> &str {
"from"
}
fn usage(&self) -> &str {
"Parse a string or binary data into structured data"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("from")
}
fn run(
&self,
_context: &EvaluationContext,
_call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, ShellError> {
Ok(Value::nothing())
}
}

View File

@ -0,0 +1,111 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{IntoValueStream, ShellError, Signature, Span, Value};
pub struct FromJson;
impl Command for FromJson {
fn name(&self) -> &str {
"from json"
}
fn usage(&self) -> &str {
"Convert from json to structured data"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("from json").switch(
"objects",
"treat each line as a separate value",
Some('o'),
)
}
fn run(
&self,
_context: &EvaluationContext,
call: &Call,
input: Value,
) -> Result<nu_protocol::Value, ShellError> {
let span = input.span();
let mut string_input = input.into_string();
string_input.push('\n');
// TODO: turn this into a structured underline of the nu_json error
if call.has_flag("objects") {
#[allow(clippy::needless_collect)]
let lines: Vec<String> = string_input.lines().map(|x| x.to_string()).collect();
Ok(Value::Stream {
stream: lines
.into_iter()
.map(move |mut x| {
x.push('\n');
match convert_string_to_value(x, span) {
Ok(v) => v,
Err(error) => Value::Error { error },
}
})
.into_value_stream(),
span,
})
} else {
convert_string_to_value(string_input, span)
}
}
}
fn convert_nujson_to_value(value: &nu_json::Value, span: Span) -> Value {
match value {
nu_json::Value::Array(array) => {
let v: Vec<Value> = array
.iter()
.map(|x| convert_nujson_to_value(x, span))
.collect();
Value::List { vals: v, span }
}
nu_json::Value::Bool(b) => Value::Bool { val: *b, span },
nu_json::Value::F64(f) => Value::Float { val: *f, span },
nu_json::Value::I64(i) => Value::Int { val: *i, span },
nu_json::Value::Null => Value::Nothing { span },
nu_json::Value::Object(k) => {
let mut cols = vec![];
let mut vals = vec![];
for item in k {
cols.push(item.0.clone());
vals.push(convert_nujson_to_value(item.1, span));
}
Value::Record { cols, vals, span }
}
nu_json::Value::U64(u) => {
if *u > i64::MAX as u64 {
Value::Error {
error: ShellError::CantConvert("i64 sized integer".into(), span),
}
} else {
Value::Int {
val: *u as i64,
span,
}
}
}
nu_json::Value::String(s) => Value::String {
val: s.clone(),
span,
},
}
}
fn convert_string_to_value(string_input: String, span: Span) -> Result<Value, ShellError> {
let result: Result<nu_json::Value, nu_json::Error> = nu_json::from_str(&string_input);
match result {
Ok(value) => Ok(convert_nujson_to_value(&value, span)),
Err(_x) => Err(ShellError::CantConvert(
"structured data from json".into(),
span,
)),
}
}

View File

@ -0,0 +1,5 @@
mod command;
mod json;
pub use command::From;
pub use json::FromJson;

View File

@ -0,0 +1,3 @@
mod from;
pub use from::*;

View File

@ -4,6 +4,7 @@ mod env;
mod experimental;
mod filesystem;
mod filters;
mod formats;
mod strings;
mod system;
mod viewers;
@ -14,6 +15,7 @@ pub use env::*;
pub use experimental::*;
pub use filesystem::*;
pub use filters::*;
pub use formats::*;
pub use strings::*;
pub use system::*;
pub use viewers::*;