mirror of
https://github.com/nushell/nushell.git
synced 2025-06-30 14:40:06 +02:00
Reimplement parsers with nu-serde (#3880)
The nu-serde crate allows us to become much more generic with respect to how we convert output to `nu-protocol::Value`s. This allows us to remove a lot of the special-case code that we wrote for deserializing JSON values. Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
@ -19,6 +19,7 @@ nu-path = { version = "0.35.0", path="../nu-path" }
|
||||
nu-parser = { version = "0.35.0", path="../nu-parser" }
|
||||
nu-plugin = { version = "0.35.0", path="../nu-plugin" }
|
||||
nu-protocol = { version = "0.35.0", path="../nu-protocol" }
|
||||
nu-serde = { version = "0.35.0", path="../nu-serde" }
|
||||
nu-source = { version = "0.35.0", path="../nu-source" }
|
||||
nu-stream = { version = "0.35.0", path="../nu-stream" }
|
||||
nu-table = { version = "0.35.0", path="../nu-table" }
|
||||
@ -84,6 +85,7 @@ sha2 = "0.9.3"
|
||||
strip-ansi-escapes = "0.1.0"
|
||||
sxd-document = "0.3.2"
|
||||
sxd-xpath = "0.4.2"
|
||||
thiserror = "1.0.26"
|
||||
tempfile = "3.2.0"
|
||||
term = { version="0.7.0", optional=true }
|
||||
term_size = "0.3.2"
|
||||
|
@ -1,9 +1,18 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, UntaggedValue, Value};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DeserializationError {
|
||||
#[error("Failed to parse input as INI")]
|
||||
Ini(#[from] serde_ini::de::Error),
|
||||
|
||||
#[error("Failed to convert to a nushell value")]
|
||||
Nu(#[from] nu_serde::Error),
|
||||
}
|
||||
|
||||
pub struct FromIni;
|
||||
|
||||
impl WholeStreamCommand for FromIni {
|
||||
@ -24,39 +33,13 @@ impl WholeStreamCommand for FromIni {
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_ini_second_to_nu_value(v: &HashMap<String, String>, tag: impl Into<Tag>) -> Value {
|
||||
let mut second = TaggedDictBuilder::new(tag);
|
||||
|
||||
for (key, value) in v.iter() {
|
||||
second.insert_untagged(key.clone(), Primitive::String(value.clone()));
|
||||
}
|
||||
|
||||
second.into_value()
|
||||
}
|
||||
|
||||
fn convert_ini_top_to_nu_value(
|
||||
v: &HashMap<String, HashMap<String, String>>,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Value {
|
||||
let tag = tag.into();
|
||||
let mut top_level = TaggedDictBuilder::new(tag.clone());
|
||||
|
||||
for (key, value) in v.iter() {
|
||||
top_level.insert_value(
|
||||
key.clone(),
|
||||
convert_ini_second_to_nu_value(value, tag.clone()),
|
||||
);
|
||||
}
|
||||
|
||||
top_level.into_value()
|
||||
}
|
||||
|
||||
pub fn from_ini_string_to_value(
|
||||
s: String,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, serde_ini::de::Error> {
|
||||
) -> Result<Value, DeserializationError> {
|
||||
let v: HashMap<String, HashMap<String, String>> = serde_ini::from_str(&s)?;
|
||||
Ok(convert_ini_top_to_nu_value(&v, tag))
|
||||
|
||||
Ok(nu_serde::to_value(v, tag)?)
|
||||
}
|
||||
|
||||
fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
@ -72,13 +55,20 @@ fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
} => Ok(list.into_iter().into_output_stream()),
|
||||
x => Ok(OutputStream::one(x)),
|
||||
},
|
||||
Err(_) => Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as INI",
|
||||
Err(DeserializationError::Ini(e)) => Err(ShellError::labeled_error_with_secondary(
|
||||
format!("Could not parse as INI: {}", e),
|
||||
"input cannot be parsed as INI",
|
||||
&tag,
|
||||
"value originates from here",
|
||||
concat_string.tag,
|
||||
)),
|
||||
Err(DeserializationError::Nu(e)) => Err(ShellError::labeled_error_with_secondary(
|
||||
format!("Could not convert to nushell value: {}", e),
|
||||
"input cannot be converted to nushell",
|
||||
&tag,
|
||||
"value originates from here",
|
||||
concat_string.tag,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,16 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, UntaggedValue, Value};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DeserializationError {
|
||||
#[error("Failed to parse input as JSON")]
|
||||
Json(#[from] nu_json::Error),
|
||||
|
||||
#[error("Failed to convert JSON to a nushell value")]
|
||||
Nu(#[from] Box<nu_serde::Error>),
|
||||
}
|
||||
|
||||
pub struct FromJson;
|
||||
|
||||
@ -27,39 +36,13 @@ impl WholeStreamCommand for FromJson {
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_json_value_to_nu_value(v: &nu_json::Value, tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
let span = tag.span;
|
||||
|
||||
match v {
|
||||
nu_json::Value::Null => UntaggedValue::Primitive(Primitive::Nothing).into_value(&tag),
|
||||
nu_json::Value::Bool(b) => UntaggedValue::boolean(*b).into_value(&tag),
|
||||
nu_json::Value::F64(n) => UntaggedValue::decimal_from_float(*n, span).into_value(&tag),
|
||||
nu_json::Value::U64(n) => UntaggedValue::big_int(*n).into_value(&tag),
|
||||
nu_json::Value::I64(n) => UntaggedValue::int(*n).into_value(&tag),
|
||||
nu_json::Value::String(s) => {
|
||||
UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(&tag)
|
||||
}
|
||||
nu_json::Value::Array(a) => UntaggedValue::Table(
|
||||
a.iter()
|
||||
.map(|x| convert_json_value_to_nu_value(x, &tag))
|
||||
.collect(),
|
||||
)
|
||||
.into_value(tag),
|
||||
nu_json::Value::Object(o) => {
|
||||
let mut collected = TaggedDictBuilder::new(&tag);
|
||||
for (k, v) in o.iter() {
|
||||
collected.insert_value(k.clone(), convert_json_value_to_nu_value(v, &tag));
|
||||
}
|
||||
|
||||
collected.into_value()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_json_string_to_value(s: String, tag: impl Into<Tag>) -> nu_json::Result<Value> {
|
||||
pub fn from_json_string_to_value(
|
||||
s: String,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, DeserializationError> {
|
||||
let v: nu_json::Value = nu_json::from_str(&s)?;
|
||||
Ok(convert_json_value_to_nu_value(&v, tag))
|
||||
|
||||
Ok(nu_serde::to_value(v, tag).map_err(Box::new)?)
|
||||
}
|
||||
|
||||
fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
@ -81,7 +64,19 @@ fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
match from_json_string_to_value(json_str, &name_tag) {
|
||||
Ok(x) => Some(x),
|
||||
Err(e) => {
|
||||
Err(DeserializationError::Nu(e)) => {
|
||||
let mut message = "Could not convert JSON to nushell value (".to_string();
|
||||
message.push_str(&e.to_string());
|
||||
message.push(')');
|
||||
Some(Value::error(ShellError::labeled_error_with_secondary(
|
||||
message,
|
||||
"input cannot be converted to nushell values",
|
||||
name_tag.clone(),
|
||||
"value originates from here",
|
||||
concat_string.tag.clone(),
|
||||
)))
|
||||
}
|
||||
Err(DeserializationError::Json(e)) => {
|
||||
let mut message = "Could not parse as JSON (".to_string();
|
||||
message.push_str(&e.to_string());
|
||||
message.push(')');
|
||||
@ -107,7 +102,7 @@ fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
x => Ok(OutputStream::one(x)),
|
||||
},
|
||||
Err(e) => {
|
||||
Err(DeserializationError::Json(e)) => {
|
||||
let mut message = "Could not parse as JSON (".to_string();
|
||||
message.push_str(&e.to_string());
|
||||
message.push(')');
|
||||
@ -122,6 +117,20 @@ fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
),
|
||||
)))
|
||||
}
|
||||
Err(DeserializationError::Nu(e)) => {
|
||||
let mut message = "Could not convert JSON to nushell value (".to_string();
|
||||
message.push_str(&e.to_string());
|
||||
message.push(')');
|
||||
Ok(OutputStream::one(Value::error(
|
||||
ShellError::labeled_error_with_secondary(
|
||||
message,
|
||||
"input cannot be converted to nushell values",
|
||||
name_tag,
|
||||
"value originates from here",
|
||||
concat_string.tag,
|
||||
),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,16 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, UntaggedValue, Value};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DeserializationError {
|
||||
#[error("Failed to parse input as TOML")]
|
||||
Toml(#[from] toml::de::Error),
|
||||
|
||||
#[error("Failed to convert to a nushell value")]
|
||||
Nu(#[from] Box<nu_serde::Error>),
|
||||
}
|
||||
|
||||
pub struct FromToml;
|
||||
|
||||
@ -23,41 +32,13 @@ impl WholeStreamCommand for FromToml {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
let span = tag.span;
|
||||
|
||||
match v {
|
||||
toml::Value::Boolean(b) => UntaggedValue::boolean(*b).into_value(tag),
|
||||
toml::Value::Integer(n) => UntaggedValue::int(*n).into_value(tag),
|
||||
toml::Value::Float(n) => UntaggedValue::decimal_from_float(*n, span).into_value(tag),
|
||||
toml::Value::String(s) => {
|
||||
UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(tag)
|
||||
}
|
||||
toml::Value::Array(a) => UntaggedValue::Table(
|
||||
a.iter()
|
||||
.map(|x| convert_toml_value_to_nu_value(x, &tag))
|
||||
.collect(),
|
||||
)
|
||||
.into_value(tag),
|
||||
toml::Value::Datetime(dt) => {
|
||||
UntaggedValue::Primitive(Primitive::String(dt.to_string())).into_value(tag)
|
||||
}
|
||||
toml::Value::Table(t) => {
|
||||
let mut collected = TaggedDictBuilder::new(&tag);
|
||||
|
||||
for (k, v) in t.iter() {
|
||||
collected.insert_value(k.clone(), convert_toml_value_to_nu_value(v, &tag));
|
||||
}
|
||||
|
||||
collected.into_value()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_toml_string_to_value(s: String, tag: impl Into<Tag>) -> Result<Value, toml::de::Error> {
|
||||
pub fn from_toml_string_to_value(
|
||||
s: String,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, DeserializationError> {
|
||||
let v: toml::Value = s.parse::<toml::Value>()?;
|
||||
Ok(convert_toml_value_to_nu_value(&v, tag))
|
||||
|
||||
Ok(nu_serde::to_value(v, tag).map_err(Box::new)?)
|
||||
}
|
||||
|
||||
pub fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
Reference in New Issue
Block a user