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)",
"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)",
@ -1626,6 +1626,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)",
@ -1648,6 +1649,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"
@ -1683,6 +1715,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)",
]
@ -1789,7 +1822,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]]
@ -2303,6 +2335,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"
@ -3319,6 +3361,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"
@ -3393,6 +3438,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"

View File

@ -25,14 +25,15 @@ dunce = "1.0.0"
indexmap = { version = "1.0.2", features = ["serde-1"] }
chrono-humanize = "0.0.11"
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-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"

View File

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

View File

@ -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<Tag>) -> Tagged<Value
let tag = tag.into();
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::Array(a) => Value::List(
a.iter()

View File

@ -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<Tag>) -
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) => {

View File

@ -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<Tag> + 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)

View File

@ -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<Tag>) -> 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()

View File

@ -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<Tag>) ->
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(

View File

@ -62,7 +62,7 @@ fn ps(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, She
if let Ok(status) = process.status().await {
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());
}
}

View File

@ -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::*;

View File

@ -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()),

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>> {
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]")),

View File

@ -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))
}

View File

@ -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(),

View File

@ -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<toml::Value, ShellError> {
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("<End of Stream>".to_string())
@ -33,13 +36,15 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value {
Value::Primitive(Primitive::BeginningOfStream) => {
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::Nothing) => toml::Value::String("<Nothing>".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("<Block>".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<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> {
@ -61,7 +76,7 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
Ok(out
.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) => {
return ReturnSuccess::value(
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>> {
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]")),

View File

@ -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))

View File

@ -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)
}
}
}

View File

@ -84,6 +84,17 @@ impl ShellError {
.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 {
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<String>,
},
Diagnostic(ShellDiagnostic),
CoerceError {
left: Tagged<String>,
@ -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<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> {
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)),

View File

@ -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};

View File

@ -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<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)]
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<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 {
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<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 {
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<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<'_> {
ValuesDebug { values }
}
@ -518,7 +535,11 @@ impl Value {
}
#[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 {
_ => {
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<OF64>) -> Value {
Value::Primitive(Primitive::Float(s.into()))
pub fn decimal(s: impl Into<Decimal>) -> Value {
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 {
@ -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,11 @@ 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),
(Decimal(left), Decimal(right)) => CompareValues::Decimals(*left, *right),
(Decimal(left), Int(right)) => CompareValues::Decimals(*left, (*right).into()),
(Int(left), Decimal(right)) => CompareValues::Decimals((*left).into(), *right),
(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()),
_ => 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");
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)?;

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>;
impl Expression {
pub(crate) fn int(i: impl Into<i64>, span: impl Into<Span>) -> Expression {
Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Integer(i.into())), span)
pub(crate) fn number(i: impl Into<Number>, span: impl Into<Span>) -> Expression {
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(
RawExpression::Literal(Literal::Size(i.into(), unit.into())),
span,
@ -224,8 +228,8 @@ impl From<Tagged<Path>> 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",

View File

@ -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),

View File

@ -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(_) => {

View File

@ -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<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;
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<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 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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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) }
}
}

View File

@ -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<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(
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,
))
}
@ -191,7 +206,7 @@ impl TokenTreeBuilder {
}
pub fn spanned_size(
input: (impl Into<i64>, impl Into<Unit>),
input: (impl Into<Number>, impl Into<Unit>),
span: impl Into<Span>,
) -> TokenNode {
let (int, unit) = (input.0.into(), input.1.into());

View File

@ -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",

View File

@ -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,

View File

@ -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<Tagged<Value>> {
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::<frequency::hertz>() 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::<frequency::hertz>() 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::<frequency::hertz>() 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::<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() {
let max_speed = (max_speed.get::<frequency::hertz>() 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::<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())
},
}
Err(_) => None,
}
}
@ -48,19 +51,29 @@ async fn cpu(tag: Tag) -> Option<Tagged<Value>> {
async fn mem(tag: Tag) -> Tagged<Value> {
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::<information::byte>()));
dict.insert("free", Value::bytes(memory.free().get::<information::byte>()));
dict.insert(
"total",
Value::bytes(memory.total().get::<information::byte>()),
);
dict.insert(
"free",
Value::bytes(memory.free().get::<information::byte>()),
);
}
if let Ok(swap) = swap_result {
dict.insert("swap total", Value::bytes(swap.total().get::<information::byte>()));
dict.insert("swap free", Value::bytes(swap.free().get::<information::byte>()));
dict.insert(
"swap total",
Value::bytes(swap.total().get::<information::byte>()),
);
dict.insert(
"swap free",
Value::bytes(swap.free().get::<information::byte>()),
);
}
dict.into_tagged_value()
@ -69,10 +82,8 @@ async fn mem(tag: Tag) -> Tagged<Value> {
async fn host(tag: Tag) -> Tagged<Value> {
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<Value> {
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::<information::byte>()));
dict.insert("used", Value::bytes(usage.used().get::<information::byte>()));
dict.insert("free", Value::bytes(usage.free().get::<information::byte>()));
dict.insert(
"total",
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());
@ -169,13 +189,13 @@ async fn battery(tag: Tag) -> Option<Value> {
if let Some(time_to_full) = battery.time_to_full() {
dict.insert(
"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() {
dict.insert(
"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());
@ -202,12 +222,25 @@ async fn temp(tag: Tag) -> Option<Value> {
if let Some(label) = sensor.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() {
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() {
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());
@ -228,8 +261,14 @@ async fn net(tag: Tag) -> Option<Value> {
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::<information::byte>()));
network_idx.insert("recv", Value::bytes(nic.bytes_recv().get::<information::byte>()));
network_idx.insert(
"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());
}
}
@ -243,17 +282,9 @@ async fn net(tag: Tag) -> Option<Value> {
async fn sysinfo(tag: Tag) -> Vec<Tagged<Value>> {
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 {

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::{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;

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::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 {