Merge pull request #145 from jonathandturner/serial

Full-fidelity ser/de
This commit is contained in:
Jonathan Turner 2019-06-30 19:04:52 +12:00 committed by GitHub
commit ea18583e30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 112 additions and 199 deletions

1
Cargo.lock generated
View File

@ -1623,6 +1623,7 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.93 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]

View File

@ -23,7 +23,7 @@ dunce = "1.0.0"
indexmap = { version = "1.0.2", features = ["serde-1"] } indexmap = { version = "1.0.2", features = ["serde-1"] }
chrono-humanize = "0.0.11" chrono-humanize = "0.0.11"
byte-unit = "2.1.0" byte-unit = "2.1.0"
ordered-float = "1.0.2" ordered-float = {version = "1.0.2", features = ["serde"]}
prettyprint = "0.7.0" prettyprint = "0.7.0"
futures-preview = { version = "0.3.0-alpha.16", features = ["compat", "io-compat"] } futures-preview = { version = "0.3.0-alpha.16", features = ["compat", "io-compat"] }
futures-sink-preview = "0.3.0-alpha.16" futures-sink-preview = "0.3.0-alpha.16"

View File

@ -72,7 +72,6 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
command("reject", reject::reject), command("reject", reject::reject),
command("trim", trim::trim), command("trim", trim::trim),
command("to-array", to_array::to_array), command("to-array", to_array::to_array),
command("to-ini", to_ini::to_ini),
command("to-json", to_json::to_json), command("to-json", to_json::to_json),
command("to-toml", to_toml::to_toml), command("to-toml", to_toml::to_toml),
command("sort-by", sort_by::sort_by), command("sort-by", sort_by::sort_by),

View File

@ -31,7 +31,6 @@ crate mod split_row;
crate mod sysinfo; crate mod sysinfo;
crate mod table; crate mod table;
crate mod to_array; crate mod to_array;
crate mod to_ini;
crate mod to_json; crate mod to_json;
crate mod to_toml; crate mod to_toml;
crate mod tree; crate mod tree;

View File

@ -62,31 +62,31 @@ pub fn sysinfo(_args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut mem_idx = indexmap::IndexMap::new(); let mut mem_idx = indexmap::IndexMap::new();
mem_idx.insert( mem_idx.insert(
"total".to_string(), "total".to_string(),
Value::Primitive(Primitive::Bytes(x.total as u128 * 1024)), Value::Primitive(Primitive::Bytes(x.total as u64 * 1024)),
); );
mem_idx.insert( mem_idx.insert(
"free".to_string(), "free".to_string(),
Value::Primitive(Primitive::Bytes(x.free as u128 * 1024)), Value::Primitive(Primitive::Bytes(x.free as u64 * 1024)),
); );
mem_idx.insert( mem_idx.insert(
"avail".to_string(), "avail".to_string(),
Value::Primitive(Primitive::Bytes(x.avail as u128 * 1024)), Value::Primitive(Primitive::Bytes(x.avail as u64 * 1024)),
); );
mem_idx.insert( mem_idx.insert(
"buffers".to_string(), "buffers".to_string(),
Value::Primitive(Primitive::Bytes(x.buffers as u128 * 1024)), Value::Primitive(Primitive::Bytes(x.buffers as u64 * 1024)),
); );
mem_idx.insert( mem_idx.insert(
"cached".to_string(), "cached".to_string(),
Value::Primitive(Primitive::Bytes(x.cached as u128 * 1024)), Value::Primitive(Primitive::Bytes(x.cached as u64 * 1024)),
); );
mem_idx.insert( mem_idx.insert(
"swap total".to_string(), "swap total".to_string(),
Value::Primitive(Primitive::Bytes(x.swap_total as u128 * 1024)), Value::Primitive(Primitive::Bytes(x.swap_total as u64 * 1024)),
); );
mem_idx.insert( mem_idx.insert(
"swap free".to_string(), "swap free".to_string(),
Value::Primitive(Primitive::Bytes(x.swap_free as u128 * 1024)), Value::Primitive(Primitive::Bytes(x.swap_free as u64 * 1024)),
); );
idx.insert("mem".to_string(), Value::Object(Dictionary::from(mem_idx))); idx.insert("mem".to_string(), Value::Object(Dictionary::from(mem_idx)));
@ -151,10 +151,7 @@ pub fn sysinfo(_args: CommandArgs) -> Result<OutputStream, ShellError> {
"temp".to_string(), "temp".to_string(),
Value::float(component.get_temperature() as f64), Value::float(component.get_temperature() as f64),
); );
component_idx.insert( component_idx.insert("max".to_string(), Value::float(component.get_max() as f64));
"max".to_string(),
Value::float(component.get_max() as f64),
);
if let Some(critical) = component.get_critical() { if let Some(critical) = component.get_critical() {
component_idx.insert("critical".to_string(), Value::float(critical as f64)); component_idx.insert("critical".to_string(), Value::float(critical as f64));
} }
@ -177,10 +174,7 @@ pub fn sysinfo(_args: CommandArgs) -> Result<OutputStream, ShellError> {
"available".to_string(), "available".to_string(),
Value::bytes(disk.get_available_space()), Value::bytes(disk.get_available_space()),
); );
disk_idx.insert( disk_idx.insert("total".to_string(), Value::bytes(disk.get_total_space()));
"total".to_string(),
Value::bytes(disk.get_total_space()),
);
v.push(Value::Object(Dictionary::from(disk_idx))); v.push(Value::Object(Dictionary::from(disk_idx)));
} }
@ -194,7 +188,10 @@ pub fn sysinfo(_args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut network_idx = indexmap::IndexMap::new(); let mut network_idx = indexmap::IndexMap::new();
network_idx.insert("incoming".to_string(), Value::bytes(incoming)); network_idx.insert("incoming".to_string(), Value::bytes(incoming));
network_idx.insert("outgoing".to_string(), Value::bytes(outgoing)); network_idx.insert("outgoing".to_string(), Value::bytes(outgoing));
idx.insert("network".to_string(), Value::Object(Dictionary::from(network_idx))); idx.insert(
"network".to_string(),
Value::Object(Dictionary::from(network_idx)),
);
// println!("{:#?}", system.get_network()); // println!("{:#?}", system.get_network());

View File

@ -1,17 +0,0 @@
use crate::object::{Primitive, Value};
use crate::prelude::*;
pub fn to_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input;
let span = args.name_span;
Ok(out
.map(move |a| match serde_ini::to_string(&a) {
Ok(x) => ReturnValue::Value(Value::Primitive(Primitive::String(x))),
Err(_) => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
"Can not convert to INI string",
"can not convert piped data to INI string",
span,
)))),
})
.boxed())
}

View File

@ -1,17 +1,54 @@
use crate::object::{Primitive, Value}; use crate::object::{Primitive, Value};
use crate::prelude::*; use crate::prelude::*;
pub fn value_to_json_value(v: &Value) -> serde_json::Value {
match v {
Value::Primitive(Primitive::Boolean(b)) => serde_json::Value::Bool(*b),
Value::Primitive(Primitive::Bytes(b)) => {
serde_json::Value::Number(serde_json::Number::from(*b as u64))
}
Value::Primitive(Primitive::Date(d)) => serde_json::Value::String(d.to_string()),
Value::Primitive(Primitive::EndOfStream) => serde_json::Value::Null,
Value::Primitive(Primitive::Float(f)) => {
serde_json::Value::Number(serde_json::Number::from_f64(f.into_inner()).unwrap())
}
Value::Primitive(Primitive::Int(i)) => {
serde_json::Value::Number(serde_json::Number::from(*i))
}
Value::Primitive(Primitive::Nothing) => serde_json::Value::Null,
Value::Primitive(Primitive::String(s)) => serde_json::Value::String(s.clone()),
Value::Filesystem => serde_json::Value::Null,
Value::List(l) => {
serde_json::Value::Array(l.iter().map(|x| value_to_json_value(x)).collect())
}
Value::Error(e) => serde_json::Value::String(e.to_string()),
Value::Block(_) => serde_json::Value::Null,
Value::Object(o) => {
let mut m = serde_json::Map::new();
for (k, v) in o.entries.iter() {
m.insert(k.name.display().to_string(), value_to_json_value(v));
}
serde_json::Value::Object(m)
}
}
}
pub fn to_json(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn to_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.name_span;
Ok(out Ok(out
.map(move |a| match serde_json::to_string(&a) { .map(
move |a| match serde_json::to_string(&value_to_json_value(&a)) {
Ok(x) => ReturnValue::Value(Value::Primitive(Primitive::String(x))), Ok(x) => ReturnValue::Value(Value::Primitive(Primitive::String(x))),
Err(_) => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( Err(_) => {
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
"Can not convert to JSON string", "Can not convert to JSON string",
"can not convert piped data to JSON string", "can not convert piped data to JSON string",
span, span,
)))), ))))
}) }
},
)
.boxed()) .boxed())
} }

View File

@ -1,11 +1,38 @@
use crate::object::{Primitive, Value}; use crate::object::{Primitive, Value};
use crate::prelude::*; use crate::prelude::*;
pub fn value_to_toml_value(v: &Value) -> toml::Value {
match v {
Value::Primitive(Primitive::Boolean(b)) => toml::Value::Boolean(*b),
Value::Primitive(Primitive::Bytes(b)) => toml::Value::Integer(*b as i64),
Value::Primitive(Primitive::Date(d)) => toml::Value::String(d.to_string()),
Value::Primitive(Primitive::EndOfStream) => {
toml::Value::String("<End of Stream>".to_string())
}
Value::Primitive(Primitive::Float(f)) => toml::Value::Float(f.into_inner()),
Value::Primitive(Primitive::Int(i)) => toml::Value::Integer(*i),
Value::Primitive(Primitive::Nothing) => toml::Value::String("<Nothing>".to_string()),
Value::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()),
Value::Filesystem => toml::Value::String("<Filesystem>".to_string()),
Value::List(l) => toml::Value::Array(l.iter().map(|x| value_to_toml_value(x)).collect()),
Value::Error(e) => toml::Value::String(e.to_string()),
Value::Block(_) => toml::Value::String("<Block>".to_string()),
Value::Object(o) => {
let mut m = toml::map::Map::new();
for (k, v) in o.entries.iter() {
m.insert(k.name.display().to_string(), value_to_toml_value(v));
}
toml::Value::Table(m)
}
}
}
pub fn to_toml(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn to_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.name_span;
Ok(out Ok(out
.map(move |a| match toml::to_string(&a) { .map(move |a| match toml::to_string(&value_to_toml_value(&a)) {
Ok(x) => ReturnValue::Value(Value::Primitive(Primitive::String(x))), Ok(x) => ReturnValue::Value(Value::Primitive(Primitive::String(x))),
Err(_) => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( Err(_) => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
"Can not convert to TOML string", "Can not convert to TOML string",

View File

@ -4,7 +4,6 @@ crate mod desc;
crate mod dict; crate mod dict;
crate mod files; crate mod files;
crate mod process; crate mod process;
crate mod serialization;
crate mod types; crate mod types;
crate use base::{Primitive, Value}; crate use base::{Primitive, Value};

View File

@ -13,7 +13,7 @@ use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer}
use std::fmt; use std::fmt;
use std::time::SystemTime; use std::time::SystemTime;
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, new)] #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, new, Serialize, Deserialize)]
pub struct OF64 { pub struct OF64 {
crate inner: OrderedFloat<f64>, crate inner: OrderedFloat<f64>,
} }
@ -30,13 +30,13 @@ impl From<f64> for OF64 {
} }
} }
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize)] #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize, Serialize)]
pub enum Primitive { pub enum Primitive {
Nothing, Nothing,
Int(i64), Int(i64),
#[allow(unused)] #[allow(unused)]
Float(OF64), Float(OF64),
Bytes(u128), Bytes(u64),
String(String), String(String),
Boolean(bool), Boolean(bool),
Date(DateTime<Utc>), Date(DateTime<Utc>),
@ -44,24 +44,6 @@ pub enum Primitive {
EndOfStream, EndOfStream,
} }
impl Serialize for Primitive {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Primitive::Nothing => serializer.serialize_i32(0),
Primitive::EndOfStream => serializer.serialize_i32(0),
Primitive::Int(i) => serializer.serialize_i64(*i),
Primitive::Float(OF64 { inner: f }) => serializer.serialize_f64(f.into_inner()),
Primitive::Bytes(b) => serializer.serialize_u128(*b),
Primitive::String(ref s) => serializer.serialize_str(s),
Primitive::Boolean(b) => serializer.serialize_bool(*b),
Primitive::Date(d) => serializer.serialize_str(&d.to_string()),
}
}
}
impl Primitive { impl Primitive {
crate fn type_name(&self) -> String { crate fn type_name(&self) -> String {
use Primitive::*; use Primitive::*;
@ -99,7 +81,7 @@ impl Primitive {
Primitive::Nothing => format!("{}", Color::Black.bold().paint("-")), Primitive::Nothing => format!("{}", Color::Black.bold().paint("-")),
Primitive::EndOfStream => format!("{}", Color::Black.bold().paint("-")), Primitive::EndOfStream => format!("{}", Color::Black.bold().paint("-")),
Primitive::Bytes(b) => { Primitive::Bytes(b) => {
let byte = byte_unit::Byte::from_bytes(*b); let byte = byte_unit::Byte::from_bytes(*b as u128);
if byte.get_bytes() == 0u128 { if byte.get_bytes() == 0u128 {
return Color::Black.bold().paint("Empty".to_string()).to_string(); return Color::Black.bold().paint("Empty".to_string()).to_string();
@ -194,7 +176,7 @@ impl Block {
} }
} }
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)] #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Serialize, Deserialize)]
pub enum Value { pub enum Value {
Primitive(Primitive), Primitive(Primitive),
Object(crate::object::Dictionary), Object(crate::object::Dictionary),
@ -404,7 +386,7 @@ impl Value {
crate fn as_i64(&self) -> Result<i64, ShellError> { crate fn as_i64(&self) -> Result<i64, ShellError> {
match self { match self {
Value::Primitive(Primitive::Int(i)) => Ok(*i), Value::Primitive(Primitive::Int(i)) => Ok(*i),
Value::Primitive(Primitive::Bytes(b)) if *b <= std::i64::MAX as u128 => Ok(*b as i64), Value::Primitive(Primitive::Bytes(b)) => Ok(*b as i64),
// TODO: this should definitely be more general with better errors // TODO: this should definitely be more general with better errors
other => Err(ShellError::string(format!( other => Err(ShellError::string(format!(
"Expected integer, got {:?}", "Expected integer, got {:?}",
@ -435,7 +417,7 @@ impl Value {
Value::Primitive(Primitive::String(s.into())) Value::Primitive(Primitive::String(s.into()))
} }
pub fn bytes(s: impl Into<u128>) -> Value { pub fn bytes(s: impl Into<u64>) -> Value {
Value::Primitive(Primitive::Bytes(s.into())) Value::Primitive(Primitive::Bytes(s.into()))
} }
@ -529,20 +511,18 @@ crate fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool {
_ => false, _ => false,
}, },
Value::Primitive(Primitive::Bytes(i)) => match (op, rhs) { Value::Primitive(Primitive::Bytes(i)) => match (op, rhs) {
(Operator::LessThan, Value::Primitive(Primitive::Int(i2))) => i < (*i2 as u128), (Operator::LessThan, Value::Primitive(Primitive::Int(i2))) => i < (*i2 as u64),
(Operator::GreaterThan, Value::Primitive(Primitive::Int(i2))) => { (Operator::GreaterThan, Value::Primitive(Primitive::Int(i2))) => {
i > (*i2 as u128) i > (*i2 as u64)
} }
(Operator::LessThanOrEqual, Value::Primitive(Primitive::Int(i2))) => { (Operator::LessThanOrEqual, Value::Primitive(Primitive::Int(i2))) => {
i <= (*i2 as u128) i <= (*i2 as u64)
} }
(Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Int(i2))) => { (Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Int(i2))) => {
i >= (*i2 as u128) i >= (*i2 as u64)
}
(Operator::Equal, Value::Primitive(Primitive::Int(i2))) => i == (*i2 as u128),
(Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => {
i != (*i2 as u128)
} }
(Operator::Equal, Value::Primitive(Primitive::Int(i2))) => i == (*i2 as u64),
(Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => i != (*i2 as u64),
_ => false, _ => false,
}, },
Value::Primitive(Primitive::Int(i)) => match (op, rhs) { Value::Primitive(Primitive::Int(i)) => match (op, rhs) {

View File

@ -29,7 +29,7 @@ crate fn dir_entry_dict(entry: &std::fs::DirEntry) -> Result<Dictionary, ShellEr
Value::boolean(metadata.permissions().readonly()), Value::boolean(metadata.permissions().readonly()),
); );
dict.add("size", Value::bytes(metadata.len() as u128)); dict.add("size", Value::bytes(metadata.len() as u64));
match metadata.created() { match metadata.created() {
Ok(c) => dict.add("created", Value::system_date(c)), Ok(c) => dict.add("created", Value::system_date(c)),

View File

@ -1,112 +0,0 @@
use crate::object::base::OF64;
use crate::prelude::*;
use ordered_float::OrderedFloat;
use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
struct OF64Visitor;
impl Visitor<'_> for OF64Visitor {
type Value = OF64;
fn visit_f64<E>(self, value: f64) -> Result<OF64, E> {
Ok(OF64::new(OrderedFloat(value)))
}
fn visit_f32<E>(self, value: f32) -> Result<OF64, E> {
Ok(OF64::new(OrderedFloat(value as f64)))
}
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a float")
}
}
impl<'de> Deserialize<'de> for OF64 {
fn deserialize<D>(deserializer: D) -> Result<OF64, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_f64(OF64Visitor)
}
}
impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Value::Primitive(p) => p.serialize(serializer),
Value::Object(o) => o.serialize(serializer),
Value::List(l) => l.serialize(serializer),
Value::Block(b) => b.serialize(serializer),
Value::Error(e) => e.serialize(serializer),
Value::Filesystem => "".serialize(serializer),
}
}
}
struct ValueVisitor;
impl<'de> Visitor<'de> for ValueVisitor {
type Value = Value;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a shell value")
}
fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::int(value))
}
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::int(value))
}
fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::int(value))
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
// TODO: Handle overflow better
Ok(Value::int(value as i64))
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::string(value))
}
fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::boolean(value))
}
}
impl<'de> Deserialize<'de> for Value {
fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(ValueVisitor)
}
}

View File

@ -68,28 +68,31 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
Value::Primitive(Primitive::Bytes(b)) => { Value::Primitive(Primitive::Bytes(b)) => {
send_response(vec![ReturnValue::Value(Value::bytes( send_response(vec![ReturnValue::Value(Value::bytes(
b + inc_by as u128, b + inc_by as u64,
))]); ))]);
} }
_ => { x => {
send_response(vec![ReturnValue::Value(Value::Error(Box::new( send_response(vec![ReturnValue::Value(Value::Error(Box::new(
ShellError::string("Unrecognized type in stream"), ShellError::string(format!("Unrecognized type in stream: {:?}", x)),
)))]); )))]);
} }
}, },
Ok(NuCommand::quit) => { Ok(NuCommand::quit) => {
break; break;
} }
Err(_) => { Err(e) => {
send_response(vec![ReturnValue::Value(Value::Error(Box::new( send_response(vec![ReturnValue::Value(Value::Error(Box::new(
ShellError::string("Unrecognized type in stream"), ShellError::string(format!(
"Unrecognized type in stream: {} {:?}",
input, e
)),
)))]); )))]);
} }
} }
} }
Err(_) => { Err(_) => {
send_response(vec![ReturnValue::Value(Value::Error(Box::new( send_response(vec![ReturnValue::Value(Value::Error(Box::new(
ShellError::string("Unrecognized type in stream"), ShellError::string(format!("Unrecognized type in stream: {}", input)),
)))]); )))]);
} }
} }

View File

@ -53,7 +53,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
Value::Primitive(Primitive::Bytes(b)) => { Value::Primitive(Primitive::Bytes(b)) => {
total += b as i64; total += b as i64;
send_response(vec![ReturnValue::Value(Value::bytes(total as u128))]); send_response(vec![ReturnValue::Value(Value::bytes(total as u64))]);
} }
_ => { _ => {
send_response(vec![ReturnValue::Value(Value::Error(Box::new( send_response(vec![ReturnValue::Value(Value::Error(Box::new(