diff --git a/Cargo.lock b/Cargo.lock index 34188909ac..b711d56451 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1615,8 +1615,8 @@ dependencies = [ "neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", "pretty-hex 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1627,6 +1627,7 @@ dependencies = [ "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "roxmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rust_decimal 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 5.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1649,6 +1650,37 @@ dependencies = [ "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-bigint" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-complex" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-derive" version = "0.2.5" @@ -1684,6 +1716,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1790,7 +1823,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2304,6 +2336,16 @@ name = "rust-ini" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rust_decimal" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustc-demangle" version = "0.1.15" @@ -3320,6 +3362,9 @@ dependencies = [ "checksum nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9761d859320e381010a4f7f8ed425f2c924de33ad121ace447367c713ad561b" "checksum nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d4312467f8b28d909344b934207e502212fa5a3adf1bff7428b0b86a666223d" "checksum ntapi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f26e041cd983acbc087e30fcba770380cfa352d0e392e175b2344ebaf7ea0602" +"checksum num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db" +"checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718" +"checksum num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fcb0cf31fb3ff77e6d2a6ebd6800df7fdcd106f2ad89113c9130bcd07f93dffc" "checksum num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" "checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e" @@ -3394,6 +3439,7 @@ dependencies = [ "checksum rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051" "checksum rust-argon2 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81ed8d04228b44a740c8d46ff872a28e50fff3d659f307ab4da2cc502e019ff3" "checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" +"checksum rust_decimal 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f7a28ded8f10361cefb69a8d8e1d195acf59344150534c165c401d6611cf013d" "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustyline 5.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8ee0838a6594169a1c5f4bb9af0fe692cc99691941710a8cc6576395ede804e" diff --git a/Cargo.toml b/Cargo.toml index bd50fcbb38..cff36bffc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,14 +26,15 @@ indexmap = { version = "1.0.2", features = ["serde-1"] } chrono-humanize = "0.0.11" byte-unit = "3.0.1" base64 = "0.10.1" -ordered-float = {version = "1.0.2", features = ["serde"]} futures-preview = { version = "=0.3.0-alpha.18", features = ["compat", "io-compat"] } futures-async-stream = "=0.1.0-alpha.5" futures_codec = "0.2.5" +num-traits = "0.2.8" term = "0.5.2" bytes = "0.4.12" log = "0.4.8" pretty_env_logger = "0.3.1" +rust_decimal = "1.0.3" serde = { version = "1.0.98", features = ["derive"] } bson = "=0.13.0" serde_json = "1.0.40" diff --git a/rust-toolchain b/rust-toolchain index 9647abec01..e6ae9d2242 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2019-08-22 +nightly-2019-08-30 diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index ff1fc267c1..9edca220e3 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -1,5 +1,4 @@ use crate::commands::WholeStreamCommand; -use crate::object::base::OF64; use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; use bson::{decode_document, spec::BinarySubtype, Bson}; @@ -28,7 +27,7 @@ fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into) -> Tagged Value::Primitive(Primitive::Float(OF64::from(*n))).tagged(tag), + Bson::FloatingPoint(n) => Value::Primitive(Primitive::from(*n)).tagged(tag), Bson::String(s) => Value::Primitive(Primitive::String(String::from(s))).tagged(tag), Bson::Array(a) => Value::List( a.iter() diff --git a/src/commands/from_json.rs b/src/commands/from_json.rs index 52ec333f36..b4b1f7980e 100644 --- a/src/commands/from_json.rs +++ b/src/commands/from_json.rs @@ -1,5 +1,4 @@ use crate::commands::WholeStreamCommand; -use crate::object::base::OF64; use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; @@ -34,9 +33,7 @@ fn convert_json_value_to_nu_value(v: &serde_hjson::Value, tag: impl Into) - match v { serde_hjson::Value::Null => Value::Primitive(Primitive::Nothing).tagged(tag), serde_hjson::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(tag), - serde_hjson::Value::F64(n) => { - Value::Primitive(Primitive::Float(OF64::from(*n))).tagged(tag) - } + serde_hjson::Value::F64(n) => Value::Primitive(Primitive::from(*n)).tagged(tag), serde_hjson::Value::U64(n) => Value::Primitive(Primitive::Int(*n as i64)).tagged(tag), serde_hjson::Value::I64(n) => Value::Primitive(Primitive::Int(*n as i64)).tagged(tag), serde_hjson::Value::String(s) => { diff --git a/src/commands/from_sqlite.rs b/src/commands/from_sqlite.rs index d92b50f807..bc288ff396 100644 --- a/src/commands/from_sqlite.rs +++ b/src/commands/from_sqlite.rs @@ -1,6 +1,5 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::base::OF64; use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; use rusqlite::{types::ValueRef, Connection, Row, NO_PARAMS}; @@ -94,7 +93,7 @@ fn convert_sqlite_value_to_nu_value(value: ValueRef, tag: impl Into + Clone match value { ValueRef::Null => Value::Primitive(Primitive::String(String::from(""))).tagged(tag), ValueRef::Integer(i) => Value::Primitive(Primitive::Int(i)).tagged(tag), - ValueRef::Real(f) => Value::Primitive(Primitive::Float(OF64::from(f))).tagged(tag), + ValueRef::Real(f) => Value::number(f).tagged(tag), t @ ValueRef::Text(_) => { // this unwrap is safe because we know the ValueRef is Text. Value::Primitive(Primitive::String(t.as_str().unwrap().to_string())).tagged(tag) diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index be9a8cc6c9..6df13d6860 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -1,5 +1,4 @@ use crate::commands::WholeStreamCommand; -use crate::object::base::OF64; use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; @@ -29,7 +28,7 @@ pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into) -> T match v { toml::Value::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(tag), toml::Value::Integer(n) => Value::Primitive(Primitive::Int(*n)).tagged(tag), - toml::Value::Float(n) => Value::Primitive(Primitive::Float(OF64::from(*n))).tagged(tag), + toml::Value::Float(n) => Value::Primitive(Primitive::from(*n)).tagged(tag), toml::Value::String(s) => Value::Primitive(Primitive::String(String::from(s))).tagged(tag), toml::Value::Array(a) => Value::List( a.iter() diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index 2294a39ee3..a5f229a033 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -1,5 +1,4 @@ use crate::commands::WholeStreamCommand; -use crate::object::base::OF64; use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; @@ -52,7 +51,7 @@ fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, tag: impl Into) -> Value::Primitive(Primitive::Int(n.as_i64().unwrap())).tagged(tag) } serde_yaml::Value::Number(n) if n.is_f64() => { - Value::Primitive(Primitive::Float(OF64::from(n.as_f64().unwrap()))).tagged(tag) + Value::Primitive(Primitive::from(n.as_f64().unwrap())).tagged(tag) } serde_yaml::Value::String(s) => Value::string(s).tagged(tag), serde_yaml::Value::Sequence(a) => Value::List( diff --git a/src/commands/ps.rs b/src/commands/ps.rs index 370b7b73d5..15a19a8578 100644 --- a/src/commands/ps.rs +++ b/src/commands/ps.rs @@ -62,7 +62,7 @@ fn ps(args: CommandArgs, registry: &CommandRegistry) -> Result() as f64)); + dict.insert("cpu", Value::number(usage.get::())); yield ReturnSuccess::value(dict.into_tagged_value()); } } diff --git a/src/commands/save.rs b/src/commands/save.rs index 11b245ef9c..eee4774d2f 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -1,5 +1,4 @@ -use crate::commands::UnevaluatedCallInfo; -use crate::commands::WholeStreamCommand; +use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand}; use crate::errors::ShellError; use crate::object::Value; use crate::prelude::*; diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs index 60dc1cf2c8..025484d8d3 100644 --- a/src/commands/to_bson.rs +++ b/src/commands/to_bson.rs @@ -27,11 +27,16 @@ impl WholeStreamCommand for ToBSON { pub fn value_to_bson_value(v: &Value) -> Bson { match v { Value::Primitive(Primitive::Boolean(b)) => Bson::Boolean(*b), - Value::Primitive(Primitive::Bytes(b)) => Bson::I64(*b as i64), + // FIXME: What about really big decimals? + Value::Primitive(Primitive::Bytes(decimal)) => Bson::FloatingPoint( + (*decimal) + .to_f64() + .expect("Unimplemented BUG: What about big decimals?"), + ), Value::Primitive(Primitive::Date(d)) => Bson::UtcDatetime(*d), Value::Primitive(Primitive::EndOfStream) => Bson::Null, Value::Primitive(Primitive::BeginningOfStream) => Bson::Null, - Value::Primitive(Primitive::Float(f)) => Bson::FloatingPoint(f.into_inner()), + Value::Primitive(Primitive::Decimal(d)) => Bson::FloatingPoint(d.to_f64().unwrap()), Value::Primitive(Primitive::Int(i)) => Bson::I64(*i), Value::Primitive(Primitive::Nothing) => Bson::Null, Value::Primitive(Primitive::String(s)) => Bson::String(s.clone()), diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 7bfe6dd113..65651396ab 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -45,7 +45,7 @@ pub fn value_to_csv_value(v: &Value) -> Value { fn to_string_helper(v: &Value) -> Result> { match v { Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), - Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", *b as u64)), + Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", b)), Value::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?), Value::List(_) => return Ok(String::from("[list list]")), Value::Object(_) => return Ok(String::from("[object]")), diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index d61ce909bc..3559459cdc 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -25,15 +25,18 @@ impl WholeStreamCommand for ToJSON { 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::Bytes(b)) => serde_json::Value::Number( + serde_json::Number::from(b.to_u64().expect("What about really big numbers")), + ), Value::Primitive(Primitive::Date(d)) => serde_json::Value::String(d.to_string()), Value::Primitive(Primitive::EndOfStream) => serde_json::Value::Null, Value::Primitive(Primitive::BeginningOfStream) => 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::Decimal(f)) => serde_json::Value::Number( + serde_json::Number::from_f64( + f.to_f64().expect("TODO: What about really big decimals?"), + ) + .unwrap(), + ), Value::Primitive(Primitive::Int(i)) => { serde_json::Value::Number(serde_json::Number::from(*i)) } diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index 05a2e9c13f..fcbf96e3b3 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -73,7 +73,7 @@ fn nu_value_to_sqlite_string(v: Value) -> String { Value::Primitive(p) => match p { Primitive::Nothing => "NULL".into(), Primitive::Int(i) => format!("{}", i), - Primitive::Float(f) => format!("{}", f.into_inner()), + Primitive::Decimal(f) => format!("{}", f), Primitive::Bytes(u) => format!("{}", u), Primitive::String(s) => format!("'{}'", s.replace("'", "''")), Primitive::Boolean(true) => "1".into(), diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 074a74a51d..5d60807390 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -1,4 +1,5 @@ use crate::commands::WholeStreamCommand; +use crate::errors::ranged; use crate::object::{Primitive, Value}; use crate::prelude::*; @@ -22,10 +23,12 @@ impl WholeStreamCommand for ToTOML { } } -pub fn value_to_toml_value(v: &Value) -> toml::Value { - match v { +pub fn value_to_toml_value(v: &Value) -> Result { + Ok(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::Bytes(b)) => { + toml::Value::Integer(ranged(b.to_i64(), "i64", b.tagged_unknown())?) + } Value::Primitive(Primitive::Date(d)) => toml::Value::String(d.to_string()), Value::Primitive(Primitive::EndOfStream) => { toml::Value::String("".to_string()) @@ -33,13 +36,15 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value { Value::Primitive(Primitive::BeginningOfStream) => { toml::Value::String("".to_string()) } - Value::Primitive(Primitive::Float(f)) => toml::Value::Float(f.into_inner()), + Value::Primitive(Primitive::Decimal(f)) => { + toml::Value::Float(ranged(f.to_f64(), "f64", f.tagged_unknown())?) + } Value::Primitive(Primitive::Int(i)) => toml::Value::Integer(*i), Value::Primitive(Primitive::Nothing) => toml::Value::String("".to_string()), Value::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()), Value::Primitive(Primitive::Path(s)) => toml::Value::String(s.display().to_string()), - Value::List(l) => toml::Value::Array(l.iter().map(|x| value_to_toml_value(x)).collect()), + Value::List(l) => toml::Value::Array(collect_values(l)?), Value::Block(_) => toml::Value::String("".to_string()), Value::Binary(b) => { toml::Value::Array(b.iter().map(|x| toml::Value::Integer(*x as i64)).collect()) @@ -47,11 +52,21 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value { Value::Object(o) => { let mut m = toml::map::Map::new(); for (k, v) in o.entries.iter() { - m.insert(k.clone(), value_to_toml_value(v)); + m.insert(k.clone(), value_to_toml_value(v)?); } toml::Value::Table(m) } + }) +} + +fn collect_values(input: &Vec>) -> Result, ShellError> { + let mut out = vec![]; + + for value in input { + out.push(value_to_toml_value(value)?); } + + Ok(out) } fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { @@ -61,7 +76,7 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { return ReturnSuccess::value( Value::Primitive(Primitive::String(val)).simple_spanned(name_span), diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index 5a8cb1de41..b999ce12d6 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -45,7 +45,7 @@ pub fn value_to_tsv_value(v: &Value) -> Value { fn to_string_helper(v: &Value) -> Result> { match v { Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), - Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", *b as u64)), + Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", b)), Value::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?), Value::List(_) => return Ok(String::from("[list list]")), Value::Object(_) => return Ok(String::from("[object]")), diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index e490954f24..f0aa0219e0 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -26,13 +26,13 @@ pub fn value_to_yaml_value(v: &Value) -> serde_yaml::Value { match v { Value::Primitive(Primitive::Boolean(b)) => serde_yaml::Value::Bool(*b), Value::Primitive(Primitive::Bytes(b)) => { - serde_yaml::Value::Number(serde_yaml::Number::from(*b as u64)) + serde_yaml::Value::Number(serde_yaml::Number::from(b.to_f64().unwrap())) } Value::Primitive(Primitive::Date(d)) => serde_yaml::Value::String(d.to_string()), Value::Primitive(Primitive::EndOfStream) => serde_yaml::Value::Null, Value::Primitive(Primitive::BeginningOfStream) => serde_yaml::Value::Null, - Value::Primitive(Primitive::Float(f)) => { - serde_yaml::Value::Number(serde_yaml::Number::from(f.into_inner())) + Value::Primitive(Primitive::Decimal(f)) => { + serde_yaml::Value::Number(serde_yaml::Number::from(f.to_f64().unwrap())) } Value::Primitive(Primitive::Int(i)) => { serde_yaml::Value::Number(serde_yaml::Number::from(*i)) diff --git a/src/commands/where_.rs b/src/commands/where_.rs index f1d9b20023..5f037ded43 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -27,7 +27,7 @@ impl PerItemCommand for Where { let stream = match condition { Tagged { item: Value::Block(block), - tag, + .. } => { let result = block.invoke(&input_clone); match result { @@ -39,11 +39,7 @@ impl PerItemCommand for Where { } } Err(e) => { - return Err(ShellError::labeled_error( - format!("Could not evaluate ({})", e.to_string()), - "could not evaluate", - tag.span, - )) + return Err(e) } } } diff --git a/src/errors.rs b/src/errors.rs index 0e2d1ff654..3bf0538be5 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -84,6 +84,17 @@ impl ShellError { .start() } + pub(crate) fn range_error( + expected: impl Into, + actual: Tagged, + ) -> ShellError { + ProximateShellError::RangeError { + kind: expected.into(), + actual_kind: actual.map(|a| format!("{:?}", a)), + } + .start() + } + pub(crate) fn syntax_error(problem: Tagged>) -> ShellError { ProximateShellError::SyntaxError { problem: problem.map(|p| p.into()), @@ -242,6 +253,20 @@ impl ShellError { } => Diagnostic::new(Severity::Error, "Type Error") .with_label(Label::new_primary(span).with_message(expected)), + ProximateShellError::RangeError { + kind, + actual_kind: + Tagged { + item, + tag: Tag { span, .. }, + }, + } => Diagnostic::new(Severity::Error, "Range Error").with_label( + Label::new_primary(span).with_message(format!( + "Expected to covert {} to {}, but it was out of range", + item, kind + )), + ), + ProximateShellError::SyntaxError { problem: Tagged { @@ -344,6 +369,10 @@ pub enum ProximateShellError { error: ArgumentError, span: Span, }, + RangeError { + kind: String, + actual_kind: Tagged, + }, Diagnostic(ShellDiagnostic), CoerceError { left: Tagged, @@ -423,6 +452,7 @@ impl std::fmt::Display for ShellError { ProximateShellError::MissingValue { .. } => write!(f, "MissingValue"), ProximateShellError::InvalidCommand { .. } => write!(f, "InvalidCommand"), ProximateShellError::TypeError { .. } => write!(f, "TypeError"), + ProximateShellError::RangeError { .. } => write!(f, "RangeError"), ProximateShellError::SyntaxError { .. } => write!(f, "SyntaxError"), ProximateShellError::MissingProperty { .. } => write!(f, "MissingProperty"), ProximateShellError::ArgumentError { .. } => write!(f, "ArgumentError"), @@ -526,3 +556,14 @@ impl ShellErrorUtils> for Option> { } } } + +pub fn ranged( + input: Option, + expected: impl Into, + actual: Tagged, +) -> Result { + match input { + Some(v) => Ok(v), + None => Err(ShellError::range_error(expected, actual)), + } +} diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index d03c39bc77..511625b368 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -106,7 +106,7 @@ pub(crate) fn evaluate_baseline_expr( fn evaluate_literal(literal: Tagged, source: &Text) -> Tagged { let result = match literal.item { - hir::Literal::Integer(int) => Value::int(int), + hir::Literal::Number(int) => int.into(), hir::Literal::Size(int, unit) => unit.compute(int), hir::Literal::String(span) => Value::string(span.slice(source)), hir::Literal::Bare => Value::string(literal.span().slice(source)), diff --git a/src/lib.rs b/src/lib.rs index b258e75aed..e1a068cde8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,12 +25,12 @@ mod utils; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; pub use crate::context::{SourceMap, SpanSource}; pub use crate::env::host::BasicHost; -pub use crate::object::base::OF64; pub use crate::parser::hir::SyntaxType; pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; pub use cli::cli; pub use errors::ShellError; +pub use num_traits::cast::ToPrimitive; pub use object::base::{Primitive, Value}; pub use object::dict::{Dictionary, TaggedDictBuilder}; pub use object::meta::{Span, Tag, Tagged, TaggedItem}; diff --git a/src/object/base.rs b/src/object/base.rs index 08379240ba..fc5a5efaad 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -8,35 +8,16 @@ use crate::Text; use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; use derive_new::new; -use ordered_float::OrderedFloat; use serde::{Deserialize, Serialize}; use std::fmt; use std::path::PathBuf; use std::time::SystemTime; -#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, new, Serialize, Deserialize)] -pub struct OF64 { - pub(crate) inner: OrderedFloat, -} - -impl OF64 { - pub(crate) fn into_inner(&self) -> f64 { - self.inner.into_inner() - } -} - -impl From for OF64 { - fn from(float: f64) -> Self { - OF64::new(OrderedFloat(float)) - } -} - #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize, Serialize)] pub enum Primitive { Nothing, Int(i64), - #[allow(unused)] - Float(OF64), + Decimal(Decimal), Bytes(u64), String(String), Boolean(bool), @@ -48,6 +29,24 @@ pub enum Primitive { EndOfStream, } +impl From for Primitive { + fn from(int: i64) -> Primitive { + Primitive::Int(int) + } +} + +impl From for Primitive { + fn from(decimal: Decimal) -> Primitive { + Primitive::Decimal(decimal) + } +} + +impl From for Primitive { + fn from(float: f64) -> Primitive { + Primitive::Decimal(Decimal::from_f64(float).unwrap()) + } +} + impl Primitive { pub(crate) fn type_name(&self) -> String { use Primitive::*; @@ -58,7 +57,7 @@ impl Primitive { EndOfStream => "end-of-stream", Path(_) => "path", Int(_) => "int", - Float(_) => "float", + Decimal(_) => "decimal", Bytes(_) => "bytes", String(_) => "string", Boolean(_) => "boolean", @@ -76,7 +75,7 @@ impl Primitive { EndOfStream => write!(f, "EndOfStream"), Int(int) => write!(f, "{}", int), Path(path) => write!(f, "{}", path.display()), - Float(float) => write!(f, "{:?}", float), + Decimal(decimal) => write!(f, "{}", decimal), Bytes(bytes) => write!(f, "{}", bytes), String(string) => write!(f, "{:?}", string), Boolean(boolean) => write!(f, "{}", boolean), @@ -84,6 +83,15 @@ impl Primitive { } } + pub fn number(number: impl Into) -> Primitive { + let number = number.into(); + + match number { + Number::Int(int) => Primitive::Int(int), + Number::Decimal(decimal) => Primitive::Decimal(decimal), + } + } + pub fn format(&self, field_name: Option<&String>) -> String { match self { Primitive::Nothing => String::new(), @@ -105,7 +113,7 @@ impl Primitive { } } Primitive::Int(i) => format!("{}", i), - Primitive::Float(OF64 { inner: f }) => format!("{:.*}", 2, f.into_inner()), + Primitive::Decimal(decimal) => format!("{}", decimal), Primitive::String(s) => format!("{}", s), Primitive::Boolean(b) => match (b, field_name) { (true, None) => format!("Yes"), @@ -122,7 +130,7 @@ impl Primitive { pub fn style(&self) -> &'static str { match self { Primitive::Bytes(0) => "c", // centre 'missing' indicator - Primitive::Int(_) | Primitive::Bytes(_) | Primitive::Float(_) => "r", + Primitive::Int(_) | Primitive::Bytes(_) | Primitive::Decimal(_) => "r", _ => "", } } @@ -176,6 +184,15 @@ pub enum Value { Block(Block), } +impl Into for Number { + fn into(self) -> Value { + match self { + Number::Int(int) => Value::int(int), + Number::Decimal(decimal) => Value::decimal(decimal), + } + } +} + pub fn debug_list(values: &Vec>) -> ValuesDebug<'_> { ValuesDebug { values } } @@ -518,7 +535,11 @@ impl Value { } #[allow(unused)] - pub(crate) fn compare(&self, operator: &Operator, other: &Value) -> Result { + pub(crate) fn compare( + &self, + operator: &Operator, + other: &Value, + ) -> Result { match operator { _ => { let coerced = coerce_compare(self, other)?; @@ -566,7 +587,7 @@ impl Value { match self { Value::Primitive(Primitive::String(s)) => Ok(s.clone()), Value::Primitive(Primitive::Boolean(x)) => Ok(format!("{}", x)), - Value::Primitive(Primitive::Float(x)) => Ok(format!("{}", x.into_inner())), + Value::Primitive(Primitive::Decimal(x)) => Ok(format!("{}", x)), Value::Primitive(Primitive::Int(x)) => Ok(format!("{}", x)), Value::Primitive(Primitive::Bytes(x)) => Ok(format!("{}", x)), // TODO: this should definitely be more general with better errors @@ -612,8 +633,17 @@ impl Value { Value::Primitive(Primitive::Int(s.into())) } - pub fn float(s: impl Into) -> Value { - Value::Primitive(Primitive::Float(s.into())) + pub fn decimal(s: impl Into) -> Value { + Value::Primitive(Primitive::Decimal(s.into())) + } + + pub fn number(s: impl Into) -> Value { + let num = s.into(); + + match num { + Number::Int(int) => Value::int(int), + Number::Decimal(decimal) => Value::decimal(decimal), + } } pub fn boolean(s: impl Into) -> Value { @@ -722,32 +752,34 @@ pub(crate) fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool (Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => i != *i2, _ => false, }, - Value::Primitive(Primitive::Float(i)) => match (op, rhs) { - (Operator::LessThan, Value::Primitive(Primitive::Float(i2))) => i < *i2, - (Operator::GreaterThan, Value::Primitive(Primitive::Float(i2))) => i > *i2, - (Operator::LessThanOrEqual, Value::Primitive(Primitive::Float(i2))) => i <= *i2, - (Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Float(i2))) => { + Value::Primitive(Primitive::Decimal(i)) => match (op, rhs) { + (Operator::LessThan, Value::Primitive(Primitive::Decimal(i2))) => i < *i2, + (Operator::GreaterThan, Value::Primitive(Primitive::Decimal(i2))) => i > *i2, + (Operator::LessThanOrEqual, Value::Primitive(Primitive::Decimal(i2))) => { + i <= *i2 + } + (Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Decimal(i2))) => { i >= *i2 } - (Operator::Equal, Value::Primitive(Primitive::Float(i2))) => i == *i2, - (Operator::NotEqual, Value::Primitive(Primitive::Float(i2))) => i != *i2, + (Operator::Equal, Value::Primitive(Primitive::Decimal(i2))) => i == *i2, + (Operator::NotEqual, Value::Primitive(Primitive::Decimal(i2))) => i != *i2, (Operator::LessThan, Value::Primitive(Primitive::Int(i2))) => { - (i.into_inner()) < *i2 as f64 + i < Decimal::from(*i2) } (Operator::GreaterThan, Value::Primitive(Primitive::Int(i2))) => { - i.into_inner() > *i2 as f64 + i > Decimal::from(*i2) } (Operator::LessThanOrEqual, Value::Primitive(Primitive::Int(i2))) => { - i.into_inner() <= *i2 as f64 + i <= Decimal::from(*i2) } (Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Int(i2))) => { - i.into_inner() >= *i2 as f64 + i >= Decimal::from(*i2) } (Operator::Equal, Value::Primitive(Primitive::Int(i2))) => { - i.into_inner() == *i2 as f64 + i == Decimal::from(*i2) } (Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => { - i.into_inner() != *i2 as f64 + i != Decimal::from(*i2) } _ => false, @@ -765,8 +797,8 @@ pub(crate) fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool enum CompareValues { Ints(i64, i64), - Floats(OF64, OF64), - Bytes(i128, i128), + Decimals(Decimal, Decimal), + Bytes(u64, u64), String(String, String), } @@ -774,7 +806,7 @@ impl CompareValues { fn compare(&self) -> std::cmp::Ordering { match self { CompareValues::Ints(left, right) => left.cmp(right), - CompareValues::Floats(left, right) => left.cmp(right), + CompareValues::Decimals(left, right) => left.cmp(right), CompareValues::Bytes(left, right) => left.cmp(right), CompareValues::String(left, right) => left.cmp(right), } @@ -797,10 +829,17 @@ fn coerce_compare_primitive( Ok(match (left, right) { (Int(left), Int(right)) => CompareValues::Ints(*left, *right), - (Float(left), Int(right)) => CompareValues::Floats(*left, (*right as f64).into()), - (Int(left), Float(right)) => CompareValues::Floats((*left as f64).into(), *right), - (Int(left), Bytes(right)) => CompareValues::Bytes(*left as i128, *right as i128), - (Bytes(left), Int(right)) => CompareValues::Bytes(*left as i128, *right as i128), + (Int(left), Decimal(right)) => CompareValues::Decimals((*left).into(), *right), + (Int(left), Bytes(right)) => CompareValues::Bytes(*left as u64, *right), + (Decimal(left), Decimal(right)) => CompareValues::Decimals(*left, *right), + (Decimal(left), Int(right)) => CompareValues::Decimals(*left, (*right).into()), + (Decimal(left), Bytes(right)) => { + CompareValues::Decimals(*left, rust_decimal::Decimal::from(*right)) + } + (Bytes(left), Int(right)) => CompareValues::Bytes(*left, *right as u64), + (Bytes(left), Decimal(right)) => { + CompareValues::Decimals(rust_decimal::Decimal::from(*left), *right) + } (String(left), String(right)) => CompareValues::String(left.clone(), right.clone()), _ => return Err((left.type_name(), right.type_name())), }) diff --git a/src/object/config.rs b/src/object/config.rs index d4793d24d8..fae3155aec 100644 --- a/src/object/config.rs +++ b/src/object/config.rs @@ -36,7 +36,7 @@ pub(crate) fn write_config(config: &IndexMap>) -> Result<( let filename = location.join("config.toml"); touch(&filename)?; - let contents = value_to_toml_value(&Value::Object(Dictionary::new(config.clone()))); + let contents = value_to_toml_value(&Value::Object(Dictionary::new(config.clone())))?; let contents = toml::to_string(&contents)?; diff --git a/src/object/process.rs b/src/object/process.rs new file mode 100644 index 0000000000..337f731b58 --- /dev/null +++ b/src/object/process.rs @@ -0,0 +1,29 @@ +use crate::object::{TaggedDictBuilder, Value}; +use crate::prelude::*; +use itertools::join; +use sysinfo::ProcessExt; + +pub(crate) fn process_dict(proc: &sysinfo::Process, tag: impl Into) -> Tagged { + let mut dict = TaggedDictBuilder::new(tag); + + let cmd = proc.cmd(); + + let cmd_value = if cmd.len() == 0 { + Value::nothing() + } else { + Value::string(join(cmd, "")) + }; + + dict.insert("pid", Value::int(proc.pid() as i64)); + dict.insert("status", Value::string(proc.status().to_string())); + dict.insert("cpu", Value::number(proc.cpu_usage())); + + match cmd_value { + Value::Primitive(Primitive::Nothing) => { + dict.insert("name", Value::string(proc.name())); + } + _ => dict.insert("name", cmd_value), + } + + dict.into_tagged_value() +} diff --git a/src/parser/hir.rs b/src/parser/hir.rs index 5421d39cd8..3f0ab2ce47 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -129,11 +129,15 @@ impl RawExpression { pub type Expression = Tagged; impl Expression { - pub(crate) fn int(i: impl Into, span: impl Into) -> Expression { - Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Integer(i.into())), span) + pub(crate) fn number(i: impl Into, span: impl Into) -> Expression { + Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Number(i.into())), span) } - pub(crate) fn size(i: impl Into, unit: impl Into, span: impl Into) -> Expression { + pub(crate) fn size( + i: impl Into, + unit: impl Into, + span: impl Into, + ) -> Expression { Tagged::from_simple_spanned_item( RawExpression::Literal(Literal::Size(i.into(), unit.into())), span, @@ -224,8 +228,8 @@ impl From> for Expression { #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum Literal { - Integer(i64), - Size(i64, Unit), + Number(Number), + Size(Number, Unit), String(Span), Bare, } @@ -233,8 +237,8 @@ pub enum Literal { impl ToDebug for Tagged<&Literal> { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { match self.item() { - Literal::Integer(int) => write!(f, "{}", *int), - Literal::Size(int, unit) => write!(f, "{}{:?}", *int, unit), + Literal::Number(number) => write!(f, "{:?}", *number), + Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit), Literal::String(span) => write!(f, "{}", span.slice(source)), Literal::Bare => write!(f, "{}", self.span().slice(source)), } @@ -244,7 +248,7 @@ impl ToDebug for Tagged<&Literal> { impl Literal { fn type_name(&self) -> &'static str { match self { - Literal::Integer(_) => "integer", + Literal::Number(..) => "number", Literal::Size(..) => "size", Literal::String(..) => "string", Literal::Bare => "string", diff --git a/src/parser/hir/baseline_parse.rs b/src/parser/hir/baseline_parse.rs index a894f2bd7c..5b4e3878ea 100644 --- a/src/parser/hir/baseline_parse.rs +++ b/src/parser/hir/baseline_parse.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Expression { match *token.item() { - RawToken::Integer(int) => hir::Expression::int(int, token.span()), + RawToken::Number(number) => hir::Expression::number(number, token.span()), RawToken::Size(int, unit) => hir::Expression::size(int, unit, token.span()), RawToken::String(span) => hir::Expression::string(span, token.span()), RawToken::Variable(span) if span.slice(source) == "it" => { @@ -24,8 +24,8 @@ pub fn baseline_parse_token_as_number(token: &Token, source: &Text) -> hir::Expr } RawToken::External(span) => hir::Expression::external_command(span, token.span()), RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::Integer(int) => hir::Expression::int(int, token.span()), - RawToken::Size(int, unit) => hir::Expression::size(int, unit, token.span()), + RawToken::Number(number) => hir::Expression::number(number, token.span()), + RawToken::Size(number, unit) => hir::Expression::size(number, unit, token.span()), RawToken::Bare => hir::Expression::bare(token.span()), RawToken::String(span) => hir::Expression::string(span, token.span()), } @@ -38,7 +38,7 @@ pub fn baseline_parse_token_as_string(token: &Token, source: &Text) -> hir::Expr } RawToken::External(span) => hir::Expression::external_command(span, token.span()), RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::Integer(_) => hir::Expression::bare(token.span()), + RawToken::Number(_) => hir::Expression::bare(token.span()), RawToken::Size(_, _) => hir::Expression::bare(token.span()), RawToken::Bare => hir::Expression::bare(token.span()), RawToken::String(span) => hir::Expression::string(span, token.span()), @@ -56,7 +56,7 @@ pub fn baseline_parse_token_as_path( } RawToken::External(span) => hir::Expression::external_command(span, token.span()), RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::Integer(_) => hir::Expression::bare(token.span()), + RawToken::Number(_) => hir::Expression::bare(token.span()), RawToken::Size(_, _) => hir::Expression::bare(token.span()), RawToken::Bare => hir::Expression::file_path( expand_path(token.span().slice(source), context), diff --git a/src/parser/hir/baseline_parse_tokens.rs b/src/parser/hir/baseline_parse_tokens.rs index be056b5b56..a1119bc765 100644 --- a/src/parser/hir/baseline_parse_tokens.rs +++ b/src/parser/hir/baseline_parse_tokens.rs @@ -294,7 +294,7 @@ pub fn baseline_parse_path( TokenNode::Token(token) => match token.item() { RawToken::Bare => token.span().slice(source), RawToken::String(span) => span.slice(source), - RawToken::Integer(_) + RawToken::Number(_) | RawToken::Size(..) | RawToken::Variable(_) | RawToken::External(_) => { diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 94ff2976eb..2597ded60c 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -4,6 +4,7 @@ use crate::parser::parse::{ call_node::*, flag::*, operator::*, pipeline::*, token_tree::*, token_tree_builder::*, tokens::*, unit::*, }; +use crate::prelude::*; use crate::{Span, Tagged}; use nom; use nom::branch::*; @@ -18,6 +19,7 @@ use nom::dbg; use nom::*; use nom::{AsBytes, FindSubstring, IResult, InputLength, InputTake, Slice}; use nom5_locate::{position, LocatedSpan}; +use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::str::FromStr; @@ -68,16 +70,94 @@ fn trace_step<'a, T: Debug>( } } -pub fn raw_integer(input: NomSpan) -> IResult> { +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)] +pub enum Number { + Int(i64), + Decimal(Decimal), +} + +impl std::ops::Mul for Number { + type Output = Number; + + fn mul(self, other: Number) -> Number { + match (self, other) { + (Number::Int(a), Number::Int(b)) => Number::Int(a * b), + (Number::Int(a), Number::Decimal(b)) => Number::Decimal(Decimal::from(a) * b), + (Number::Decimal(a), Number::Int(b)) => Number::Decimal(a * Decimal::from(b)), + (Number::Decimal(a), Number::Decimal(b)) => Number::Decimal(a * b), + } + } +} + +// For literals +impl std::ops::Mul for Number { + type Output = Number; + + fn mul(self, other: u32) -> Number { + match self { + Number::Int(left) => Number::Int(left * (other as i64)), + Number::Decimal(left) => Number::Decimal(left * Decimal::from(other)), + } + } +} + +impl Into for f32 { + fn into(self) -> Number { + Number::Decimal(Decimal::from_f32(self).unwrap()) + } +} + +impl Into for f64 { + fn into(self) -> Number { + Number::Decimal(Decimal::from_f64(self).unwrap()) + } +} + +impl Into for i64 { + fn into(self) -> Number { + Number::Int(self) + } +} + +impl Into for Decimal { + fn into(self) -> Number { + Number::Decimal(self) + } +} + +pub fn raw_number(input: NomSpan) -> IResult> { + let original = input; let start = input.offset; - trace_step(input, "raw_integer", move |input| { + trace_step(input, "raw_decimal", move |input| { let (input, neg) = opt(tag("-"))(input)?; - let (input, num) = digit1(input)?; + let (input, head) = digit1(input)?; + let dot: IResult = tag(".")(input); + + let input = match dot { + Ok((input, dot)) => input, + + // it's just an integer + Err(_) => { + return Ok(( + input, + Tagged::from_simple_spanned_item( + Number::Int(int(head.fragment, neg)), + (start, input.offset), + ), + )) + } + }; + + let (input, tail) = digit1(input)?; + let end = input.offset; + let decimal = Decimal::from_str(&format!("{}.{}", head.fragment, tail.fragment)) + .expect("BUG: Should have already ensured that the input is a valid decimal"); + Ok(( input, - Tagged::from_simple_spanned_item(int(num.fragment, neg), (start, end)), + Tagged::from_simple_spanned_item(Number::Decimal(decimal), (start, end)), )) }) } @@ -245,7 +325,7 @@ pub fn size(input: NomSpan) -> IResult { trace_step(input, "size", move |input| { let mut is_size = false; let start = input.offset; - let (input, int) = raw_integer(input)?; + let (input, number) = raw_number(input)?; if let Ok((input, Some(size))) = opt(raw_unit)(input) { let end = input.offset; @@ -256,7 +336,7 @@ pub fn size(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_size((*int, *size), (start, end)), + TokenTreeBuilder::spanned_size((number.item, *size), (start, end)), )) } else { let end = input.offset; @@ -266,7 +346,10 @@ pub fn size(input: NomSpan) -> IResult { return Err(nom::Err::Error((input, nom::error::ErrorKind::Char))); } - Ok((input, TokenTreeBuilder::spanned_int((*int), (start, end)))) + Ok(( + input, + TokenTreeBuilder::spanned_number(number.item, number.tag), + )) } }) } @@ -625,12 +708,12 @@ mod tests { fn test_integer() { assert_leaf! { parsers [ size ] - "123" -> 0..3 { Integer(123) } + "123" -> 0..3 { Number(Number::Int(123)) } } assert_leaf! { parsers [ size ] - "-123" -> 0..4 { Integer(-123) } + "-123" -> 0..4 { Number(Number::Int(-123)) } } } @@ -638,12 +721,12 @@ mod tests { fn test_size() { assert_leaf! { parsers [ size ] - "123MB" -> 0..5 { Size(123, Unit::MB) } + "123MB" -> 0..5 { Size(Number::Int(123), Unit::MB) } } assert_leaf! { parsers [ size ] - "10GB" -> 0..4 { Size(10, Unit::GB) } + "10GB" -> 0..4 { Size(Number::Int(10), Unit::GB) } } } diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index a30b3d74d2..19a7f07a4f 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -3,6 +3,7 @@ use crate::prelude::*; use crate::parser::parse::flag::{Flag, FlagKind}; use crate::parser::parse::operator::Operator; +use crate::parser::parse::parser::Number; use crate::parser::parse::pipeline::{Pipeline, PipelineElement}; use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode}; use crate::parser::parse::tokens::{RawToken, Token}; @@ -170,9 +171,23 @@ impl TokenTreeBuilder { }) } - pub fn spanned_int(input: impl Into, span: impl Into) -> TokenNode { + pub fn spanned_number(input: impl Into, span: impl Into) -> TokenNode { + match input.into() { + Number::Int(int) => TokenTreeBuilder::spanned_int(int, span), + Number::Decimal(decimal) => TokenTreeBuilder::spanned_decimal(decimal, span), + } + } + + fn spanned_int(input: i64, span: impl Into) -> TokenNode { TokenNode::Token(Token::from_simple_spanned_item( - RawToken::Integer(input.into()), + RawToken::Number(Number::Int(input)), + span, + )) + } + + fn spanned_decimal(input: Decimal, span: impl Into) -> TokenNode { + TokenNode::Token(Token::from_simple_spanned_item( + RawToken::Number(Number::Decimal(input)), span, )) } @@ -191,7 +206,7 @@ impl TokenTreeBuilder { } pub fn spanned_size( - input: (impl Into, impl Into), + input: (impl Into, impl Into), span: impl Into, ) -> TokenNode { let (int, unit) = (input.0.into(), input.1.into()); diff --git a/src/parser/parse/tokens.rs b/src/parser/parse/tokens.rs index 717f5845eb..79f08630f8 100644 --- a/src/parser/parse/tokens.rs +++ b/src/parser/parse/tokens.rs @@ -1,11 +1,12 @@ use crate::parser::parse::unit::*; +use crate::prelude::*; use crate::{Span, Tagged, Text}; use std::fmt; #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum RawToken { - Integer(i64), - Size(i64, Unit), + Number(Number), + Size(Number, Unit), String(Span), Variable(Span), External(Span), @@ -15,7 +16,7 @@ pub enum RawToken { impl RawToken { pub fn type_name(&self) -> &'static str { match self { - RawToken::Integer(_) => "Integer", + RawToken::Number(_) => "Number", RawToken::Size(..) => "Size", RawToken::String(_) => "String", RawToken::Variable(_) => "Variable", diff --git a/src/parser/parse/unit.rs b/src/parser/parse/unit.rs index 54465fc5ed..47129ccf2a 100644 --- a/src/parser/parse/unit.rs +++ b/src/parser/parse/unit.rs @@ -1,4 +1,5 @@ use crate::object::base::Value; +use crate::prelude::*; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -24,8 +25,8 @@ impl Unit { } } - pub(crate) fn compute(&self, size: i64) -> Value { - Value::int(match self { + pub(crate) fn compute(&self, size: Number) -> Value { + Value::number(match self { Unit::B => size, Unit::KB => size * 1024, Unit::MB => size * 1024 * 1024, diff --git a/src/plugins/sys.rs b/src/plugins/sys.rs index d92f0b377e..74d87befe0 100644 --- a/src/plugins/sys.rs +++ b/src/plugins/sys.rs @@ -2,12 +2,12 @@ use std::ffi::OsStr; use futures::executor::block_on; use futures::stream::StreamExt; +use heim::units::{frequency, information, thermodynamic_temperature, time}; +use heim::{disk, host, memory, net, sensors}; use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, Tag, Tagged, TaggedDictBuilder, Value, }; -use heim::{disk, memory, net, sensors, host}; -use heim::units::{frequency, information, time, thermodynamic_temperature}; struct Sys; impl Sys { @@ -17,30 +17,33 @@ impl Sys { } async fn cpu(tag: Tag) -> Option> { - match futures::future::try_join( - heim::cpu::logical_count(), - heim::cpu::frequency(), - ).await { + match futures::future::try_join(heim::cpu::logical_count(), heim::cpu::frequency()).await { Ok((num_cpu, cpu_speed)) => { let mut cpu_idx = TaggedDictBuilder::with_capacity(tag, 4); cpu_idx.insert("cores", Primitive::Int(num_cpu as i64)); let current_speed = - (cpu_speed.current().get::() as f64 / 1_000_000_000.0 * 100.0).round() / 100.0; - cpu_idx.insert("current ghz", Primitive::Float(current_speed.into())); + (cpu_speed.current().get::() as f64 / 1_000_000_000.0 * 100.0) + .round() + / 100.0; + cpu_idx.insert("current ghz", Primitive::number(current_speed)); if let Some(min_speed) = cpu_speed.min() { - let min_speed = (min_speed.get::() as f64 / 1_000_000_000.0 * 100.0).round() / 100.0; - cpu_idx.insert("min ghz", Primitive::Float(min_speed.into())); + let min_speed = + (min_speed.get::() as f64 / 1_000_000_000.0 * 100.0).round() + / 100.0; + cpu_idx.insert("min ghz", Primitive::number(min_speed)); } if let Some(max_speed) = cpu_speed.max() { - let max_speed = (max_speed.get::() as f64 / 1_000_000_000.0 * 100.0).round() / 100.0; - cpu_idx.insert("max ghz", Primitive::Float(max_speed.into())); + let max_speed = + (max_speed.get::() as f64 / 1_000_000_000.0 * 100.0).round() + / 100.0; + cpu_idx.insert("max ghz", Primitive::number(max_speed)); } Some(cpu_idx.into_tagged_value()) - }, + } Err(_) => None, } } @@ -48,19 +51,29 @@ async fn cpu(tag: Tag) -> Option> { async fn mem(tag: Tag) -> Tagged { let mut dict = TaggedDictBuilder::with_capacity(tag, 4); - let (memory_result, swap_result) = futures::future::join( - memory::memory(), - memory::swap() - ).await; + let (memory_result, swap_result) = + futures::future::join(memory::memory(), memory::swap()).await; if let Ok(memory) = memory_result { - dict.insert("total", Value::bytes(memory.total().get::())); - dict.insert("free", Value::bytes(memory.free().get::())); + dict.insert( + "total", + Value::bytes(memory.total().get::()), + ); + dict.insert( + "free", + Value::bytes(memory.free().get::()), + ); } if let Ok(swap) = swap_result { - dict.insert("swap total", Value::bytes(swap.total().get::())); - dict.insert("swap free", Value::bytes(swap.free().get::())); + dict.insert( + "swap total", + Value::bytes(swap.total().get::()), + ); + dict.insert( + "swap free", + Value::bytes(swap.free().get::()), + ); } dict.into_tagged_value() @@ -69,10 +82,8 @@ async fn mem(tag: Tag) -> Tagged { async fn host(tag: Tag) -> Tagged { let mut dict = TaggedDictBuilder::with_capacity(tag, 6); - let (platform_result, uptime_result) = futures::future::join( - host::platform(), - host::uptime(), - ).await; + let (platform_result, uptime_result) = + futures::future::join(host::platform(), host::uptime()).await; // OS if let Ok(platform) = platform_result { @@ -133,9 +144,18 @@ async fn disks(tag: Tag) -> Option { dict.insert("mount", Value::string(part.mount_point().to_string_lossy())); if let Ok(usage) = disk::usage(part.mount_point().to_path_buf()).await { - dict.insert("total", Value::bytes(usage.total().get::())); - dict.insert("used", Value::bytes(usage.used().get::())); - dict.insert("free", Value::bytes(usage.free().get::())); + dict.insert( + "total", + Value::bytes(usage.total().get::()), + ); + dict.insert( + "used", + Value::bytes(usage.used().get::()), + ); + dict.insert( + "free", + Value::bytes(usage.free().get::()), + ); } output.push(dict.into_tagged_value()); @@ -169,13 +189,13 @@ async fn battery(tag: Tag) -> Option { if let Some(time_to_full) = battery.time_to_full() { dict.insert( "mins to full", - Value::float(time_to_full.get::() as f64), + Value::number(time_to_full.get::()), ); } if let Some(time_to_empty) = battery.time_to_empty() { dict.insert( "mins to empty", - Value::float(time_to_empty.get::() as f64), + Value::number(time_to_empty.get::()), ); } output.push(dict.into_tagged_value()); @@ -202,12 +222,25 @@ async fn temp(tag: Tag) -> Option { if let Some(label) = sensor.label() { dict.insert("label", Value::string(label)); } - dict.insert("temp", Value::float(sensor.current().get::() as f64)); + dict.insert( + "temp", + Value::number( + sensor + .current() + .get::(), + ), + ); if let Some(high) = sensor.high() { - dict.insert("high", Value::float(high.get::() as f64)); + dict.insert( + "high", + Value::number(high.get::()), + ); } if let Some(critical) = sensor.critical() { - dict.insert("critical", Value::float(critical.get::() as f64)); + dict.insert( + "critical", + Value::number(critical.get::()), + ); } output.push(dict.into_tagged_value()); @@ -228,8 +261,14 @@ async fn net(tag: Tag) -> Option { if let Ok(nic) = nic { let mut network_idx = TaggedDictBuilder::with_capacity(tag, 3); network_idx.insert("name", Value::string(nic.interface())); - network_idx.insert("sent", Value::bytes(nic.bytes_sent().get::())); - network_idx.insert("recv", Value::bytes(nic.bytes_recv().get::())); + network_idx.insert( + "sent", + Value::bytes(nic.bytes_sent().get::()), + ); + network_idx.insert( + "recv", + Value::bytes(nic.bytes_recv().get::()), + ); output.push(network_idx.into_tagged_value()); } } @@ -242,18 +281,10 @@ async fn net(tag: Tag) -> Option { async fn sysinfo(tag: Tag) -> Vec> { let mut sysinfo = TaggedDictBuilder::with_capacity(tag, 7); - - let (host, cpu, disks, memory, temp) = futures::future::join5( - host(tag), - cpu(tag), - disks(tag), - mem(tag), - temp(tag), - ).await; - let (net, battery) = futures::future::join( - net(tag), - battery(tag), - ).await; + + let (host, cpu, disks, memory, temp) = + futures::future::join5(host(tag), cpu(tag), disks(tag), mem(tag), temp(tag)).await; + let (net, battery) = futures::future::join(net(tag), battery(tag)).await; sysinfo.insert_tagged("host", host); if let Some(cpu) = cpu { diff --git a/src/prelude.rs b/src/prelude.rs index 2e4a9d3465..8e511f82cd 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -63,6 +63,7 @@ pub(crate) use crate::object::meta::{Tag, Tagged, TaggedItem}; pub(crate) use crate::object::types::ExtractType; pub(crate) use crate::object::{Primitive, Value}; pub(crate) use crate::parser::hir::SyntaxType; +pub(crate) use crate::parser::parse::parser::Number; pub(crate) use crate::parser::registry::Signature; pub(crate) use crate::shell::filesystem_shell::FilesystemShell; pub(crate) use crate::shell::shell_manager::ShellManager; @@ -74,6 +75,8 @@ pub(crate) use crate::Text; pub(crate) use futures::stream::BoxStream; pub(crate) use futures::{FutureExt, Stream, StreamExt}; pub(crate) use futures_async_stream::async_stream_block; +pub(crate) use num_traits::cast::{FromPrimitive, ToPrimitive}; +pub(crate) use rust_decimal::Decimal; #[allow(unused)] pub(crate) use serde::{Deserialize, Serialize}; pub(crate) use std::collections::VecDeque; diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 039a4b2bcb..9feffcb4ce 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -116,7 +116,7 @@ fn paint_token_node(token_node: &TokenNode, line: &str) -> String { TokenNode::Operator(..) => Color::White.normal().paint(token_node.span().slice(line)), TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.span().slice(line)), TokenNode::Token(Tagged { - item: RawToken::Integer(..), + item: RawToken::Number(..), .. }) => Color::Purple.bold().paint(token_node.span().slice(line)), TokenNode::Token(Tagged {