mirror of
https://github.com/nushell/nushell.git
synced 2024-11-21 16:03:19 +01:00
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.
This commit is contained in:
parent
9e17b937c3
commit
8a29c9e6ab
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
/target
|
||||
/scratch
|
||||
**/*.rs.bk
|
||||
history.txt
|
||||
tests/fixtures/nuplayground
|
||||
tests/fixtures/nuplayground
|
||||
|
52
Cargo.lock
generated
52
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"]
|
||||
|
@ -5,6 +5,11 @@ use crate::prelude::*;
|
||||
|
||||
pub struct First;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FirstArgs {
|
||||
amount: Tagged<u64>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for First {
|
||||
fn name(&self) -> &str {
|
||||
"first"
|
||||
@ -24,27 +29,13 @@ impl WholeStreamCommand for First {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
first(args, registry)
|
||||
args.process(registry, first)?.run()
|
||||
}
|
||||
}
|
||||
|
||||
fn first(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
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<OutputStream, ShellError> {
|
||||
Ok(OutputStream::from_input(context.input.values.take(*amount)))
|
||||
}
|
||||
|
@ -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<Tag>) -> Tagged<Value> {
|
||||
fn bson_array(input: &Vec<Bson>, tag: Tag) -> Result<Vec<Tagged<Value>>, 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<Tag>,
|
||||
) -> Result<Tagged<Value>, 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<Tag>) -> Tagged<Value
|
||||
);
|
||||
collected.into_tagged_value()
|
||||
}
|
||||
// TODO: Add Int32 to nushell?
|
||||
Bson::I32(n) => 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<Tag>) -> Tagged<Value
|
||||
);
|
||||
collected.insert_tagged(
|
||||
"$scope".to_string(),
|
||||
convert_bson_value_to_nu_value(&Bson::Document(doc.to_owned()), tag),
|
||||
convert_bson_value_to_nu_value(&Bson::Document(doc.to_owned()), tag)?,
|
||||
);
|
||||
collected.into_tagged_value()
|
||||
}
|
||||
Bson::TimeStamp(ts) => {
|
||||
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<Tag>) -> Tagged<Value
|
||||
collected.insert_tagged(
|
||||
"$binary_subtype".to_string(),
|
||||
match bst {
|
||||
BinarySubtype::UserDefined(u) => 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<Tag>) -> Tagged<Value
|
||||
);
|
||||
collected.into_tagged_value()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn binary_subtype_to_string(bst: BinarySubtype) -> 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<OutputStream, ShellError> {
|
||||
|
@ -37,10 +37,10 @@ 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::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)
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ fn convert_sqlite_row_to_nu_value(
|
||||
fn convert_sqlite_value_to_nu_value(value: ValueRef, tag: impl Into<Tag> + Clone) -> Tagged<Value> {
|
||||
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.
|
||||
|
@ -30,9 +30,9 @@ pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into<Tag>) -> 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()
|
||||
|
@ -54,9 +54,9 @@ fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, tag: impl Into<Tag>) ->
|
||||
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)
|
||||
|
@ -5,6 +5,11 @@ use crate::prelude::*;
|
||||
|
||||
pub struct Last;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LastArgs {
|
||||
amount: Tagged<u64>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Last {
|
||||
fn name(&self) -> &str {
|
||||
"last"
|
||||
@ -24,37 +29,18 @@ impl WholeStreamCommand for Last {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
last(args, registry)
|
||||
args.process(registry, last)?.run()
|
||||
// last(args, registry)
|
||||
}
|
||||
}
|
||||
|
||||
fn last(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
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<OutputStream, ShellError> {
|
||||
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<Value> = x.clone();
|
||||
yield ReturnSuccess::value(y)
|
||||
|
@ -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<Value>) -> Result<Bson, ShellError> {
|
||||
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::<Result<_, _>>()?,
|
||||
),
|
||||
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<Bson, ShellError> {
|
||||
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<String, _> = 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<Vec<u8>, _> = 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<Value>) -> Option<BinarySubtype> {
|
||||
fn get_binary_subtype<'a>(tagged_value: &'a Tagged<Value>) -> Result<BinarySubtype, ShellError> {
|
||||
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<Value>) -> Option<BinarySubty
|
||||
"md5" => 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<Bson, ShellError> {
|
||||
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<OutputStream
|
||||
Ok(out
|
||||
.values
|
||||
.map(
|
||||
move |a| match bson_value_to_bytes(value_to_bson_value(&a), name_span) {
|
||||
move |a| match bson_value_to_bytes(value_to_bson_value(&a)?, name_span) {
|
||||
Ok(x) => ReturnSuccess::value(Value::Binary(x).simple_spanned(name_span)),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected an object with BSON-compatible structure from pipeline",
|
||||
|
@ -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<Value>) -> Result<serde_json::Value, ShellError> {
|
||||
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::<i64>::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<Tagged<Value>>) -> Result<Vec<serde_json::Value>, 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<OutputStream, ShellError> {
|
||||
@ -77,7 +85,7 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
||||
Ok(out
|
||||
.values
|
||||
.map(
|
||||
move |a| match serde_json::to_string(&value_to_json_value(&a)) {
|
||||
move |a| match serde_json::to_string(&value_to_json_value(&a)?) {
|
||||
Ok(x) => ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::String(x)).simple_spanned(name_span),
|
||||
),
|
||||
|
@ -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<toml::Value, ShellError> {
|
||||
Ok(match v {
|
||||
pub fn value_to_toml_value(v: &Tagged<Value>) -> Result<toml::Value, ShellError> {
|
||||
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("<End of Stream>".to_string())
|
||||
@ -41,9 +38,11 @@ pub fn value_to_toml_value(v: &Value) -> Result<toml::Value, ShellError> {
|
||||
toml::Value::String("<Beginning of Stream>".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("<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()),
|
||||
|
@ -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<Value>) -> Result<serde_yaml::Value, ShellError> {
|
||||
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::<i64>::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<OutputStream, ShellError> {
|
||||
@ -71,7 +80,7 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
||||
Ok(out
|
||||
.values
|
||||
.map(
|
||||
move |a| match serde_yaml::to_string(&value_to_yaml_value(&a)) {
|
||||
move |a| match serde_yaml::to_string(&value_to_yaml_value(&a)?) {
|
||||
Ok(x) => ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::String(x)).simple_spanned(name_span),
|
||||
),
|
||||
|
156
src/errors.rs
156
src/errors.rs
@ -85,12 +85,14 @@ impl ShellError {
|
||||
}
|
||||
|
||||
pub(crate) fn range_error(
|
||||
expected: impl Into<String>,
|
||||
actual: Tagged<impl fmt::Debug>,
|
||||
expected: impl Into<ExpectedRange>,
|
||||
actual: &Tagged<impl fmt::Debug>,
|
||||
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<String>,
|
||||
operation: String,
|
||||
},
|
||||
Diagnostic(ShellDiagnostic),
|
||||
CoerceError {
|
||||
@ -557,13 +602,94 @@ 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)),
|
||||
}
|
||||
pub trait CoerceInto<U> {
|
||||
fn coerce_into(self, operation: impl Into<String>) -> Result<U, ShellError>;
|
||||
}
|
||||
|
||||
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<BigInt> {
|
||||
fn coerce_into(self, operation: impl Into<String>) -> 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<String>) -> 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<BigDecimal> {
|
||||
fn coerce_into(self, operation: impl Into<String>) -> 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<String>) -> 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);
|
||||
|
@ -38,7 +38,7 @@ pub(crate) fn evaluate_baseline_expr(
|
||||
source: &Text,
|
||||
) -> Result<Tagged<Value>, 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<hir::Literal>, source: &Text) -> Tagged<Value> {
|
||||
fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged<Value> {
|
||||
let result = match literal.item {
|
||||
hir::Literal::Number(int) => int.into(),
|
||||
hir::Literal::Size(int, unit) => unit.compute(int),
|
||||
|
@ -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};
|
||||
|
@ -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<i64> for Primitive {
|
||||
fn from(int: i64) -> Primitive {
|
||||
Primitive::Int(int)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Decimal> for Primitive {
|
||||
fn from(decimal: Decimal) -> Primitive {
|
||||
impl From<BigDecimal> for Primitive {
|
||||
fn from(decimal: BigDecimal) -> Primitive {
|
||||
Primitive::Decimal(decimal)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> 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<Value> for Number {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> 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<Tagged<Value>>) -> ValuesDebug<'_> {
|
||||
ValuesDebug { values }
|
||||
}
|
||||
@ -251,7 +254,9 @@ impl std::convert::TryFrom<&Tagged<Value>> for i64 {
|
||||
|
||||
fn try_from(value: &Tagged<Value>) -> Result<i64, ShellError> {
|
||||
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<i64, ShellError> {
|
||||
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<i64>) -> Value {
|
||||
pub fn int(s: impl Into<BigInt>) -> Value {
|
||||
Value::Primitive(Primitive::Int(s.into()))
|
||||
}
|
||||
|
||||
pub fn decimal(s: impl Into<Decimal>) -> Value {
|
||||
pub fn decimal(s: impl Into<BigDecimal>) -> 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())),
|
||||
|
@ -36,7 +36,8 @@ 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())).tagged_unknown())?;
|
||||
|
||||
let contents = toml::to_string(&contents)?;
|
||||
|
||||
|
@ -135,11 +135,25 @@ impl ExtractType for i64 {
|
||||
fn extract(value: &Tagged<Value>) -> Result<i64, ShellError> {
|
||||
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<Value>) -> Result<u64, ShellError> {
|
||||
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())),
|
||||
}
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ impl From<Tagged<Path>> 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),
|
||||
|
@ -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()),
|
||||
}
|
||||
|
@ -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<u32> 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<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 {
|
||||
impl Into<Number> for BigDecimal {
|
||||
fn into(self) -> Number {
|
||||
Number::Decimal(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_number(input: NomSpan) -> IResult<NomSpan, Tagged<Number>> {
|
||||
pub fn raw_number(input: NomSpan) -> IResult<NomSpan, Tagged<RawNumber>> {
|
||||
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<NomSpan, Tagged<Number>> {
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<i64>) -> CurriedToken {
|
||||
pub fn int(input: impl Into<BigInt>) -> 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<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),
|
||||
}
|
||||
pub fn decimal(input: impl Into<BigDecimal>) -> 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<Span>) -> TokenNode {
|
||||
TokenNode::Token(Token::from_simple_spanned_item(
|
||||
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,
|
||||
pub fn spanned_number(input: impl Into<RawNumber>, span: impl Into<Span>) -> 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<Number>, impl Into<Unit>),
|
||||
input: (impl Into<RawNumber>, impl Into<Unit>),
|
||||
span: impl Into<Span>,
|
||||
) -> TokenNode {
|
||||
let (int, unit) = (input.0.into(), input.1.into());
|
||||
|
@ -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<Span>) -> Tagged<RawNumber> {
|
||||
let span = span.into();
|
||||
|
||||
RawNumber::Int(span).tagged(span)
|
||||
}
|
||||
|
||||
pub fn decimal(span: impl Into<Span>) -> Tagged<RawNumber> {
|
||||
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 {
|
||||
|
@ -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,
|
||||
|
@ -75,7 +75,7 @@ impl Inc {
|
||||
}
|
||||
|
||||
fn inc(&self, value: Tagged<Value>) -> Result<Tagged<Value>, 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()))
|
||||
|
@ -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(
|
||||
|
@ -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)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -12,10 +12,10 @@ impl Sum {
|
||||
}
|
||||
|
||||
fn sum(&mut self, value: Tagged<Value>) -> 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!(
|
||||
|
@ -20,7 +20,7 @@ async fn cpu(tag: Tag) -> Option<Tagged<Value>> {
|
||||
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::<frequency::hertz>() as f64 / 1_000_000_000.0 * 100.0)
|
||||
|
@ -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};
|
||||
|
Loading…
Reference in New Issue
Block a user