Basic support for decimal numbers

This commit is more substantial than it looks: there was basically no
real support for decimals before, and that impacted values all the way
through.

I also made Size contain a decimal instead of an integer (`1.6kb` is a
reasonable thing to type), which impacted a bunch of code.

The biggest impact of this commit is that it creates many more possible
ways for valid nu types to fail to serialize as toml, json, etc. which
typically can't support the full range of Decimal (or Bigint, which I
also think we should support). This commit makes to-toml fallible, and a
similar effort is necessary for the rest of the serializations.

We also need to figure out how to clearly communicate to users what has
happened, but failing to serialize to toml seems clearly superior to me
than weird errors in basic math operations.
This commit is contained in:
Yehuda Katz 2019-08-30 10:29:04 -07:00
parent f274df6753
commit 138b5af82b
34 changed files with 477 additions and 178 deletions

50
Cargo.lock generated
View File

@ -1614,8 +1614,8 @@ dependencies = [
"neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "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)", "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-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)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1626,6 +1626,7 @@ dependencies = [
"regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "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)", "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)", "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1648,6 +1649,37 @@ dependencies = [
"which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "num-derive" name = "num-derive"
version = "0.2.5" version = "0.2.5"
@ -1683,6 +1715,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "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-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)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1789,7 +1822,6 @@ 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.98 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -2303,6 +2335,16 @@ name = "rust-ini"
version = "0.13.0" version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.15" version = "0.1.15"
@ -3319,6 +3361,9 @@ dependencies = [
"checksum nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9761d859320e381010a4f7f8ed425f2c924de33ad121ace447367c713ad561b" "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 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 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-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-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" "checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e"
@ -3393,6 +3438,7 @@ dependencies = [
"checksum rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051" "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-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-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-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 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" "checksum rustyline 5.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8ee0838a6594169a1c5f4bb9af0fe692cc99691941710a8cc6576395ede804e"

View File

@ -25,14 +25,15 @@ 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 = "3.0.1" byte-unit = "3.0.1"
ordered-float = {version = "1.0.2", features = ["serde"]}
futures-preview = { version = "=0.3.0-alpha.18", features = ["compat", "io-compat"] } futures-preview = { version = "=0.3.0-alpha.18", features = ["compat", "io-compat"] }
futures-async-stream = "=0.1.0-alpha.5" futures-async-stream = "=0.1.0-alpha.5"
futures_codec = "0.2.5" futures_codec = "0.2.5"
num-traits = "0.2.8"
term = "0.5.2" term = "0.5.2"
bytes = "0.4.12" bytes = "0.4.12"
log = "0.4.8" log = "0.4.8"
pretty_env_logger = "0.3.1" pretty_env_logger = "0.3.1"
rust_decimal = "1.0.3"
serde = { version = "1.0.98", features = ["derive"] } serde = { version = "1.0.98", features = ["derive"] }
bson = "=0.13.0" bson = "=0.13.0"
serde_json = "1.0.40" serde_json = "1.0.40"

View File

@ -1 +1 @@
nightly-2019-08-22 nightly-2019-08-30

View File

@ -1,5 +1,4 @@
use crate::commands::WholeStreamCommand; use crate::commands::WholeStreamCommand;
use crate::object::base::OF64;
use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::object::{Primitive, TaggedDictBuilder, Value};
use crate::prelude::*; use crate::prelude::*;
use bson::{decode_document, spec::BinarySubtype, Bson}; use bson::{decode_document, spec::BinarySubtype, Bson};
@ -28,7 +27,7 @@ fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into<Tag>) -> Tagged<Value
let tag = tag.into(); let tag = tag.into();
match v { match v {
Bson::FloatingPoint(n) => 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::String(s) => Value::Primitive(Primitive::String(String::from(s))).tagged(tag),
Bson::Array(a) => Value::List( Bson::Array(a) => Value::List(
a.iter() a.iter()

View File

@ -1,5 +1,4 @@
use crate::commands::WholeStreamCommand; use crate::commands::WholeStreamCommand;
use crate::object::base::OF64;
use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::object::{Primitive, TaggedDictBuilder, Value};
use crate::prelude::*; use crate::prelude::*;
@ -34,9 +33,7 @@ fn convert_json_value_to_nu_value(v: &serde_hjson::Value, tag: impl Into<Tag>) -
match v { match v {
serde_hjson::Value::Null => Value::Primitive(Primitive::Nothing).tagged(tag), 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::Bool(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(tag),
serde_hjson::Value::F64(n) => { serde_hjson::Value::F64(n) => Value::Primitive(Primitive::from(*n)).tagged(tag),
Value::Primitive(Primitive::Float(OF64::from(*n))).tagged(tag)
}
serde_hjson::Value::U64(n) => Value::Primitive(Primitive::Int(*n as i64)).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::I64(n) => Value::Primitive(Primitive::Int(*n as i64)).tagged(tag),
serde_hjson::Value::String(s) => { serde_hjson::Value::String(s) => {

View File

@ -1,6 +1,5 @@
use crate::commands::WholeStreamCommand; use crate::commands::WholeStreamCommand;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::base::OF64;
use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::object::{Primitive, TaggedDictBuilder, Value};
use crate::prelude::*; use crate::prelude::*;
use rusqlite::{types::ValueRef, Connection, Row, NO_PARAMS}; use rusqlite::{types::ValueRef, Connection, Row, NO_PARAMS};
@ -94,7 +93,7 @@ fn convert_sqlite_value_to_nu_value(value: ValueRef, tag: impl Into<Tag> + Clone
match value { match value {
ValueRef::Null => Value::Primitive(Primitive::String(String::from(""))).tagged(tag), ValueRef::Null => Value::Primitive(Primitive::String(String::from(""))).tagged(tag),
ValueRef::Integer(i) => Value::Primitive(Primitive::Int(i)).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(_) => { t @ ValueRef::Text(_) => {
// this unwrap is safe because we know the ValueRef is Text. // this unwrap is safe because we know the ValueRef is Text.
Value::Primitive(Primitive::String(t.as_str().unwrap().to_string())).tagged(tag) Value::Primitive(Primitive::String(t.as_str().unwrap().to_string())).tagged(tag)

View File

@ -1,5 +1,4 @@
use crate::commands::WholeStreamCommand; use crate::commands::WholeStreamCommand;
use crate::object::base::OF64;
use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::object::{Primitive, TaggedDictBuilder, Value};
use crate::prelude::*; use crate::prelude::*;
@ -29,7 +28,7 @@ pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into<Tag>) -> T
match v { match v {
toml::Value::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(tag), toml::Value::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(tag),
toml::Value::Integer(n) => Value::Primitive(Primitive::Int(*n)).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::String(s) => Value::Primitive(Primitive::String(String::from(s))).tagged(tag),
toml::Value::Array(a) => Value::List( toml::Value::Array(a) => Value::List(
a.iter() a.iter()

View File

@ -1,5 +1,4 @@
use crate::commands::WholeStreamCommand; use crate::commands::WholeStreamCommand;
use crate::object::base::OF64;
use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::object::{Primitive, TaggedDictBuilder, Value};
use crate::prelude::*; use crate::prelude::*;
@ -52,7 +51,7 @@ fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, tag: impl Into<Tag>) ->
Value::Primitive(Primitive::Int(n.as_i64().unwrap())).tagged(tag) Value::Primitive(Primitive::Int(n.as_i64().unwrap())).tagged(tag)
} }
serde_yaml::Value::Number(n) if n.is_f64() => { 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::String(s) => Value::string(s).tagged(tag),
serde_yaml::Value::Sequence(a) => Value::List( serde_yaml::Value::Sequence(a) => Value::List(

View File

@ -62,7 +62,7 @@ fn ps(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, She
if let Ok(status) = process.status().await { if let Ok(status) = process.status().await {
dict.insert("status", Value::string(format!("{:?}", status))); dict.insert("status", Value::string(format!("{:?}", status)));
} }
dict.insert("cpu", Value::float(usage.get::<ratio::percent>() as f64)); dict.insert("cpu", Value::number(usage.get::<ratio::percent>()));
yield ReturnSuccess::value(dict.into_tagged_value()); yield ReturnSuccess::value(dict.into_tagged_value());
} }
} }

View File

@ -1,5 +1,4 @@
use crate::commands::UnevaluatedCallInfo; use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand};
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::Value; use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;

View File

@ -27,11 +27,16 @@ impl WholeStreamCommand for ToBSON {
pub fn value_to_bson_value(v: &Value) -> Bson { pub fn value_to_bson_value(v: &Value) -> Bson {
match v { match v {
Value::Primitive(Primitive::Boolean(b)) => Bson::Boolean(*b), 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::Date(d)) => Bson::UtcDatetime(*d),
Value::Primitive(Primitive::EndOfStream) => Bson::Null, Value::Primitive(Primitive::EndOfStream) => Bson::Null,
Value::Primitive(Primitive::BeginningOfStream) => 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::Int(i)) => Bson::I64(*i),
Value::Primitive(Primitive::Nothing) => Bson::Null, Value::Primitive(Primitive::Nothing) => Bson::Null,
Value::Primitive(Primitive::String(s)) => Bson::String(s.clone()), Value::Primitive(Primitive::String(s)) => Bson::String(s.clone()),

View File

@ -45,7 +45,7 @@ pub fn value_to_csv_value(v: &Value) -> Value {
fn to_string_helper(v: &Value) -> Result<String, Box<dyn std::error::Error>> { fn to_string_helper(v: &Value) -> Result<String, Box<dyn std::error::Error>> {
match v { match v {
Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), 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::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?),
Value::List(_) => return Ok(String::from("[list list]")), Value::List(_) => return Ok(String::from("[list list]")),
Value::Object(_) => return Ok(String::from("[object]")), Value::Object(_) => return Ok(String::from("[object]")),

View File

@ -25,15 +25,18 @@ impl WholeStreamCommand for ToJSON {
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 {
Value::Primitive(Primitive::Boolean(b)) => serde_json::Value::Bool(*b), Value::Primitive(Primitive::Boolean(b)) => serde_json::Value::Bool(*b),
Value::Primitive(Primitive::Bytes(b)) => { Value::Primitive(Primitive::Bytes(b)) => serde_json::Value::Number(
serde_json::Value::Number(serde_json::Number::from(*b as u64)) 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::Date(d)) => serde_json::Value::String(d.to_string()),
Value::Primitive(Primitive::EndOfStream) => serde_json::Value::Null, Value::Primitive(Primitive::EndOfStream) => serde_json::Value::Null,
Value::Primitive(Primitive::BeginningOfStream) => serde_json::Value::Null, Value::Primitive(Primitive::BeginningOfStream) => serde_json::Value::Null,
Value::Primitive(Primitive::Float(f)) => { Value::Primitive(Primitive::Decimal(f)) => serde_json::Value::Number(
serde_json::Value::Number(serde_json::Number::from_f64(f.into_inner()).unwrap()) serde_json::Number::from_f64(
} f.to_f64().expect("TODO: What about really big decimals?"),
)
.unwrap(),
),
Value::Primitive(Primitive::Int(i)) => { Value::Primitive(Primitive::Int(i)) => {
serde_json::Value::Number(serde_json::Number::from(*i)) serde_json::Value::Number(serde_json::Number::from(*i))
} }

View File

@ -73,7 +73,7 @@ fn nu_value_to_sqlite_string(v: Value) -> String {
Value::Primitive(p) => match p { Value::Primitive(p) => match p {
Primitive::Nothing => "NULL".into(), Primitive::Nothing => "NULL".into(),
Primitive::Int(i) => format!("{}", i), Primitive::Int(i) => format!("{}", i),
Primitive::Float(f) => format!("{}", f.into_inner()), Primitive::Decimal(f) => format!("{}", f),
Primitive::Bytes(u) => format!("{}", u), Primitive::Bytes(u) => format!("{}", u),
Primitive::String(s) => format!("'{}'", s.replace("'", "''")), Primitive::String(s) => format!("'{}'", s.replace("'", "''")),
Primitive::Boolean(true) => "1".into(), Primitive::Boolean(true) => "1".into(),

View File

@ -1,4 +1,5 @@
use crate::commands::WholeStreamCommand; use crate::commands::WholeStreamCommand;
use crate::errors::ranged;
use crate::object::{Primitive, Value}; use crate::object::{Primitive, Value};
use crate::prelude::*; use crate::prelude::*;
@ -22,10 +23,12 @@ impl WholeStreamCommand for ToTOML {
} }
} }
pub fn value_to_toml_value(v: &Value) -> toml::Value { pub fn value_to_toml_value(v: &Value) -> Result<toml::Value, ShellError> {
match v { Ok(match v {
Value::Primitive(Primitive::Boolean(b)) => toml::Value::Boolean(*b), 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::Date(d)) => toml::Value::String(d.to_string()),
Value::Primitive(Primitive::EndOfStream) => { Value::Primitive(Primitive::EndOfStream) => {
toml::Value::String("<End of Stream>".to_string()) toml::Value::String("<End of Stream>".to_string())
@ -33,13 +36,15 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value {
Value::Primitive(Primitive::BeginningOfStream) => { Value::Primitive(Primitive::BeginningOfStream) => {
toml::Value::String("<Beginning of Stream>".to_string()) toml::Value::String("<Beginning of Stream>".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::Int(i)) => toml::Value::Integer(*i),
Value::Primitive(Primitive::Nothing) => toml::Value::String("<Nothing>".to_string()), Value::Primitive(Primitive::Nothing) => toml::Value::String("<Nothing>".to_string()),
Value::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()), Value::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()),
Value::Primitive(Primitive::Path(s)) => toml::Value::String(s.display().to_string()), 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("<Block>".to_string()), Value::Block(_) => toml::Value::String("<Block>".to_string()),
Value::Binary(b) => { Value::Binary(b) => {
toml::Value::Array(b.iter().map(|x| toml::Value::Integer(*x as i64)).collect()) 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) => { Value::Object(o) => {
let mut m = toml::map::Map::new(); let mut m = toml::map::Map::new();
for (k, v) in o.entries.iter() { 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) toml::Value::Table(m)
} }
})
}
fn collect_values(input: &Vec<Tagged<Value>>) -> Result<Vec<toml::Value>, 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<OutputStream, ShellError> { fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
@ -61,7 +76,7 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
Ok(out Ok(out
.values .values
.map(move |a| match toml::to_string(&value_to_toml_value(&a)) { .map(move |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)).simple_spanned(name_span), Value::Primitive(Primitive::String(val)).simple_spanned(name_span),

View File

@ -45,7 +45,7 @@ pub fn value_to_tsv_value(v: &Value) -> Value {
fn to_string_helper(v: &Value) -> Result<String, Box<dyn std::error::Error>> { fn to_string_helper(v: &Value) -> Result<String, Box<dyn std::error::Error>> {
match v { match v {
Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), 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::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?),
Value::List(_) => return Ok(String::from("[list list]")), Value::List(_) => return Ok(String::from("[list list]")),
Value::Object(_) => return Ok(String::from("[object]")), Value::Object(_) => return Ok(String::from("[object]")),

View File

@ -26,13 +26,13 @@ pub fn value_to_yaml_value(v: &Value) -> serde_yaml::Value {
match v { match v {
Value::Primitive(Primitive::Boolean(b)) => serde_yaml::Value::Bool(*b), Value::Primitive(Primitive::Boolean(b)) => serde_yaml::Value::Bool(*b),
Value::Primitive(Primitive::Bytes(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::Date(d)) => serde_yaml::Value::String(d.to_string()),
Value::Primitive(Primitive::EndOfStream) => serde_yaml::Value::Null, Value::Primitive(Primitive::EndOfStream) => serde_yaml::Value::Null,
Value::Primitive(Primitive::BeginningOfStream) => serde_yaml::Value::Null, Value::Primitive(Primitive::BeginningOfStream) => serde_yaml::Value::Null,
Value::Primitive(Primitive::Float(f)) => { Value::Primitive(Primitive::Decimal(f)) => {
serde_yaml::Value::Number(serde_yaml::Number::from(f.into_inner())) serde_yaml::Value::Number(serde_yaml::Number::from(f.to_f64().unwrap()))
} }
Value::Primitive(Primitive::Int(i)) => { Value::Primitive(Primitive::Int(i)) => {
serde_yaml::Value::Number(serde_yaml::Number::from(*i)) serde_yaml::Value::Number(serde_yaml::Number::from(*i))

View File

@ -27,7 +27,7 @@ impl PerItemCommand for Where {
let stream = match condition { let stream = match condition {
Tagged { Tagged {
item: Value::Block(block), item: Value::Block(block),
tag, ..
} => { } => {
let result = block.invoke(&input_clone); let result = block.invoke(&input_clone);
match result { match result {
@ -39,11 +39,7 @@ impl PerItemCommand for Where {
} }
} }
Err(e) => { Err(e) => {
return Err(ShellError::labeled_error( return Err(e)
format!("Could not evaluate ({})", e.to_string()),
"could not evaluate",
tag.span,
))
} }
} }
} }

View File

@ -84,6 +84,17 @@ impl ShellError {
.start() .start()
} }
pub(crate) fn range_error(
expected: impl Into<String>,
actual: Tagged<impl fmt::Debug>,
) -> ShellError {
ProximateShellError::RangeError {
kind: expected.into(),
actual_kind: actual.map(|a| format!("{:?}", a)),
}
.start()
}
pub(crate) fn syntax_error(problem: Tagged<impl Into<String>>) -> ShellError { pub(crate) fn syntax_error(problem: Tagged<impl Into<String>>) -> ShellError {
ProximateShellError::SyntaxError { ProximateShellError::SyntaxError {
problem: problem.map(|p| p.into()), problem: problem.map(|p| p.into()),
@ -242,6 +253,20 @@ impl ShellError {
} => Diagnostic::new(Severity::Error, "Type Error") } => Diagnostic::new(Severity::Error, "Type Error")
.with_label(Label::new_primary(span).with_message(expected)), .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 { ProximateShellError::SyntaxError {
problem: problem:
Tagged { Tagged {
@ -344,6 +369,10 @@ pub enum ProximateShellError {
error: ArgumentError, error: ArgumentError,
span: Span, span: Span,
}, },
RangeError {
kind: String,
actual_kind: Tagged<String>,
},
Diagnostic(ShellDiagnostic), Diagnostic(ShellDiagnostic),
CoerceError { CoerceError {
left: Tagged<String>, left: Tagged<String>,
@ -423,6 +452,7 @@ impl std::fmt::Display for ShellError {
ProximateShellError::MissingValue { .. } => write!(f, "MissingValue"), ProximateShellError::MissingValue { .. } => write!(f, "MissingValue"),
ProximateShellError::InvalidCommand { .. } => write!(f, "InvalidCommand"), ProximateShellError::InvalidCommand { .. } => write!(f, "InvalidCommand"),
ProximateShellError::TypeError { .. } => write!(f, "TypeError"), ProximateShellError::TypeError { .. } => write!(f, "TypeError"),
ProximateShellError::RangeError { .. } => write!(f, "RangeError"),
ProximateShellError::SyntaxError { .. } => write!(f, "SyntaxError"), ProximateShellError::SyntaxError { .. } => write!(f, "SyntaxError"),
ProximateShellError::MissingProperty { .. } => write!(f, "MissingProperty"), ProximateShellError::MissingProperty { .. } => write!(f, "MissingProperty"),
ProximateShellError::ArgumentError { .. } => write!(f, "ArgumentError"), ProximateShellError::ArgumentError { .. } => write!(f, "ArgumentError"),
@ -526,3 +556,14 @@ impl<T> ShellErrorUtils<Tagged<T>> for Option<Tagged<T>> {
} }
} }
} }
pub fn ranged<T>(
input: Option<T>,
expected: impl Into<String>,
actual: Tagged<impl fmt::Debug>,
) -> Result<T, ShellError> {
match input {
Some(v) => Ok(v),
None => Err(ShellError::range_error(expected, actual)),
}
}

View File

@ -106,7 +106,7 @@ pub(crate) fn evaluate_baseline_expr(
fn evaluate_literal(literal: Tagged<hir::Literal>, source: &Text) -> Tagged<Value> { fn evaluate_literal(literal: Tagged<hir::Literal>, source: &Text) -> Tagged<Value> {
let result = match literal.item { 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::Size(int, unit) => unit.compute(int),
hir::Literal::String(span) => Value::string(span.slice(source)), hir::Literal::String(span) => Value::string(span.slice(source)),
hir::Literal::Bare => Value::string(literal.span().slice(source)), hir::Literal::Bare => Value::string(literal.span().slice(source)),

View File

@ -25,12 +25,12 @@ mod utils;
pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue};
pub use crate::context::{SourceMap, SpanSource}; pub use crate::context::{SourceMap, SpanSource};
pub use crate::env::host::BasicHost; pub use crate::env::host::BasicHost;
pub use crate::object::base::OF64;
pub use crate::parser::hir::SyntaxType; pub use crate::parser::hir::SyntaxType;
pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::plugin::{serve_plugin, Plugin};
pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath};
pub use cli::cli; pub use cli::cli;
pub use errors::ShellError; pub use errors::ShellError;
pub use num_traits::cast::ToPrimitive;
pub use object::base::{Primitive, Value}; pub use object::base::{Primitive, Value};
pub use object::dict::{Dictionary, TaggedDictBuilder}; pub use object::dict::{Dictionary, TaggedDictBuilder};
pub use object::meta::{Span, Tag, Tagged, TaggedItem}; pub use object::meta::{Span, Tag, Tagged, TaggedItem};

View File

@ -8,35 +8,16 @@ use crate::Text;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use chrono_humanize::Humanize; use chrono_humanize::Humanize;
use derive_new::new; use derive_new::new;
use ordered_float::OrderedFloat;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::SystemTime; use std::time::SystemTime;
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, new, Serialize, Deserialize)]
pub struct OF64 {
pub(crate) inner: OrderedFloat<f64>,
}
impl OF64 {
pub(crate) fn into_inner(&self) -> f64 {
self.inner.into_inner()
}
}
impl From<f64> for OF64 {
fn from(float: f64) -> Self {
OF64::new(OrderedFloat(float))
}
}
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize, Serialize)] #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize, Serialize)]
pub enum Primitive { pub enum Primitive {
Nothing, Nothing,
Int(i64), Int(i64),
#[allow(unused)] Decimal(Decimal),
Float(OF64),
Bytes(u64), Bytes(u64),
String(String), String(String),
Boolean(bool), Boolean(bool),
@ -48,6 +29,24 @@ pub enum Primitive {
EndOfStream, EndOfStream,
} }
impl From<i64> for Primitive {
fn from(int: i64) -> Primitive {
Primitive::Int(int)
}
}
impl From<Decimal> for Primitive {
fn from(decimal: Decimal) -> Primitive {
Primitive::Decimal(decimal)
}
}
impl From<f64> for Primitive {
fn from(float: f64) -> Primitive {
Primitive::Decimal(Decimal::from_f64(float).unwrap())
}
}
impl Primitive { impl Primitive {
pub(crate) fn type_name(&self) -> String { pub(crate) fn type_name(&self) -> String {
use Primitive::*; use Primitive::*;
@ -58,7 +57,7 @@ impl Primitive {
EndOfStream => "end-of-stream", EndOfStream => "end-of-stream",
Path(_) => "path", Path(_) => "path",
Int(_) => "int", Int(_) => "int",
Float(_) => "float", Decimal(_) => "decimal",
Bytes(_) => "bytes", Bytes(_) => "bytes",
String(_) => "string", String(_) => "string",
Boolean(_) => "boolean", Boolean(_) => "boolean",
@ -76,7 +75,7 @@ impl Primitive {
EndOfStream => write!(f, "EndOfStream"), EndOfStream => write!(f, "EndOfStream"),
Int(int) => write!(f, "{}", int), Int(int) => write!(f, "{}", int),
Path(path) => write!(f, "{}", path.display()), Path(path) => write!(f, "{}", path.display()),
Float(float) => write!(f, "{:?}", float), Decimal(decimal) => write!(f, "{}", decimal),
Bytes(bytes) => write!(f, "{}", bytes), Bytes(bytes) => write!(f, "{}", bytes),
String(string) => write!(f, "{:?}", string), String(string) => write!(f, "{:?}", string),
Boolean(boolean) => write!(f, "{}", boolean), Boolean(boolean) => write!(f, "{}", boolean),
@ -84,6 +83,15 @@ impl Primitive {
} }
} }
pub fn number(number: impl Into<Number>) -> 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 { pub fn format(&self, field_name: Option<&String>) -> String {
match self { match self {
Primitive::Nothing => String::new(), Primitive::Nothing => String::new(),
@ -105,7 +113,7 @@ impl Primitive {
} }
} }
Primitive::Int(i) => format!("{}", i), 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::String(s) => format!("{}", s),
Primitive::Boolean(b) => match (b, field_name) { Primitive::Boolean(b) => match (b, field_name) {
(true, None) => format!("Yes"), (true, None) => format!("Yes"),
@ -122,7 +130,7 @@ impl Primitive {
pub fn style(&self) -> &'static str { pub fn style(&self) -> &'static str {
match self { match self {
Primitive::Bytes(0) => "c", // centre 'missing' indicator 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), Block(Block),
} }
impl Into<Value> 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<Tagged<Value>>) -> ValuesDebug<'_> { pub fn debug_list(values: &Vec<Tagged<Value>>) -> ValuesDebug<'_> {
ValuesDebug { values } ValuesDebug { values }
} }
@ -518,7 +535,11 @@ impl Value {
} }
#[allow(unused)] #[allow(unused)]
pub(crate) fn compare(&self, operator: &Operator, other: &Value) -> Result<bool, (String, String)> { pub(crate) fn compare(
&self,
operator: &Operator,
other: &Value,
) -> Result<bool, (String, String)> {
match operator { match operator {
_ => { _ => {
let coerced = coerce_compare(self, other)?; let coerced = coerce_compare(self, other)?;
@ -566,7 +587,7 @@ impl Value {
match self { match self {
Value::Primitive(Primitive::String(s)) => Ok(s.clone()), Value::Primitive(Primitive::String(s)) => Ok(s.clone()),
Value::Primitive(Primitive::Boolean(x)) => Ok(format!("{}", x)), 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::Int(x)) => Ok(format!("{}", x)),
Value::Primitive(Primitive::Bytes(x)) => Ok(format!("{}", x)), Value::Primitive(Primitive::Bytes(x)) => Ok(format!("{}", x)),
// TODO: this should definitely be more general with better errors // TODO: this should definitely be more general with better errors
@ -612,8 +633,17 @@ impl Value {
Value::Primitive(Primitive::Int(s.into())) Value::Primitive(Primitive::Int(s.into()))
} }
pub fn float(s: impl Into<OF64>) -> Value { pub fn decimal(s: impl Into<Decimal>) -> Value {
Value::Primitive(Primitive::Float(s.into())) Value::Primitive(Primitive::Decimal(s.into()))
}
pub fn number(s: impl Into<Number>) -> 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<bool>) -> Value { pub fn boolean(s: impl Into<bool>) -> 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, (Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => i != *i2,
_ => false, _ => false,
}, },
Value::Primitive(Primitive::Float(i)) => match (op, rhs) { Value::Primitive(Primitive::Decimal(i)) => match (op, rhs) {
(Operator::LessThan, Value::Primitive(Primitive::Float(i2))) => i < *i2, (Operator::LessThan, Value::Primitive(Primitive::Decimal(i2))) => i < *i2,
(Operator::GreaterThan, Value::Primitive(Primitive::Float(i2))) => i > *i2, (Operator::GreaterThan, Value::Primitive(Primitive::Decimal(i2))) => i > *i2,
(Operator::LessThanOrEqual, Value::Primitive(Primitive::Float(i2))) => i <= *i2, (Operator::LessThanOrEqual, Value::Primitive(Primitive::Decimal(i2))) => {
(Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Float(i2))) => { i <= *i2
}
(Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Decimal(i2))) => {
i >= *i2 i >= *i2
} }
(Operator::Equal, Value::Primitive(Primitive::Float(i2))) => i == *i2, (Operator::Equal, Value::Primitive(Primitive::Decimal(i2))) => i == *i2,
(Operator::NotEqual, Value::Primitive(Primitive::Float(i2))) => i != *i2, (Operator::NotEqual, Value::Primitive(Primitive::Decimal(i2))) => i != *i2,
(Operator::LessThan, Value::Primitive(Primitive::Int(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))) => { (Operator::GreaterThan, Value::Primitive(Primitive::Int(i2))) => {
i.into_inner() > *i2 as f64 i > Decimal::from(*i2)
} }
(Operator::LessThanOrEqual, Value::Primitive(Primitive::Int(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))) => { (Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Int(i2))) => {
i.into_inner() >= *i2 as f64 i >= Decimal::from(*i2)
} }
(Operator::Equal, Value::Primitive(Primitive::Int(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))) => { (Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => {
i.into_inner() != *i2 as f64 i != Decimal::from(*i2)
} }
_ => false, _ => false,
@ -765,8 +797,8 @@ pub(crate) fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool
enum CompareValues { enum CompareValues {
Ints(i64, i64), Ints(i64, i64),
Floats(OF64, OF64), Decimals(Decimal, Decimal),
Bytes(i128, i128), Bytes(u64, u64),
String(String, String), String(String, String),
} }
@ -774,7 +806,7 @@ impl CompareValues {
fn compare(&self) -> std::cmp::Ordering { fn compare(&self) -> std::cmp::Ordering {
match self { match self {
CompareValues::Ints(left, right) => left.cmp(right), 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::Bytes(left, right) => left.cmp(right),
CompareValues::String(left, right) => left.cmp(right), CompareValues::String(left, right) => left.cmp(right),
} }
@ -797,10 +829,11 @@ fn coerce_compare_primitive(
Ok(match (left, right) { Ok(match (left, right) {
(Int(left), Int(right)) => CompareValues::Ints(*left, *right), (Int(left), Int(right)) => CompareValues::Ints(*left, *right),
(Float(left), Int(right)) => CompareValues::Floats(*left, (*right as f64).into()), (Decimal(left), Decimal(right)) => CompareValues::Decimals(*left, *right),
(Int(left), Float(right)) => CompareValues::Floats((*left as f64).into(), *right), (Decimal(left), Int(right)) => CompareValues::Decimals(*left, (*right).into()),
(Int(left), Bytes(right)) => CompareValues::Bytes(*left as i128, *right as i128), (Int(left), Decimal(right)) => CompareValues::Decimals((*left).into(), *right),
(Bytes(left), Int(right)) => CompareValues::Bytes(*left as i128, *right as i128), (Int(left), Bytes(right)) => CompareValues::Bytes(*left as u64, *right),
(Bytes(left), Int(right)) => CompareValues::Bytes(*left, *right as u64),
(String(left), String(right)) => CompareValues::String(left.clone(), right.clone()), (String(left), String(right)) => CompareValues::String(left.clone(), right.clone()),
_ => return Err((left.type_name(), right.type_name())), _ => return Err((left.type_name(), right.type_name())),
}) })

View File

@ -36,7 +36,7 @@ pub(crate) fn write_config(config: &IndexMap<String, Tagged<Value>>) -> Result<(
let filename = location.join("config.toml"); let filename = location.join("config.toml");
touch(&filename)?; 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)?; let contents = toml::to_string(&contents)?;

29
src/object/process.rs Normal file
View File

@ -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<Tag>) -> Tagged<Value> {
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()
}

View File

@ -129,11 +129,15 @@ impl RawExpression {
pub type Expression = Tagged<RawExpression>; pub type Expression = Tagged<RawExpression>;
impl Expression { impl Expression {
pub(crate) fn int(i: impl Into<i64>, span: impl Into<Span>) -> Expression { pub(crate) fn number(i: impl Into<Number>, span: impl Into<Span>) -> Expression {
Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Integer(i.into())), span) Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Number(i.into())), span)
} }
pub(crate) fn size(i: impl Into<i64>, unit: impl Into<Unit>, span: impl Into<Span>) -> Expression { pub(crate) fn size(
i: impl Into<Number>,
unit: impl Into<Unit>,
span: impl Into<Span>,
) -> Expression {
Tagged::from_simple_spanned_item( Tagged::from_simple_spanned_item(
RawExpression::Literal(Literal::Size(i.into(), unit.into())), RawExpression::Literal(Literal::Size(i.into(), unit.into())),
span, span,
@ -224,8 +228,8 @@ impl From<Tagged<Path>> for Expression {
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Literal { pub enum Literal {
Integer(i64), Number(Number),
Size(i64, Unit), Size(Number, Unit),
String(Span), String(Span),
Bare, Bare,
} }
@ -233,8 +237,8 @@ pub enum Literal {
impl ToDebug for Tagged<&Literal> { impl ToDebug for Tagged<&Literal> {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result {
match self.item() { match self.item() {
Literal::Integer(int) => write!(f, "{}", *int), Literal::Number(number) => write!(f, "{:?}", *number),
Literal::Size(int, unit) => write!(f, "{}{:?}", *int, unit), Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit),
Literal::String(span) => write!(f, "{}", span.slice(source)), Literal::String(span) => write!(f, "{}", span.slice(source)),
Literal::Bare => write!(f, "{}", self.span().slice(source)), Literal::Bare => write!(f, "{}", self.span().slice(source)),
} }
@ -244,7 +248,7 @@ impl ToDebug for Tagged<&Literal> {
impl Literal { impl Literal {
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
match self { match self {
Literal::Integer(_) => "integer", Literal::Number(..) => "number",
Literal::Size(..) => "size", Literal::Size(..) => "size",
Literal::String(..) => "string", Literal::String(..) => "string",
Literal::Bare => "string", Literal::Bare => "string",

View File

@ -5,7 +5,7 @@ use std::path::PathBuf;
pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Expression { pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Expression {
match *token.item() { 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::Size(int, unit) => hir::Expression::size(int, unit, token.span()),
RawToken::String(span) => hir::Expression::string(span, token.span()), RawToken::String(span) => hir::Expression::string(span, token.span()),
RawToken::Variable(span) if span.slice(source) == "it" => { 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::External(span) => hir::Expression::external_command(span, token.span()),
RawToken::Variable(span) => hir::Expression::variable(span, token.span()), RawToken::Variable(span) => hir::Expression::variable(span, token.span()),
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::Size(number, unit) => hir::Expression::size(number, unit, token.span()),
RawToken::Bare => hir::Expression::bare(token.span()), RawToken::Bare => hir::Expression::bare(token.span()),
RawToken::String(span) => hir::Expression::string(span, 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::External(span) => hir::Expression::external_command(span, token.span()),
RawToken::Variable(span) => hir::Expression::variable(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::Size(_, _) => hir::Expression::bare(token.span()),
RawToken::Bare => hir::Expression::bare(token.span()), RawToken::Bare => hir::Expression::bare(token.span()),
RawToken::String(span) => hir::Expression::string(span, 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::External(span) => hir::Expression::external_command(span, token.span()),
RawToken::Variable(span) => hir::Expression::variable(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::Size(_, _) => hir::Expression::bare(token.span()),
RawToken::Bare => hir::Expression::file_path( RawToken::Bare => hir::Expression::file_path(
expand_path(token.span().slice(source), context), expand_path(token.span().slice(source), context),

View File

@ -294,7 +294,7 @@ pub fn baseline_parse_path(
TokenNode::Token(token) => match token.item() { TokenNode::Token(token) => match token.item() {
RawToken::Bare => token.span().slice(source), RawToken::Bare => token.span().slice(source),
RawToken::String(span) => span.slice(source), RawToken::String(span) => span.slice(source),
RawToken::Integer(_) RawToken::Number(_)
| RawToken::Size(..) | RawToken::Size(..)
| RawToken::Variable(_) | RawToken::Variable(_)
| RawToken::External(_) => { | RawToken::External(_) => {

View File

@ -4,6 +4,7 @@ use crate::parser::parse::{
call_node::*, flag::*, operator::*, pipeline::*, token_tree::*, token_tree_builder::*, call_node::*, flag::*, operator::*, pipeline::*, token_tree::*, token_tree_builder::*,
tokens::*, unit::*, tokens::*, unit::*,
}; };
use crate::prelude::*;
use crate::{Span, Tagged}; use crate::{Span, Tagged};
use nom; use nom;
use nom::branch::*; use nom::branch::*;
@ -18,6 +19,7 @@ use nom::dbg;
use nom::*; use nom::*;
use nom::{AsBytes, FindSubstring, IResult, InputLength, InputTake, Slice}; use nom::{AsBytes, FindSubstring, IResult, InputLength, InputTake, Slice};
use nom5_locate::{position, LocatedSpan}; use nom5_locate::{position, LocatedSpan};
use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use std::str::FromStr; use std::str::FromStr;
@ -68,16 +70,94 @@ fn trace_step<'a, T: Debug>(
} }
} }
pub fn raw_integer(input: NomSpan) -> IResult<NomSpan, Tagged<i64>> { #[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<u32> 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<Number> for f32 {
fn into(self) -> Number {
Number::Decimal(Decimal::from_f32(self).unwrap())
}
}
impl Into<Number> for f64 {
fn into(self) -> Number {
Number::Decimal(Decimal::from_f64(self).unwrap())
}
}
impl Into<Number> for i64 {
fn into(self) -> Number {
Number::Int(self)
}
}
impl Into<Number> for Decimal {
fn into(self) -> Number {
Number::Decimal(self)
}
}
pub fn raw_number(input: NomSpan) -> IResult<NomSpan, Tagged<Number>> {
let original = input;
let start = input.offset; 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, neg) = opt(tag("-"))(input)?;
let (input, num) = digit1(input)?; let (input, head) = digit1(input)?;
let dot: IResult<NomSpan, NomSpan, (NomSpan, nom::error::ErrorKind)> = 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 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(( Ok((
input, 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<NomSpan, TokenNode> {
trace_step(input, "size", move |input| { trace_step(input, "size", move |input| {
let mut is_size = false; let mut is_size = false;
let start = input.offset; 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) { if let Ok((input, Some(size))) = opt(raw_unit)(input) {
let end = input.offset; let end = input.offset;
@ -256,7 +336,7 @@ pub fn size(input: NomSpan) -> IResult<NomSpan, TokenNode> {
Ok(( Ok((
input, input,
TokenTreeBuilder::spanned_size((*int, *size), (start, end)), TokenTreeBuilder::spanned_size((number.item, *size), (start, end)),
)) ))
} else { } else {
let end = input.offset; let end = input.offset;
@ -266,7 +346,10 @@ pub fn size(input: NomSpan) -> IResult<NomSpan, TokenNode> {
return Err(nom::Err::Error((input, nom::error::ErrorKind::Char))); 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() { fn test_integer() {
assert_leaf! { assert_leaf! {
parsers [ size ] parsers [ size ]
"123" -> 0..3 { Integer(123) } "123" -> 0..3 { Number(Number::Int(123)) }
} }
assert_leaf! { assert_leaf! {
parsers [ size ] parsers [ size ]
"-123" -> 0..4 { Integer(-123) } "-123" -> 0..4 { Number(Number::Int(-123)) }
} }
} }
@ -638,12 +721,12 @@ mod tests {
fn test_size() { fn test_size() {
assert_leaf! { assert_leaf! {
parsers [ size ] parsers [ size ]
"123MB" -> 0..5 { Size(123, Unit::MB) } "123MB" -> 0..5 { Size(Number::Int(123), Unit::MB) }
} }
assert_leaf! { assert_leaf! {
parsers [ size ] parsers [ size ]
"10GB" -> 0..4 { Size(10, Unit::GB) } "10GB" -> 0..4 { Size(Number::Int(10), Unit::GB) }
} }
} }

View File

@ -3,6 +3,7 @@ use crate::prelude::*;
use crate::parser::parse::flag::{Flag, FlagKind}; use crate::parser::parse::flag::{Flag, FlagKind};
use crate::parser::parse::operator::Operator; use crate::parser::parse::operator::Operator;
use crate::parser::parse::parser::Number;
use crate::parser::parse::pipeline::{Pipeline, PipelineElement}; use crate::parser::parse::pipeline::{Pipeline, PipelineElement};
use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode}; use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode};
use crate::parser::parse::tokens::{RawToken, Token}; use crate::parser::parse::tokens::{RawToken, Token};
@ -170,9 +171,23 @@ impl TokenTreeBuilder {
}) })
} }
pub fn spanned_int(input: impl Into<i64>, span: impl Into<Span>) -> TokenNode { pub fn spanned_number(input: impl Into<Number>, span: impl Into<Span>) -> 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<Span>) -> TokenNode {
TokenNode::Token(Token::from_simple_spanned_item( 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<Span>) -> TokenNode {
TokenNode::Token(Token::from_simple_spanned_item(
RawToken::Number(Number::Decimal(input)),
span, span,
)) ))
} }
@ -191,7 +206,7 @@ impl TokenTreeBuilder {
} }
pub fn spanned_size( pub fn spanned_size(
input: (impl Into<i64>, impl Into<Unit>), input: (impl Into<Number>, impl Into<Unit>),
span: impl Into<Span>, span: impl Into<Span>,
) -> TokenNode { ) -> TokenNode {
let (int, unit) = (input.0.into(), input.1.into()); let (int, unit) = (input.0.into(), input.1.into());

View File

@ -1,11 +1,12 @@
use crate::parser::parse::unit::*; use crate::parser::parse::unit::*;
use crate::prelude::*;
use crate::{Span, Tagged, Text}; use crate::{Span, Tagged, Text};
use std::fmt; use std::fmt;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum RawToken { pub enum RawToken {
Integer(i64), Number(Number),
Size(i64, Unit), Size(Number, Unit),
String(Span), String(Span),
Variable(Span), Variable(Span),
External(Span), External(Span),
@ -15,7 +16,7 @@ pub enum RawToken {
impl RawToken { impl RawToken {
pub fn type_name(&self) -> &'static str { pub fn type_name(&self) -> &'static str {
match self { match self {
RawToken::Integer(_) => "Integer", RawToken::Number(_) => "Number",
RawToken::Size(..) => "Size", RawToken::Size(..) => "Size",
RawToken::String(_) => "String", RawToken::String(_) => "String",
RawToken::Variable(_) => "Variable", RawToken::Variable(_) => "Variable",

View File

@ -1,4 +1,5 @@
use crate::object::base::Value; use crate::object::base::Value;
use crate::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::str::FromStr; use std::str::FromStr;
@ -24,8 +25,8 @@ impl Unit {
} }
} }
pub(crate) fn compute(&self, size: i64) -> Value { pub(crate) fn compute(&self, size: Number) -> Value {
Value::int(match self { Value::number(match self {
Unit::B => size, Unit::B => size,
Unit::KB => size * 1024, Unit::KB => size * 1024,
Unit::MB => size * 1024 * 1024, Unit::MB => size * 1024 * 1024,

View File

@ -2,12 +2,12 @@ use std::ffi::OsStr;
use futures::executor::block_on; use futures::executor::block_on;
use futures::stream::StreamExt; use futures::stream::StreamExt;
use heim::units::{frequency, information, thermodynamic_temperature, time};
use heim::{disk, host, memory, net, sensors};
use nu::{ use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
Tag, Tagged, TaggedDictBuilder, Value, Tag, Tagged, TaggedDictBuilder, Value,
}; };
use heim::{disk, memory, net, sensors, host};
use heim::units::{frequency, information, time, thermodynamic_temperature};
struct Sys; struct Sys;
impl Sys { impl Sys {
@ -17,30 +17,33 @@ impl Sys {
} }
async fn cpu(tag: Tag) -> Option<Tagged<Value>> { async fn cpu(tag: Tag) -> Option<Tagged<Value>> {
match futures::future::try_join( match futures::future::try_join(heim::cpu::logical_count(), heim::cpu::frequency()).await {
heim::cpu::logical_count(),
heim::cpu::frequency(),
).await {
Ok((num_cpu, cpu_speed)) => { Ok((num_cpu, cpu_speed)) => {
let mut cpu_idx = TaggedDictBuilder::with_capacity(tag, 4); let mut cpu_idx = TaggedDictBuilder::with_capacity(tag, 4);
cpu_idx.insert("cores", Primitive::Int(num_cpu as i64)); cpu_idx.insert("cores", Primitive::Int(num_cpu as i64));
let current_speed = let current_speed =
(cpu_speed.current().get::<frequency::hertz>() as f64 / 1_000_000_000.0 * 100.0).round() / 100.0; (cpu_speed.current().get::<frequency::hertz>() as f64 / 1_000_000_000.0 * 100.0)
cpu_idx.insert("current ghz", Primitive::Float(current_speed.into())); .round()
/ 100.0;
cpu_idx.insert("current ghz", Primitive::number(current_speed));
if let Some(min_speed) = cpu_speed.min() { if let Some(min_speed) = cpu_speed.min() {
let min_speed = (min_speed.get::<frequency::hertz>() as f64 / 1_000_000_000.0 * 100.0).round() / 100.0; let min_speed =
cpu_idx.insert("min ghz", Primitive::Float(min_speed.into())); (min_speed.get::<frequency::hertz>() 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() { if let Some(max_speed) = cpu_speed.max() {
let max_speed = (max_speed.get::<frequency::hertz>() as f64 / 1_000_000_000.0 * 100.0).round() / 100.0; let max_speed =
cpu_idx.insert("max ghz", Primitive::Float(max_speed.into())); (max_speed.get::<frequency::hertz>() 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()) Some(cpu_idx.into_tagged_value())
}, }
Err(_) => None, Err(_) => None,
} }
} }
@ -48,19 +51,29 @@ async fn cpu(tag: Tag) -> Option<Tagged<Value>> {
async fn mem(tag: Tag) -> Tagged<Value> { async fn mem(tag: Tag) -> Tagged<Value> {
let mut dict = TaggedDictBuilder::with_capacity(tag, 4); let mut dict = TaggedDictBuilder::with_capacity(tag, 4);
let (memory_result, swap_result) = futures::future::join( let (memory_result, swap_result) =
memory::memory(), futures::future::join(memory::memory(), memory::swap()).await;
memory::swap()
).await;
if let Ok(memory) = memory_result { if let Ok(memory) = memory_result {
dict.insert("total", Value::bytes(memory.total().get::<information::byte>())); dict.insert(
dict.insert("free", Value::bytes(memory.free().get::<information::byte>())); "total",
Value::bytes(memory.total().get::<information::byte>()),
);
dict.insert(
"free",
Value::bytes(memory.free().get::<information::byte>()),
);
} }
if let Ok(swap) = swap_result { if let Ok(swap) = swap_result {
dict.insert("swap total", Value::bytes(swap.total().get::<information::byte>())); dict.insert(
dict.insert("swap free", Value::bytes(swap.free().get::<information::byte>())); "swap total",
Value::bytes(swap.total().get::<information::byte>()),
);
dict.insert(
"swap free",
Value::bytes(swap.free().get::<information::byte>()),
);
} }
dict.into_tagged_value() dict.into_tagged_value()
@ -69,10 +82,8 @@ async fn mem(tag: Tag) -> Tagged<Value> {
async fn host(tag: Tag) -> Tagged<Value> { async fn host(tag: Tag) -> Tagged<Value> {
let mut dict = TaggedDictBuilder::with_capacity(tag, 6); let mut dict = TaggedDictBuilder::with_capacity(tag, 6);
let (platform_result, uptime_result) = futures::future::join( let (platform_result, uptime_result) =
host::platform(), futures::future::join(host::platform(), host::uptime()).await;
host::uptime(),
).await;
// OS // OS
if let Ok(platform) = platform_result { if let Ok(platform) = platform_result {
@ -133,9 +144,18 @@ async fn disks(tag: Tag) -> Option<Value> {
dict.insert("mount", Value::string(part.mount_point().to_string_lossy())); dict.insert("mount", Value::string(part.mount_point().to_string_lossy()));
if let Ok(usage) = disk::usage(part.mount_point().to_path_buf()).await { if let Ok(usage) = disk::usage(part.mount_point().to_path_buf()).await {
dict.insert("total", Value::bytes(usage.total().get::<information::byte>())); dict.insert(
dict.insert("used", Value::bytes(usage.used().get::<information::byte>())); "total",
dict.insert("free", Value::bytes(usage.free().get::<information::byte>())); Value::bytes(usage.total().get::<information::byte>()),
);
dict.insert(
"used",
Value::bytes(usage.used().get::<information::byte>()),
);
dict.insert(
"free",
Value::bytes(usage.free().get::<information::byte>()),
);
} }
output.push(dict.into_tagged_value()); output.push(dict.into_tagged_value());
@ -169,13 +189,13 @@ async fn battery(tag: Tag) -> Option<Value> {
if let Some(time_to_full) = battery.time_to_full() { if let Some(time_to_full) = battery.time_to_full() {
dict.insert( dict.insert(
"mins to full", "mins to full",
Value::float(time_to_full.get::<battery::units::time::minute>() as f64), Value::number(time_to_full.get::<battery::units::time::minute>()),
); );
} }
if let Some(time_to_empty) = battery.time_to_empty() { if let Some(time_to_empty) = battery.time_to_empty() {
dict.insert( dict.insert(
"mins to empty", "mins to empty",
Value::float(time_to_empty.get::<battery::units::time::minute>() as f64), Value::number(time_to_empty.get::<battery::units::time::minute>()),
); );
} }
output.push(dict.into_tagged_value()); output.push(dict.into_tagged_value());
@ -202,12 +222,25 @@ async fn temp(tag: Tag) -> Option<Value> {
if let Some(label) = sensor.label() { if let Some(label) = sensor.label() {
dict.insert("label", Value::string(label)); dict.insert("label", Value::string(label));
} }
dict.insert("temp", Value::float(sensor.current().get::<thermodynamic_temperature::degree_celsius>() as f64)); dict.insert(
"temp",
Value::number(
sensor
.current()
.get::<thermodynamic_temperature::degree_celsius>(),
),
);
if let Some(high) = sensor.high() { if let Some(high) = sensor.high() {
dict.insert("high", Value::float(high.get::<thermodynamic_temperature::degree_celsius>() as f64)); dict.insert(
"high",
Value::number(high.get::<thermodynamic_temperature::degree_celsius>()),
);
} }
if let Some(critical) = sensor.critical() { if let Some(critical) = sensor.critical() {
dict.insert("critical", Value::float(critical.get::<thermodynamic_temperature::degree_celsius>() as f64)); dict.insert(
"critical",
Value::number(critical.get::<thermodynamic_temperature::degree_celsius>()),
);
} }
output.push(dict.into_tagged_value()); output.push(dict.into_tagged_value());
@ -228,8 +261,14 @@ async fn net(tag: Tag) -> Option<Value> {
if let Ok(nic) = nic { if let Ok(nic) = nic {
let mut network_idx = TaggedDictBuilder::with_capacity(tag, 3); let mut network_idx = TaggedDictBuilder::with_capacity(tag, 3);
network_idx.insert("name", Value::string(nic.interface())); network_idx.insert("name", Value::string(nic.interface()));
network_idx.insert("sent", Value::bytes(nic.bytes_sent().get::<information::byte>())); network_idx.insert(
network_idx.insert("recv", Value::bytes(nic.bytes_recv().get::<information::byte>())); "sent",
Value::bytes(nic.bytes_sent().get::<information::byte>()),
);
network_idx.insert(
"recv",
Value::bytes(nic.bytes_recv().get::<information::byte>()),
);
output.push(network_idx.into_tagged_value()); output.push(network_idx.into_tagged_value());
} }
} }
@ -243,17 +282,9 @@ async fn net(tag: Tag) -> Option<Value> {
async fn sysinfo(tag: Tag) -> Vec<Tagged<Value>> { async fn sysinfo(tag: Tag) -> Vec<Tagged<Value>> {
let mut sysinfo = TaggedDictBuilder::with_capacity(tag, 7); let mut sysinfo = TaggedDictBuilder::with_capacity(tag, 7);
let (host, cpu, disks, memory, temp) = futures::future::join5( let (host, cpu, disks, memory, temp) =
host(tag), futures::future::join5(host(tag), cpu(tag), disks(tag), mem(tag), temp(tag)).await;
cpu(tag), let (net, battery) = futures::future::join(net(tag), battery(tag)).await;
disks(tag),
mem(tag),
temp(tag),
).await;
let (net, battery) = futures::future::join(
net(tag),
battery(tag),
).await;
sysinfo.insert_tagged("host", host); sysinfo.insert_tagged("host", host);
if let Some(cpu) = cpu { if let Some(cpu) = cpu {

View File

@ -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::types::ExtractType;
pub(crate) use crate::object::{Primitive, Value}; pub(crate) use crate::object::{Primitive, Value};
pub(crate) use crate::parser::hir::SyntaxType; 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::parser::registry::Signature;
pub(crate) use crate::shell::filesystem_shell::FilesystemShell; pub(crate) use crate::shell::filesystem_shell::FilesystemShell;
pub(crate) use crate::shell::shell_manager::ShellManager; 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::stream::BoxStream;
pub(crate) use futures::{FutureExt, Stream, StreamExt}; pub(crate) use futures::{FutureExt, Stream, StreamExt};
pub(crate) use futures_async_stream::async_stream_block; 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)] #[allow(unused)]
pub(crate) use serde::{Deserialize, Serialize}; pub(crate) use serde::{Deserialize, Serialize};
pub(crate) use std::collections::VecDeque; pub(crate) use std::collections::VecDeque;

View File

@ -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::Operator(..) => Color::White.normal().paint(token_node.span().slice(line)),
TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.span().slice(line)), TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.span().slice(line)),
TokenNode::Token(Tagged { TokenNode::Token(Tagged {
item: RawToken::Integer(..), item: RawToken::Number(..),
.. ..
}) => Color::Purple.bold().paint(token_node.span().slice(line)), }) => Color::Purple.bold().paint(token_node.span().slice(line)),
TokenNode::Token(Tagged { TokenNode::Token(Tagged {