From 8a29c9e6ab85c9d3844c31f2eae788e07393fca6 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 1 Sep 2019 09:20:31 -0700 Subject: [PATCH] Migrated numerics to BigInt/BigDecimal This commit migrates Value's numeric types to BigInt and BigDecimal. The basic idea is that overflow errors aren't great in a shell environment, and not really necessary. The main immediate consequence is that new errors can occur when serializing Nu values to other formats. You can see this in changes to the various serialization formats (JSON, TOML, etc.). There's a new `CoerceInto` trait that uses the `ToPrimitive` trait from `num_traits` to attempt to coerce a `BigNum` or `BigDecimal` into a target type, and produces a `RangeError` (kind of `ShellError`) if the coercion fails. Another possible future consequence is that certain performance-critical numeric operations might be too slow. If that happens, we can introduce specialized numeric types to help improve the performance of those situations, based on the real-world experience. --- .gitignore | 3 +- Cargo.lock | 52 +++------ Cargo.toml | 3 +- src/commands/first.rs | 31 ++--- src/commands/from_bson.rs | 55 +++++---- src/commands/from_json.rs | 8 +- src/commands/from_sqlite.rs | 2 +- src/commands/from_toml.rs | 6 +- src/commands/from_yaml.rs | 4 +- src/commands/last.rs | 40 +++---- src/commands/to_bson.rs | 72 +++++++----- src/commands/to_json.rs | 28 +++-- src/commands/to_toml.rs | 15 ++- src/commands/to_yaml.rs | 27 +++-- src/errors.rs | 156 ++++++++++++++++++++++--- src/evaluate/evaluator.rs | 4 +- src/lib.rs | 2 +- src/object/base.rs | 103 ++++++++-------- src/object/config.rs | 3 +- src/object/types.rs | 18 ++- src/parser/hir.rs | 2 +- src/parser/hir/baseline_parse.rs | 12 +- src/parser/parse/parser.rs | 100 ++++++++-------- src/parser/parse/token_tree_builder.rs | 49 ++++---- src/parser/parse/tokens.rs | 34 +++++- src/parser/parse/unit.rs | 4 +- src/plugins/inc.rs | 2 +- src/plugins/skip.rs | 9 +- src/plugins/str.rs | 4 +- src/plugins/sum.rs | 6 +- src/plugins/sys.rs | 2 +- src/prelude.rs | 9 +- 32 files changed, 525 insertions(+), 340 deletions(-) diff --git a/.gitignore b/.gitignore index 6d330b7f8d..2026921b61 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target +/scratch **/*.rs.bk history.txt -tests/fixtures/nuplayground \ No newline at end of file +tests/fixtures/nuplayground diff --git a/Cargo.lock b/Cargo.lock index 97a039f9c7..0c663645ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,6 +111,17 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bigdecimal" +version = "0.1.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-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)", + "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bincode" version = "1.1.4" @@ -1560,6 +1571,7 @@ dependencies = [ "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "battery 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bigdecimal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bson 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "byte-unit 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1592,6 +1604,7 @@ 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-bigint 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)", "onig_sys 69.1.0 (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)", @@ -1604,7 +1617,6 @@ 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.99 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1627,19 +1639,6 @@ 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" @@ -1647,15 +1646,7 @@ 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)", + "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1683,7 +1674,6 @@ 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)", ] @@ -2286,16 +2276,6 @@ 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.99 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rustc-demangle" version = "0.1.15" @@ -3130,6 +3110,7 @@ dependencies = [ "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum battery 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6d6fe5630049e900227cd89afce4c1204b88ec8e61a2581bb96fcce26f047b" +"checksum bigdecimal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "460825c9e21708024d67c07057cd5560e5acdccac85de0de624a81d3de51bacb" "checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum blake2b_simd 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "461f4b879a8eb70c1debf7d0788a9a5ff15f1ea9d25925fea264ef4258bed6b2" @@ -3285,9 +3266,7 @@ 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-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-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" @@ -3360,7 +3339,6 @@ 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-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" diff --git a/Cargo.toml b/Cargo.toml index 49dcbad94e..93e11989ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,6 @@ 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.99", features = ["derive"] } bson = { version = "0.14.0", features = ["decimal128"] } serde_json = "1.0.40" @@ -81,6 +80,8 @@ clipboard = {version = "0.5", optional = true } shellexpand = "1.0.0" futures-timer = "0.3.0" pin-utils = "0.1.0-alpha.4" +num-bigint = { version = "0.2.2", features = ["serde"] } +bigdecimal = { version = "0.1.0", features = ["serde"] } [features] raw-key = ["rawkey", "neso"] diff --git a/src/commands/first.rs b/src/commands/first.rs index af61dc8be5..6381d5def6 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -5,6 +5,11 @@ use crate::prelude::*; pub struct First; +#[derive(Deserialize)] +pub struct FirstArgs { + amount: Tagged, +} + impl WholeStreamCommand for First { fn name(&self) -> &str { "first" @@ -24,27 +29,13 @@ impl WholeStreamCommand for First { args: CommandArgs, registry: &CommandRegistry, ) -> Result { - first(args, registry) + args.process(registry, first)?.run() } } -fn first(args: CommandArgs, registry: &CommandRegistry) -> Result { - let args = args.evaluate_once(registry)?; - - let amount = args.expect_nth(0)?.as_i64(); - - let amount = match amount { - Ok(o) => o, - Err(_) => { - return Err(ShellError::labeled_error( - "Value is not a number", - "expected integer", - args.expect_nth(0)?.span(), - )) - } - }; - - Ok(OutputStream::from_input( - args.input.values.take(amount as u64), - )) +fn first( + FirstArgs { amount }: FirstArgs, + context: RunnableContext, +) -> Result { + Ok(OutputStream::from_input(context.input.values.take(*amount))) } diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index 0d5f05024f..ab7e81994d 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -1,4 +1,5 @@ use crate::commands::WholeStreamCommand; +use crate::errors::ExpectedRange; use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; use bson::{decode_document, spec::BinarySubtype, Bson}; @@ -28,22 +29,30 @@ impl WholeStreamCommand for FromBSON { } } -fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into) -> Tagged { +fn bson_array(input: &Vec, tag: Tag) -> Result>, ShellError> { + let mut out = vec![]; + + for value in input { + out.push(convert_bson_value_to_nu_value(value, tag)?); + } + + Ok(out) +} + +fn convert_bson_value_to_nu_value( + v: &Bson, + tag: impl Into, +) -> Result, ShellError> { let tag = tag.into(); - match v { + Ok(match v { 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() - .map(|x| convert_bson_value_to_nu_value(x, tag)) - .collect(), - ) - .tagged(tag), + Bson::Array(a) => Value::List(bson_array(a, tag)?).tagged(tag), Bson::Document(doc) => { let mut collected = TaggedDictBuilder::new(tag); for (k, v) in doc.iter() { - collected.insert_tagged(k.clone(), convert_bson_value_to_nu_value(v, tag)); + collected.insert_tagged(k.clone(), convert_bson_value_to_nu_value(v, tag)?); } collected.into_tagged_value() @@ -62,11 +71,18 @@ fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into) -> Tagged Value::Primitive(Primitive::Int(*n as i64)).tagged(tag), - Bson::I64(n) => Value::Primitive(Primitive::Int(*n as i64)).tagged(tag), + Bson::I32(n) => Value::number(n).tagged(tag), + Bson::I64(n) => Value::number(n).tagged(tag), Bson::Decimal128(n) => { - let decimal = Decimal::from_str(&format!("{}", n)).unwrap(); + // TODO: this really isn't great, and we should update this to do a higher + // fidelity translation + let decimal = BigDecimal::from_str(&format!("{}", n)).map_err(|_| { + ShellError::range_error( + ExpectedRange::BigDecimal, + &n.tagged(tag), + format!("converting BSON Decimal128 to BigDecimal"), + ) + })?; Value::Primitive(Primitive::Decimal(decimal)).tagged(tag) } Bson::JavaScriptCode(js) => { @@ -85,16 +101,13 @@ fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into) -> Tagged { let mut collected = TaggedDictBuilder::new(tag); - collected.insert_tagged( - "$timestamp".to_string(), - Value::Primitive(Primitive::Int(*ts as i64)).tagged(tag), - ); + collected.insert_tagged("$timestamp".to_string(), Value::number(ts).tagged(tag)); collected.into_tagged_value() } Bson::Binary(bst, bytes) => { @@ -102,7 +115,7 @@ fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into) -> Tagged Value::Primitive(Primitive::Int(*u as i64)), + BinarySubtype::UserDefined(u) => Value::number(u), _ => Value::Primitive(Primitive::String(binary_subtype_to_string(*bst))), } .tagged(tag), @@ -130,7 +143,7 @@ fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into) -> Tagged String { @@ -179,7 +192,7 @@ pub fn from_bson_bytes_to_value( while let Ok(v) = decode_document(&mut b_reader) { docs.push(Bson::Document(v)); } - Ok(convert_bson_value_to_nu_value(&Bson::Array(docs), tag)) + Ok(convert_bson_value_to_nu_value(&Bson::Array(docs), tag).expect("FIXME: Don't commit like this")) } fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/commands/from_json.rs b/src/commands/from_json.rs index e902d0a3de..f932e35793 100644 --- a/src/commands/from_json.rs +++ b/src/commands/from_json.rs @@ -37,10 +37,10 @@ fn convert_json_value_to_nu_value(v: &serde_hjson::Value, tag: impl Into) - match v { serde_hjson::Value::Null => Value::Primitive(Primitive::Nothing).tagged(tag), - serde_hjson::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(tag), - serde_hjson::Value::F64(n) => Value::Primitive(Primitive::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::Bool(b) => Value::boolean(*b).tagged(tag), + serde_hjson::Value::F64(n) => Value::number(n).tagged(tag), + serde_hjson::Value::U64(n) => Value::number(n).tagged(tag), + serde_hjson::Value::I64(n) => Value::number(n).tagged(tag), serde_hjson::Value::String(s) => { Value::Primitive(Primitive::String(String::from(s))).tagged(tag) } diff --git a/src/commands/from_sqlite.rs b/src/commands/from_sqlite.rs index 88bd30375e..9aca8a222e 100644 --- a/src/commands/from_sqlite.rs +++ b/src/commands/from_sqlite.rs @@ -100,7 +100,7 @@ fn convert_sqlite_row_to_nu_value( fn convert_sqlite_value_to_nu_value(value: ValueRef, tag: impl Into + Clone) -> Tagged { match value { ValueRef::Null => Value::Primitive(Primitive::String(String::from(""))).tagged(tag), - ValueRef::Integer(i) => Value::Primitive(Primitive::Int(i)).tagged(tag), + ValueRef::Integer(i) => Value::number(i).tagged(tag), ValueRef::Real(f) => Value::number(f).tagged(tag), t @ ValueRef::Text(_) => { // this unwrap is safe because we know the ValueRef is Text. diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index b4b341ecaa..3d1d92fb67 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -30,9 +30,9 @@ pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into) -> T let tag = tag.into(); 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::from(*n)).tagged(tag), + toml::Value::Boolean(b) => Value::boolean(*b).tagged(tag), + toml::Value::Integer(n) => Value::number(n).tagged(tag), + toml::Value::Float(n) => Value::number(n).tagged(tag), toml::Value::String(s) => Value::Primitive(Primitive::String(String::from(s))).tagged(tag), toml::Value::Array(a) => Value::List( a.iter() diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index e67c045038..3bac9d3f81 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -54,9 +54,9 @@ fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, tag: impl Into) -> let tag = tag.into(); match v { - serde_yaml::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(tag), + serde_yaml::Value::Bool(b) => Value::boolean(*b).tagged(tag), serde_yaml::Value::Number(n) if n.is_i64() => { - Value::Primitive(Primitive::Int(n.as_i64().unwrap())).tagged(tag) + Value::number(n.as_i64().unwrap()).tagged(tag) } serde_yaml::Value::Number(n) if n.is_f64() => { Value::Primitive(Primitive::from(n.as_f64().unwrap())).tagged(tag) diff --git a/src/commands/last.rs b/src/commands/last.rs index 29fe773775..2471eec968 100644 --- a/src/commands/last.rs +++ b/src/commands/last.rs @@ -5,6 +5,11 @@ use crate::prelude::*; pub struct Last; +#[derive(Deserialize)] +pub struct LastArgs { + amount: Tagged, +} + impl WholeStreamCommand for Last { fn name(&self) -> &str { "last" @@ -24,37 +29,18 @@ impl WholeStreamCommand for Last { args: CommandArgs, registry: &CommandRegistry, ) -> Result { - last(args, registry) + args.process(registry, last)?.run() + // last(args, registry) } } -fn last(args: CommandArgs, registry: &CommandRegistry) -> Result { - let args = args.evaluate_once(registry)?; - - let amount = args.expect_nth(0)?.as_i64(); - - let amount = match amount { - Ok(o) => o, - Err(_) => { - return Err(ShellError::labeled_error( - "Value is not a number", - "expected integer", - args.expect_nth(0)?.span(), - )) - } - }; - - if amount <= 0 { - return Err(ShellError::labeled_error( - "Value is too low", - "expected a positive integer", - args.expect_nth(0)?.span(), - )); - } - +fn last( + LastArgs { amount }: LastArgs, + context: RunnableContext, +) -> Result { let stream = async_stream_block! { - let v: Vec<_> = args.input.into_vec().await; - let k = v.len() - (amount as usize); + let v: Vec<_> = context.input.into_vec().await; + let k = v.len() - (*amount as usize); for x in v[k..].iter() { let y: Tagged = x.clone(); yield ReturnSuccess::value(y) diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs index 64ba9aa4af..35fc9839d4 100644 --- a/src/commands/to_bson.rs +++ b/src/commands/to_bson.rs @@ -28,12 +28,12 @@ impl WholeStreamCommand for ToBSON { } } -pub fn value_to_bson_value(v: &Value) -> Bson { - match v { +pub fn value_to_bson_value(v: &Tagged) -> Result { + Ok(match &v.item { Value::Primitive(Primitive::Boolean(b)) => Bson::Boolean(*b), // FIXME: What about really big decimals? Value::Primitive(Primitive::Bytes(decimal)) => Bson::FloatingPoint( - (*decimal) + (decimal) .to_f64() .expect("Unimplemented BUG: What about big decimals?"), ), @@ -41,20 +41,26 @@ pub fn value_to_bson_value(v: &Value) -> Bson { Value::Primitive(Primitive::EndOfStream) => Bson::Null, Value::Primitive(Primitive::BeginningOfStream) => Bson::Null, 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.tagged(v.tag).coerce_into("converting to BSON")?) + } Value::Primitive(Primitive::Nothing) => Bson::Null, Value::Primitive(Primitive::String(s)) => Bson::String(s.clone()), Value::Primitive(Primitive::Path(s)) => Bson::String(s.display().to_string()), - Value::List(l) => Bson::Array(l.iter().map(|x| value_to_bson_value(x)).collect()), + Value::List(l) => Bson::Array( + l.iter() + .map(|x| value_to_bson_value(x)) + .collect::>()?, + ), Value::Block(_) => Bson::Null, Value::Binary(b) => Bson::Binary(BinarySubtype::Generic, b.clone()), - Value::Object(o) => object_value_to_bson(o), - } + Value::Object(o) => object_value_to_bson(o)?, + }) } // object_value_to_bson handles all Objects, even those that correspond to special // types (things like regex or javascript code). -fn object_value_to_bson(o: &Dictionary) -> Bson { +fn object_value_to_bson(o: &Dictionary) -> Result { let mut it = o.entries.iter(); if it.len() > 2 { return generic_object_value_to_bson(o); @@ -67,7 +73,7 @@ fn object_value_to_bson(o: &Dictionary) -> Bson { if r.is_err() || opts.is_err() { generic_object_value_to_bson(o) } else { - Bson::RegExp(r.unwrap(), opts.unwrap()) + Ok(Bson::RegExp(r.unwrap(), opts.unwrap())) } } _ => generic_object_value_to_bson(o), @@ -80,8 +86,8 @@ fn object_value_to_bson(o: &Dictionary) -> Bson { if js.is_err() || s.is_err() { generic_object_value_to_bson(o) } else { - if let Bson::Document(doc) = object_value_to_bson(s.unwrap()) { - Bson::JavaScriptCodeWithScope(js.unwrap(), doc) + if let Bson::Document(doc) = object_value_to_bson(s.unwrap())? { + Ok(Bson::JavaScriptCodeWithScope(js.unwrap(), doc)) } else { generic_object_value_to_bson(o) } @@ -89,10 +95,10 @@ fn object_value_to_bson(o: &Dictionary) -> Bson { } None => { let js: Result = tagged_javascript_value.try_into(); - if js.is_err() { - generic_object_value_to_bson(o) - } else { - Bson::JavaScriptCode(js.unwrap()) + + match js { + Err(_) => generic_object_value_to_bson(o), + Ok(v) => Ok(Bson::JavaScriptCode(v)), } } _ => generic_object_value_to_bson(o), @@ -103,7 +109,7 @@ fn object_value_to_bson(o: &Dictionary) -> Bson { if ts.is_err() { generic_object_value_to_bson(o) } else { - Bson::TimeStamp(ts.unwrap()) + Ok(Bson::TimeStamp(ts.unwrap())) } } Some((binary_subtype, tagged_binary_subtype_value)) @@ -113,10 +119,10 @@ fn object_value_to_bson(o: &Dictionary) -> Bson { Some((binary, tagged_bin_value)) if binary == "$binary" => { let bst = get_binary_subtype(tagged_binary_subtype_value); let bin: Result, _> = tagged_bin_value.try_into(); - if bst.is_none() || bin.is_err() { - generic_object_value_to_bson(o) - } else { - Bson::Binary(bst.unwrap(), bin.unwrap()) + + match bst { + Err(_) => generic_object_value_to_bson(o), + Ok(v) => Ok(Bson::Binary(v, bin.unwrap())), } } _ => generic_object_value_to_bson(o), @@ -131,7 +137,7 @@ fn object_value_to_bson(o: &Dictionary) -> Bson { if obj_id.is_err() { generic_object_value_to_bson(o) } else { - Bson::ObjectId(obj_id.unwrap()) + Ok(Bson::ObjectId(obj_id.unwrap())) } } } @@ -140,16 +146,16 @@ fn object_value_to_bson(o: &Dictionary) -> Bson { if sym.is_err() { generic_object_value_to_bson(o) } else { - Bson::Symbol(sym.unwrap()) + Ok(Bson::Symbol(sym.unwrap())) } } _ => generic_object_value_to_bson(o), } } -fn get_binary_subtype<'a>(tagged_value: &'a Tagged) -> Option { +fn get_binary_subtype<'a>(tagged_value: &'a Tagged) -> Result { match tagged_value.item() { - Value::Primitive(Primitive::String(s)) => Some(match s.as_ref() { + Value::Primitive(Primitive::String(s)) => Ok(match s.as_ref() { "generic" => BinarySubtype::Generic, "function" => BinarySubtype::Function, "binary_old" => BinarySubtype::BinaryOld, @@ -158,19 +164,25 @@ fn get_binary_subtype<'a>(tagged_value: &'a Tagged) -> Option BinarySubtype::Md5, _ => unreachable!(), }), - Value::Primitive(Primitive::Int(i)) => Some(BinarySubtype::UserDefined(*i as u8)), - _ => None, + Value::Primitive(Primitive::Int(i)) => Ok(BinarySubtype::UserDefined( + i.tagged(tagged_value.tag) + .coerce_into("converting to BSON binary subtype")?, + )), + _ => Err(ShellError::type_error( + "bson binary", + tagged_value.tagged_type_name(), + )), } } // generic_object_value_bson handles any Object that does not // correspond to a special bson type (things like regex or javascript code). -fn generic_object_value_to_bson(o: &Dictionary) -> Bson { +fn generic_object_value_to_bson(o: &Dictionary) -> Result { let mut doc = Document::new(); for (k, v) in o.entries.iter() { - doc.insert(k.clone(), value_to_bson_value(v)); + doc.insert(k.clone(), value_to_bson_value(v)?); } - Bson::Document(doc) + Ok(Bson::Document(doc)) } fn shell_encode_document( @@ -225,7 +237,7 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result ReturnSuccess::value(Value::Binary(x).simple_spanned(name_span)), _ => Err(ShellError::labeled_error_with_secondary( "Expected an object with BSON-compatible structure from pipeline", diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index a1c0e22987..9849e691e3 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -26,8 +26,8 @@ impl WholeStreamCommand for ToJSON { } } -pub fn value_to_json_value(v: &Value) -> serde_json::Value { - match v { +pub fn value_to_json_value(v: &Tagged) -> Result { + Ok(match v.item() { Value::Primitive(Primitive::Boolean(b)) => serde_json::Value::Bool(*b), Value::Primitive(Primitive::Bytes(b)) => serde_json::Value::Number( serde_json::Number::from(b.to_u64().expect("What about really big numbers")), @@ -41,16 +41,14 @@ pub fn value_to_json_value(v: &Value) -> serde_json::Value { ) .unwrap(), ), - Value::Primitive(Primitive::Int(i)) => { - serde_json::Value::Number(serde_json::Number::from(*i)) - } + Value::Primitive(Primitive::Int(i)) => serde_json::Value::Number(serde_json::Number::from( + CoerceInto::::coerce_into(i.tagged(v.tag), "converting to JSON number")?, + )), Value::Primitive(Primitive::Nothing) => serde_json::Value::Null, Value::Primitive(Primitive::String(s)) => serde_json::Value::String(s.clone()), Value::Primitive(Primitive::Path(s)) => serde_json::Value::String(s.display().to_string()), - Value::List(l) => { - serde_json::Value::Array(l.iter().map(|x| value_to_json_value(x)).collect()) - } + Value::List(l) => serde_json::Value::Array(json_list(l)?), Value::Block(_) => serde_json::Value::Null, Value::Binary(b) => serde_json::Value::Array( b.iter() @@ -62,11 +60,21 @@ pub fn value_to_json_value(v: &Value) -> serde_json::Value { Value::Object(o) => { let mut m = serde_json::Map::new(); for (k, v) in o.entries.iter() { - m.insert(k.clone(), value_to_json_value(v)); + m.insert(k.clone(), value_to_json_value(v)?); } serde_json::Value::Object(m) } + }) +} + +fn json_list(input: &Vec>) -> Result, ShellError> { + let mut out = vec![]; + + for value in input { + out.push(value_to_json_value(value)?); } + + Ok(out) } fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result { @@ -77,7 +85,7 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result ReturnSuccess::value( Value::Primitive(Primitive::String(x)).simple_spanned(name_span), ), diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index d8216f9004..8c44e21b2c 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -1,5 +1,4 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ranged; use crate::object::{Primitive, Value}; use crate::prelude::*; @@ -27,12 +26,10 @@ impl WholeStreamCommand for ToTOML { } } -pub fn value_to_toml_value(v: &Value) -> Result { - Ok(match v { +pub fn value_to_toml_value(v: &Tagged) -> Result { + Ok(match v.item() { Value::Primitive(Primitive::Boolean(b)) => toml::Value::Boolean(*b), - Value::Primitive(Primitive::Bytes(b)) => { - toml::Value::Integer(ranged(b.to_i64(), "i64", b.tagged_unknown())?) - } + Value::Primitive(Primitive::Bytes(b)) => toml::Value::Integer(*b as i64), Value::Primitive(Primitive::Date(d)) => toml::Value::String(d.to_string()), Value::Primitive(Primitive::EndOfStream) => { toml::Value::String("".to_string()) @@ -41,9 +38,11 @@ pub fn value_to_toml_value(v: &Value) -> Result { toml::Value::String("".to_string()) } Value::Primitive(Primitive::Decimal(f)) => { - toml::Value::Float(ranged(f.to_f64(), "f64", f.tagged_unknown())?) + toml::Value::Float(f.tagged(v.tag).coerce_into("converting to TOML float")?) + } + Value::Primitive(Primitive::Int(i)) => { + toml::Value::Integer(i.tagged(v.tag).coerce_into("converting to TOML integer")?) } - Value::Primitive(Primitive::Int(i)) => toml::Value::Integer(*i), Value::Primitive(Primitive::Nothing) => toml::Value::String("".to_string()), Value::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()), Value::Primitive(Primitive::Path(s)) => toml::Value::String(s.display().to_string()), diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index 9b91cbc0cc..8ef6e90da2 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -26,8 +26,8 @@ impl WholeStreamCommand for ToYAML { } } -pub fn value_to_yaml_value(v: &Value) -> serde_yaml::Value { - match v { +pub fn value_to_yaml_value(v: &Tagged) -> Result { + Ok(match v.item() { Value::Primitive(Primitive::Boolean(b)) => serde_yaml::Value::Bool(*b), Value::Primitive(Primitive::Bytes(b)) => { serde_yaml::Value::Number(serde_yaml::Number::from(b.to_f64().unwrap())) @@ -38,15 +38,21 @@ pub fn value_to_yaml_value(v: &Value) -> serde_yaml::Value { 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)) - } + Value::Primitive(Primitive::Int(i)) => serde_yaml::Value::Number(serde_yaml::Number::from( + CoerceInto::::coerce_into(i.tagged(v.tag), "converting to YAML number")?, + )), Value::Primitive(Primitive::Nothing) => serde_yaml::Value::Null, Value::Primitive(Primitive::String(s)) => serde_yaml::Value::String(s.clone()), Value::Primitive(Primitive::Path(s)) => serde_yaml::Value::String(s.display().to_string()), Value::List(l) => { - serde_yaml::Value::Sequence(l.iter().map(|x| value_to_yaml_value(x)).collect()) + let mut out = vec![]; + + for value in l { + out.push(value_to_yaml_value(value)?); + } + + serde_yaml::Value::Sequence(out) } Value::Block(_) => serde_yaml::Value::Null, Value::Binary(b) => serde_yaml::Value::Sequence( @@ -57,11 +63,14 @@ pub fn value_to_yaml_value(v: &Value) -> serde_yaml::Value { Value::Object(o) => { let mut m = serde_yaml::Mapping::new(); for (k, v) in o.entries.iter() { - m.insert(serde_yaml::Value::String(k.clone()), value_to_yaml_value(v)); + m.insert( + serde_yaml::Value::String(k.clone()), + value_to_yaml_value(v)?, + ); } serde_yaml::Value::Mapping(m) } - } + }) } fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result { @@ -71,7 +80,7 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result ReturnSuccess::value( Value::Primitive(Primitive::String(x)).simple_spanned(name_span), ), diff --git a/src/errors.rs b/src/errors.rs index 3bf0538be5..c936413b75 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -85,12 +85,14 @@ impl ShellError { } pub(crate) fn range_error( - expected: impl Into, - actual: Tagged, + expected: impl Into, + actual: &Tagged, + operation: String, ) -> ShellError { ProximateShellError::RangeError { kind: expected.into(), - actual_kind: actual.map(|a| format!("{:?}", a)), + actual_kind: actual.copy_span(format!("{:?}", actual.item)), + operation, } .start() } @@ -255,6 +257,7 @@ impl ShellError { ProximateShellError::RangeError { kind, + operation, actual_kind: Tagged { item, @@ -262,8 +265,10 @@ impl ShellError { }, } => 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 + "Expected to convert {} to {} while {}, but it was out of range", + item, + kind.desc(), + operation )), ), @@ -343,6 +348,45 @@ impl ShellError { } } +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] +pub enum ExpectedRange { + I8, + I16, + I32, + I64, + I128, + U8, + U16, + U32, + U64, + U128, + F32, + F64, + BigInt, + BigDecimal, +} + +impl ExpectedRange { + fn desc(&self) -> &'static str { + match self { + ExpectedRange::I8 => "an 8-bit signed integer", + ExpectedRange::I16 => "a 16-bit signed integer", + ExpectedRange::I32 => "a 32-bit signed integer", + ExpectedRange::I64 => "a 64-bit signed integer", + ExpectedRange::I128 => "a 128-bit signed integer", + ExpectedRange::U8 => "an 8-bit unsigned integer", + ExpectedRange::U16 => "a 16-bit unsigned integer", + ExpectedRange::U32 => "a 32-bit unsigned integer", + ExpectedRange::U64 => "a 64-bit unsigned integer", + ExpectedRange::U128 => "a 128-bit unsigned integer", + ExpectedRange::F32 => "a 32-bit float", + ExpectedRange::F64 => "a 64-bit float", + ExpectedRange::BigDecimal => "a decimal", + ExpectedRange::BigInt => "an integer", + } + } +} + #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] pub enum ProximateShellError { String(StringError), @@ -370,8 +414,9 @@ pub enum ProximateShellError { span: Span, }, RangeError { - kind: String, + kind: ExpectedRange, actual_kind: Tagged, + operation: String, }, Diagnostic(ShellDiagnostic), CoerceError { @@ -557,13 +602,94 @@ impl ShellErrorUtils> for Option> { } } -pub fn ranged( - input: Option, - expected: impl Into, - actual: Tagged, -) -> Result { - match input { - Some(v) => Ok(v), - None => Err(ShellError::range_error(expected, actual)), - } +pub trait CoerceInto { + fn coerce_into(self, operation: impl Into) -> Result; } + +trait ToExpectedRange { + fn to_expected_range() -> ExpectedRange; +} + +macro_rules! ranged_int { + ($ty:tt -> $op:tt -> $variant:tt) => { + impl ToExpectedRange for $ty { + fn to_expected_range() -> ExpectedRange { + ExpectedRange::$variant + } + } + + impl CoerceInto<$ty> for Tagged { + fn coerce_into(self, operation: impl Into) -> Result<$ty, ShellError> { + match self.$op() { + Some(v) => Ok(v), + None => Err(ShellError::range_error( + $ty::to_expected_range(), + &self, + operation.into(), + )), + } + } + } + + impl CoerceInto<$ty> for Tagged<&BigInt> { + fn coerce_into(self, operation: impl Into) -> Result<$ty, ShellError> { + match self.$op() { + Some(v) => Ok(v), + None => Err(ShellError::range_error( + $ty::to_expected_range(), + &self, + operation.into(), + )), + } + } + } + }; +} + +ranged_int!(u8 -> to_u8 -> U8); +ranged_int!(u16 -> to_u16 -> U16); +ranged_int!(u32 -> to_u32 -> U32); +ranged_int!(u64 -> to_u64 -> U64); +ranged_int!(i8 -> to_i8 -> I8); +ranged_int!(i16 -> to_i16 -> I16); +ranged_int!(i32 -> to_i32 -> I32); +ranged_int!(i64 -> to_i64 -> I64); + +macro_rules! ranged_decimal { + ($ty:tt -> $op:tt -> $variant:tt) => { + impl ToExpectedRange for $ty { + fn to_expected_range() -> ExpectedRange { + ExpectedRange::$variant + } + } + + impl CoerceInto<$ty> for Tagged { + fn coerce_into(self, operation: impl Into) -> Result<$ty, ShellError> { + match self.$op() { + Some(v) => Ok(v), + None => Err(ShellError::range_error( + $ty::to_expected_range(), + &self, + operation.into(), + )), + } + } + } + + impl CoerceInto<$ty> for Tagged<&BigDecimal> { + fn coerce_into(self, operation: impl Into) -> Result<$ty, ShellError> { + match self.$op() { + Some(v) => Ok(v), + None => Err(ShellError::range_error( + $ty::to_expected_range(), + &self, + operation.into(), + )), + } + } + } + }; +} + +ranged_decimal!(f32 -> to_f32 -> F32); +ranged_decimal!(f64 -> to_f64 -> F64); diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 511625b368..0bf31b5e71 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -38,7 +38,7 @@ pub(crate) fn evaluate_baseline_expr( source: &Text, ) -> Result, ShellError> { match &expr.item { - RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(*literal), source)), + RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(literal), source)), RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.span())), RawExpression::Synthetic(hir::Synthetic::String(s)) => Ok(Value::string(s).tagged_unknown()), RawExpression::Variable(var) => evaluate_reference(var, scope, source), @@ -104,7 +104,7 @@ pub(crate) fn evaluate_baseline_expr( } } -fn evaluate_literal(literal: Tagged, source: &Text) -> Tagged { +fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged { let result = match literal.item { hir::Literal::Number(int) => int.into(), hir::Literal::Size(int, unit) => unit.compute(int), diff --git a/src/lib.rs b/src/lib.rs index e1a068cde8..af2eb4561c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,7 @@ 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 errors::{CoerceInto, ShellError}; pub use num_traits::cast::ToPrimitive; pub use object::base::{Primitive, Value}; pub use object::dict::{Dictionary, TaggedDictBuilder}; diff --git a/src/object/base.rs b/src/object/base.rs index fc5a5efaad..0d4709f1f1 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -16,8 +16,8 @@ use std::time::SystemTime; #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize, Serialize)] pub enum Primitive { Nothing, - Int(i64), - Decimal(Decimal), + Int(BigInt), + Decimal(BigDecimal), Bytes(u64), String(String), Boolean(bool), @@ -29,21 +29,15 @@ pub enum Primitive { EndOfStream, } -impl From for Primitive { - fn from(int: i64) -> Primitive { - Primitive::Int(int) - } -} - -impl From for Primitive { - fn from(decimal: Decimal) -> Primitive { +impl From for Primitive { + fn from(decimal: BigDecimal) -> Primitive { Primitive::Decimal(decimal) } } impl From for Primitive { fn from(float: f64) -> Primitive { - Primitive::Decimal(Decimal::from_f64(float).unwrap()) + Primitive::Decimal(BigDecimal::from_f64(float).unwrap()) } } @@ -193,6 +187,15 @@ impl Into for Number { } } +impl Into for &Number { + fn into(self) -> Value { + match self { + Number::Int(int) => Value::int(int.clone()), + Number::Decimal(decimal) => Value::decimal(decimal.clone()), + } + } +} + pub fn debug_list(values: &Vec>) -> ValuesDebug<'_> { ValuesDebug { values } } @@ -251,7 +254,9 @@ impl std::convert::TryFrom<&Tagged> for i64 { fn try_from(value: &Tagged) -> Result { match value.item() { - Value::Primitive(Primitive::Int(int)) => Ok(*int), + Value::Primitive(Primitive::Int(int)) => { + int.tagged(value.tag).coerce_into("converting to i64") + } v => Err(ShellError::type_error( "Integer", value.copy_span(v.type_name()), @@ -598,18 +603,6 @@ impl Value { } } - pub(crate) fn as_i64(&self) -> Result { - match self { - Value::Primitive(Primitive::Int(i)) => Ok(*i), - Value::Primitive(Primitive::Bytes(b)) => Ok(*b as i64), - // TODO: this should definitely be more general with better errors - other => Err(ShellError::string(format!( - "Expected integer, got {:?}", - other - ))), - } - } - pub(crate) fn is_true(&self) -> bool { match self { Value::Primitive(Primitive::Boolean(true)) => true, @@ -629,11 +622,11 @@ impl Value { Value::Primitive(Primitive::Bytes(s.into())) } - pub fn int(s: impl Into) -> Value { + pub fn int(s: impl Into) -> Value { Value::Primitive(Primitive::Int(s.into())) } - pub fn decimal(s: impl Into) -> Value { + pub fn decimal(s: impl Into) -> Value { Value::Primitive(Primitive::Decimal(s.into())) } @@ -727,18 +720,24 @@ pub(crate) fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool _ => false, }, Value::Primitive(Primitive::Bytes(i)) => match (op, rhs) { - (Operator::LessThan, Value::Primitive(Primitive::Int(i2))) => i < (*i2 as u64), + (Operator::LessThan, Value::Primitive(Primitive::Int(i2))) => { + BigInt::from(i) < *i2 + } (Operator::GreaterThan, Value::Primitive(Primitive::Int(i2))) => { - i > (*i2 as u64) + BigInt::from(i) > *i2 } (Operator::LessThanOrEqual, Value::Primitive(Primitive::Int(i2))) => { - i <= (*i2 as u64) + BigInt::from(i) <= *i2 } (Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Int(i2))) => { - i >= (*i2 as u64) + BigInt::from(i) >= *i2 + } + (Operator::Equal, Value::Primitive(Primitive::Int(i2))) => { + BigInt::from(i) == *i2 + } + (Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => { + BigInt::from(i) != *i2 } - (Operator::Equal, Value::Primitive(Primitive::Int(i2))) => i == (*i2 as u64), - (Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => i != (*i2 as u64), _ => false, }, Value::Primitive(Primitive::Int(i)) => match (op, rhs) { @@ -764,22 +763,22 @@ pub(crate) fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool (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 < Decimal::from(*i2) + i < BigDecimal::from(i2.clone()) } (Operator::GreaterThan, Value::Primitive(Primitive::Int(i2))) => { - i > Decimal::from(*i2) + i > BigDecimal::from(i2.clone()) } (Operator::LessThanOrEqual, Value::Primitive(Primitive::Int(i2))) => { - i <= Decimal::from(*i2) + i <= BigDecimal::from(i2.clone()) } (Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Int(i2))) => { - i >= Decimal::from(*i2) + i >= BigDecimal::from(i2.clone()) } (Operator::Equal, Value::Primitive(Primitive::Int(i2))) => { - i == Decimal::from(*i2) + i == BigDecimal::from(i2.clone()) } (Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => { - i != Decimal::from(*i2) + i != BigDecimal::from(i2.clone()) } _ => false, @@ -796,9 +795,8 @@ pub(crate) fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool } enum CompareValues { - Ints(i64, i64), - Decimals(Decimal, Decimal), - Bytes(u64, u64), + Ints(BigInt, BigInt), + Decimals(BigDecimal, BigDecimal), String(String, String), } @@ -807,7 +805,6 @@ impl CompareValues { match self { CompareValues::Ints(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), } } @@ -828,17 +825,21 @@ fn coerce_compare_primitive( use Primitive::*; Ok(match (left, right) { - (Int(left), Int(right)) => CompareValues::Ints(*left, *right), - (Int(left), Decimal(right)) => CompareValues::Decimals((*left).into(), *right), - (Int(left), Bytes(right)) => CompareValues::Bytes(*left as u64, *right), - (Decimal(left), Decimal(right)) => CompareValues::Decimals(*left, *right), - (Decimal(left), Int(right)) => CompareValues::Decimals(*left, (*right).into()), - (Decimal(left), Bytes(right)) => { - CompareValues::Decimals(*left, rust_decimal::Decimal::from(*right)) + (Int(left), Int(right)) => CompareValues::Ints(left.clone(), right.clone()), + (Int(left), Decimal(right)) => { + CompareValues::Decimals(BigDecimal::zero() + left, right.clone()) } - (Bytes(left), Int(right)) => CompareValues::Bytes(*left, *right as u64), + (Int(left), Bytes(right)) => CompareValues::Ints(left.clone(), BigInt::from(*right)), + (Decimal(left), Decimal(right)) => CompareValues::Decimals(left.clone(), right.clone()), + (Decimal(left), Int(right)) => { + CompareValues::Decimals(left.clone(), BigDecimal::zero() + right) + } + (Decimal(left), Bytes(right)) => { + CompareValues::Decimals(left.clone(), BigDecimal::from(*right)) + } + (Bytes(left), Int(right)) => CompareValues::Ints(BigInt::from(*left), right.clone()), (Bytes(left), Decimal(right)) => { - CompareValues::Decimals(rust_decimal::Decimal::from(*left), *right) + CompareValues::Decimals(BigDecimal::from(*left), right.clone()) } (String(left), String(right)) => CompareValues::String(left.clone(), right.clone()), _ => return Err((left.type_name(), right.type_name())), diff --git a/src/object/config.rs b/src/object/config.rs index fae3155aec..53a1c446f8 100644 --- a/src/object/config.rs +++ b/src/object/config.rs @@ -36,7 +36,8 @@ pub(crate) fn write_config(config: &IndexMap>) -> Result<( let filename = location.join("config.toml"); touch(&filename)?; - let contents = value_to_toml_value(&Value::Object(Dictionary::new(config.clone())))?; + let contents = + value_to_toml_value(&Value::Object(Dictionary::new(config.clone())).tagged_unknown())?; let contents = toml::to_string(&contents)?; diff --git a/src/object/types.rs b/src/object/types.rs index b98e4b5494..74ea03ac67 100644 --- a/src/object/types.rs +++ b/src/object/types.rs @@ -135,11 +135,25 @@ impl ExtractType for i64 { fn extract(value: &Tagged) -> Result { trace!("Extracting {:?} for i64", value); - match value { + match &value { &Tagged { item: Value::Primitive(Primitive::Int(int)), .. - } => Ok(int), + } => Ok(int.tagged(value.tag).coerce_into("converting to i64")?), + other => Err(ShellError::type_error("Integer", other.tagged_type_name())), + } + } +} + +impl ExtractType for u64 { + fn extract(value: &Tagged) -> Result { + trace!("Extracting {:?} for u64", value); + + match &value { + &Tagged { + item: Value::Primitive(Primitive::Int(int)), + .. + } => Ok(int.tagged(value.tag).coerce_into("converting to u64")?), other => Err(ShellError::type_error("Integer", other.tagged_type_name())), } } diff --git a/src/parser/hir.rs b/src/parser/hir.rs index 3f0ab2ce47..f60a5b1e50 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -226,7 +226,7 @@ impl From> for Expression { } } -#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum Literal { Number(Number), Size(Number, Unit), diff --git a/src/parser/hir/baseline_parse.rs b/src/parser/hir/baseline_parse.rs index 5b4e3878ea..d76a88d510 100644 --- a/src/parser/hir/baseline_parse.rs +++ b/src/parser/hir/baseline_parse.rs @@ -5,8 +5,10 @@ use std::path::PathBuf; pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Expression { match *token.item() { - RawToken::Number(number) => hir::Expression::number(number, token.span()), - RawToken::Size(int, unit) => hir::Expression::size(int, unit, token.span()), + RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()), + RawToken::Size(int, unit) => { + hir::Expression::size(int.to_number(source), unit, token.span()) + } RawToken::String(span) => hir::Expression::string(span, token.span()), RawToken::Variable(span) if span.slice(source) == "it" => { hir::Expression::it_variable(span, token.span()) @@ -24,8 +26,10 @@ 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::Number(number) => hir::Expression::number(number, token.span()), - RawToken::Size(number, unit) => hir::Expression::size(number, unit, token.span()), + RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()), + RawToken::Size(number, unit) => { + hir::Expression::size(number.to_number(source), unit, token.span()) + } RawToken::Bare => hir::Expression::bare(token.span()), RawToken::String(span) => hir::Expression::string(span, token.span()), } diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 2597ded60c..2afe29a745 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -70,20 +70,60 @@ fn trace_step<'a, T: Debug>( } } -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)] +#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)] pub enum Number { - Int(i64), - Decimal(Decimal), + Int(BigInt), + Decimal(BigDecimal), } +macro_rules! primitive_int { + ($($ty:ty)*) => { + $( + impl From<$ty> for Number { + fn from(int: $ty) -> Number { + Number::Int(BigInt::zero() + int) + } + } + + impl From<&$ty> for Number { + fn from(int: &$ty) -> Number { + Number::Int(BigInt::zero() + *int) + } + } + )* + } +} + +primitive_int!(i8 u8 i16 u16 i32 u32 i64 u64 i128 u128); + +macro_rules! primitive_decimal { + ($($ty:tt -> $from:tt),*) => { + $( + impl From<$ty> for Number { + fn from(decimal: $ty) -> Number { + Number::Decimal(BigDecimal::$from(decimal).unwrap()) + } + } + + impl From<&$ty> for Number { + fn from(decimal: &$ty) -> Number { + Number::Decimal(BigDecimal::$from(*decimal).unwrap()) + } + } + )* + } +} + +primitive_decimal!(f32 -> from_f32, f64 -> from_f64); + 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::Int(a), Number::Decimal(b)) => Number::Decimal(BigDecimal::from(a) * b), + (Number::Decimal(a), Number::Int(b)) => Number::Decimal(a * BigDecimal::from(b)), (Number::Decimal(a), Number::Decimal(b)) => Number::Decimal(a * b), } } @@ -96,36 +136,18 @@ impl std::ops::Mul for 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)), + Number::Decimal(left) => Number::Decimal(left * BigDecimal::from(other)), } } } -impl Into for f32 { - fn into(self) -> Number { - Number::Decimal(Decimal::from_f32(self).unwrap()) - } -} - -impl Into for f64 { - fn into(self) -> Number { - Number::Decimal(Decimal::from_f64(self).unwrap()) - } -} - -impl Into for i64 { - fn into(self) -> Number { - Number::Int(self) - } -} - -impl Into for Decimal { +impl Into for BigDecimal { fn into(self) -> Number { Number::Decimal(self) } } -pub fn raw_number(input: NomSpan) -> IResult> { +pub fn raw_number(input: NomSpan) -> IResult> { let original = input; let start = input.offset; trace_step(input, "raw_decimal", move |input| { @@ -137,28 +159,14 @@ pub fn raw_number(input: NomSpan) -> IResult> { 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), - ), - )) - } + Err(_) => return Ok((input, RawNumber::int((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(Number::Decimal(decimal), (start, end)), - )) + Ok((input, RawNumber::decimal((start, end)))) }) } @@ -708,12 +716,12 @@ mod tests { fn test_integer() { assert_leaf! { parsers [ size ] - "123" -> 0..3 { Number(Number::Int(123)) } + "123" -> 0..3 { Number(RawNumber::int((0, 3)).item) } } assert_leaf! { parsers [ size ] - "-123" -> 0..4 { Number(Number::Int(-123)) } + "-123" -> 0..4 { Number(RawNumber::int((0, 4)).item) } } } @@ -721,12 +729,12 @@ mod tests { fn test_size() { assert_leaf! { parsers [ size ] - "123MB" -> 0..5 { Size(Number::Int(123), Unit::MB) } + "123MB" -> 0..5 { Size(RawNumber::int((0, 3)).item, Unit::MB) } } assert_leaf! { parsers [ size ] - "10GB" -> 0..4 { Size(Number::Int(10), Unit::GB) } + "10GB" -> 0..4 { Size(RawNumber::int((0, 2)).item, Unit::GB) } } } diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index 19a7f07a4f..38db878fea 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -3,10 +3,9 @@ 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}; +use crate::parser::parse::tokens::{RawNumber, RawToken}; use crate::parser::parse::unit::Unit; use crate::parser::CallNode; use crate::Span; @@ -160,35 +159,32 @@ impl TokenTreeBuilder { )) } - pub fn int(input: impl Into) -> CurriedToken { + pub fn int(input: impl Into) -> CurriedToken { let int = input.into(); Box::new(move |b| { let (start, end) = b.consume(&int.to_string()); b.pos = end; - TokenTreeBuilder::spanned_int(int, (start, end)) + TokenTreeBuilder::spanned_number(RawNumber::Int((start, end).into()), (start, end)) }) } - pub fn spanned_number(input: impl Into, span: impl Into) -> TokenNode { - match input.into() { - Number::Int(int) => TokenTreeBuilder::spanned_int(int, span), - Number::Decimal(decimal) => TokenTreeBuilder::spanned_decimal(decimal, span), - } + pub fn decimal(input: impl Into) -> CurriedToken { + let decimal = input.into(); + + Box::new(move |b| { + let (start, end) = b.consume(&decimal.to_string()); + b.pos = end; + + TokenTreeBuilder::spanned_number(RawNumber::Decimal((start, end).into()), (start, end)) + }) } - fn spanned_int(input: i64, span: impl Into) -> TokenNode { - TokenNode::Token(Token::from_simple_spanned_item( - RawToken::Number(Number::Int(input)), - span, - )) - } - - fn spanned_decimal(input: Decimal, span: impl Into) -> TokenNode { - TokenNode::Token(Token::from_simple_spanned_item( - RawToken::Number(Number::Decimal(input)), - span, + pub fn spanned_number(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Token(Tagged::from_simple_spanned_item( + RawToken::Number(input.into()), + span.into(), )) } @@ -197,16 +193,19 @@ impl TokenTreeBuilder { let unit = unit.into(); Box::new(move |b| { - let (start, _) = b.consume(&int.to_string()); - let (_, end) = b.consume(unit.as_str()); - b.pos = end; + let (start_int, end_int) = b.consume(&int.to_string()); + let (start_unit, end_unit) = b.consume(unit.as_str()); + b.pos = end_unit; - TokenTreeBuilder::spanned_size((int, unit), (start, end)) + TokenTreeBuilder::spanned_size( + (RawNumber::Int((start_int, end_int).into()), unit), + (start_int, end_unit), + ) }) } pub fn spanned_size( - input: (impl Into, impl Into), + input: (impl Into, impl Into), span: impl Into, ) -> TokenNode { let (int, unit) = (input.0.into(), input.1.into()); diff --git a/src/parser/parse/tokens.rs b/src/parser/parse/tokens.rs index 79f08630f8..ed9c1f72a4 100644 --- a/src/parser/parse/tokens.rs +++ b/src/parser/parse/tokens.rs @@ -2,17 +2,47 @@ use crate::parser::parse::unit::*; use crate::prelude::*; use crate::{Span, Tagged, Text}; use std::fmt; +use std::str::FromStr; #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum RawToken { - Number(Number), - Size(Number, Unit), + Number(RawNumber), + Size(RawNumber, Unit), String(Span), Variable(Span), External(Span), Bare, } +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum RawNumber { + Int(Span), + Decimal(Span), +} + +impl RawNumber { + pub fn int(span: impl Into) -> Tagged { + let span = span.into(); + + RawNumber::Int(span).tagged(span) + } + + pub fn decimal(span: impl Into) -> Tagged { + let span = span.into(); + + RawNumber::Decimal(span).tagged(span) + } + + pub(crate) fn to_number(self, source: &Text) -> Number { + match self { + RawNumber::Int(span) => Number::Int(BigInt::from_str(span.slice(source)).unwrap()), + RawNumber::Decimal(span) => { + Number::Decimal(BigDecimal::from_str(span.slice(source)).unwrap()) + } + } + } +} + impl RawToken { pub fn type_name(&self) -> &'static str { match self { diff --git a/src/parser/parse/unit.rs b/src/parser/parse/unit.rs index 47129ccf2a..a29ce15b9f 100644 --- a/src/parser/parse/unit.rs +++ b/src/parser/parse/unit.rs @@ -25,7 +25,9 @@ impl Unit { } } - pub(crate) fn compute(&self, size: Number) -> Value { + pub(crate) fn compute(&self, size: &Number) -> Value { + let size = size.clone(); + Value::number(match self { Unit::B => size, Unit::KB => size * 1024, diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index 5e6a2a293f..fecba04e7d 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -75,7 +75,7 @@ impl Inc { } fn inc(&self, value: Tagged) -> Result, ShellError> { - match value.item { + match value.item() { Value::Primitive(Primitive::Int(i)) => Ok(Value::int(i + 1).tagged(value.tag())), Value::Primitive(Primitive::Bytes(b)) => { Ok(Value::bytes(b + 1 as u64).tagged(value.tag())) diff --git a/src/plugins/skip.rs b/src/plugins/skip.rs index bb80534218..cb259e99b9 100644 --- a/src/plugins/skip.rs +++ b/src/plugins/skip.rs @@ -1,11 +1,12 @@ use nu::{ - serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxType, Tagged, Value, + serve_plugin, CallInfo, CoerceInto, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, + Signature, SyntaxType, Tagged, TaggedItem, Value, }; struct Skip { skip_amount: i64, } + impl Skip { fn new() -> Skip { Skip { skip_amount: 0 } @@ -25,9 +26,9 @@ impl Plugin for Skip { match arg { Tagged { item: Value::Primitive(Primitive::Int(i)), - .. + tag, } => { - self.skip_amount = i; + self.skip_amount = i.tagged(tag).coerce_into("converting for skip")?; } _ => { return Err(ShellError::labeled_error( diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 8aa24b4907..d47fec0f22 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -258,13 +258,13 @@ fn main() { #[cfg(test)] mod tests { - use super::{Action, ReplaceAction, Str}; use indexmap::IndexMap; use nu::{ CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, SourceMap, Span, Tag, Tagged, TaggedDictBuilder, TaggedItem, Value, }; + use num_bigint::BigInt; impl Str { fn replace_with(&mut self, value: &str) { @@ -600,7 +600,7 @@ mod tests { ReturnSuccess::Value(Tagged { item: Value::Primitive(Primitive::Int(i)), .. - }) => assert_eq!(*i, 10), + }) => assert_eq!(*i, BigInt::from(10)), _ => {} } } diff --git a/src/plugins/sum.rs b/src/plugins/sum.rs index bd30cef98e..32ecd7a9ce 100644 --- a/src/plugins/sum.rs +++ b/src/plugins/sum.rs @@ -12,10 +12,10 @@ impl Sum { } fn sum(&mut self, value: Tagged) -> Result<(), ShellError> { - match value.item { + match value.item() { Value::Primitive(Primitive::Nothing) => Ok(()), Value::Primitive(Primitive::Int(i)) => { - match self.total { + match &self.total { Some(Tagged { item: Value::Primitive(Primitive::Int(j)), tag: Tag { span, .. }, @@ -26,7 +26,7 @@ impl Sum { Ok(()) } None => { - self.total = Some(value); + self.total = Some(value.clone()); Ok(()) } _ => Err(ShellError::string(format!( diff --git a/src/plugins/sys.rs b/src/plugins/sys.rs index e7ff919d8f..41b280ffe4 100644 --- a/src/plugins/sys.rs +++ b/src/plugins/sys.rs @@ -20,7 +20,7 @@ async fn cpu(tag: Tag) -> Option> { 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)); + cpu_idx.insert("cores", Primitive::number(num_cpu)); let current_speed = (cpu_speed.current().get::() as f64 / 1_000_000_000.0 * 100.0) diff --git a/src/prelude.rs b/src/prelude.rs index d781ff22ce..f800dc8cda 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -57,7 +57,7 @@ pub(crate) use crate::context::CommandRegistry; pub(crate) use crate::context::{Context, SpanSource}; pub(crate) use crate::env::host::handle_unexpected; pub(crate) use crate::env::Host; -pub(crate) use crate::errors::ShellError; +pub(crate) use crate::errors::{CoerceInto, ShellError}; pub(crate) use crate::object::base as value; pub(crate) use crate::object::meta::{Tag, Tagged, TaggedItem}; pub(crate) use crate::object::types::ExtractType; @@ -73,13 +73,14 @@ pub(crate) use crate::stream::{InputStream, OutputStream}; pub(crate) use crate::traits::{HasSpan, ToDebug}; pub(crate) use crate::Span; pub(crate) use crate::Text; +pub(crate) use bigdecimal::BigDecimal; 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_bigint::BigInt; 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 num_traits::identities::Zero; +pub(crate) use serde::Deserialize; pub(crate) use std::collections::VecDeque; pub(crate) use std::future::Future; pub(crate) use std::sync::{Arc, Mutex};