mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 16:33:37 +01:00
1747 add ns to duration (#2128)
* Added nanos to Duration * Removed unwraps * Added nanos to Duration * Removed unwraps * Fixed errors * Removed unwraps * Changed serialization to String * Fixed Date and Duration comparison
This commit is contained in:
parent
6a89b1b010
commit
e07a9e4ee7
@ -46,7 +46,7 @@ pub fn value_to_bson_value(v: &Value) -> Result<Bson, ShellError> {
|
|||||||
.to_f64()
|
.to_f64()
|
||||||
.expect("Unimplemented BUG: What about big decimals?"),
|
.expect("Unimplemented BUG: What about big decimals?"),
|
||||||
),
|
),
|
||||||
UntaggedValue::Primitive(Primitive::Duration(secs)) => Bson::I64(*secs as i64),
|
UntaggedValue::Primitive(Primitive::Duration(i)) => Bson::String(i.to_string()),
|
||||||
UntaggedValue::Primitive(Primitive::Date(d)) => Bson::UtcDatetime(*d),
|
UntaggedValue::Primitive(Primitive::Date(d)) => Bson::UtcDatetime(*d),
|
||||||
UntaggedValue::Primitive(Primitive::EndOfStream) => Bson::Null,
|
UntaggedValue::Primitive(Primitive::EndOfStream) => Bson::Null,
|
||||||
UntaggedValue::Primitive(Primitive::BeginningOfStream) => Bson::Null,
|
UntaggedValue::Primitive(Primitive::BeginningOfStream) => Bson::Null,
|
||||||
|
@ -65,8 +65,8 @@ pub fn value_to_json_value(v: &Value) -> Result<serde_json::Value, ShellError> {
|
|||||||
UntaggedValue::Primitive(Primitive::Bytes(b)) => serde_json::Value::Number(
|
UntaggedValue::Primitive(Primitive::Bytes(b)) => serde_json::Value::Number(
|
||||||
serde_json::Number::from(b.to_u64().expect("What about really big numbers")),
|
serde_json::Number::from(b.to_u64().expect("What about really big numbers")),
|
||||||
),
|
),
|
||||||
UntaggedValue::Primitive(Primitive::Duration(secs)) => {
|
UntaggedValue::Primitive(Primitive::Duration(i)) => {
|
||||||
serde_json::Value::Number(serde_json::Number::from(*secs))
|
serde_json::Value::String(i.to_string())
|
||||||
}
|
}
|
||||||
UntaggedValue::Primitive(Primitive::Date(d)) => serde_json::Value::String(d.to_string()),
|
UntaggedValue::Primitive(Primitive::Date(d)) => serde_json::Value::String(d.to_string()),
|
||||||
UntaggedValue::Primitive(Primitive::EndOfStream) => serde_json::Value::Null,
|
UntaggedValue::Primitive(Primitive::EndOfStream) => serde_json::Value::Null,
|
||||||
|
@ -91,7 +91,7 @@ fn nu_value_to_sqlite_string(v: Value) -> String {
|
|||||||
UntaggedValue::Primitive(p) => match p {
|
UntaggedValue::Primitive(p) => match p {
|
||||||
Primitive::Nothing => "NULL".into(),
|
Primitive::Nothing => "NULL".into(),
|
||||||
Primitive::Int(i) => format!("{}", i),
|
Primitive::Int(i) => format!("{}", i),
|
||||||
Primitive::Duration(u) => format!("{}", u),
|
Primitive::Duration(i) => format!("{}", i),
|
||||||
Primitive::Decimal(f) => format!("{}", f),
|
Primitive::Decimal(f) => format!("{}", f),
|
||||||
Primitive::Bytes(u) => format!("{}", u),
|
Primitive::Bytes(u) => format!("{}", u),
|
||||||
Primitive::Pattern(s) => format!("'{}'", s.replace("'", "''")),
|
Primitive::Pattern(s) => format!("'{}'", s.replace("'", "''")),
|
||||||
|
@ -45,7 +45,7 @@ fn helper(v: &Value) -> Result<toml::Value, ShellError> {
|
|||||||
Ok(match &v.value {
|
Ok(match &v.value {
|
||||||
UntaggedValue::Primitive(Primitive::Boolean(b)) => toml::Value::Boolean(*b),
|
UntaggedValue::Primitive(Primitive::Boolean(b)) => toml::Value::Boolean(*b),
|
||||||
UntaggedValue::Primitive(Primitive::Bytes(b)) => toml::Value::Integer(*b as i64),
|
UntaggedValue::Primitive(Primitive::Bytes(b)) => toml::Value::Integer(*b as i64),
|
||||||
UntaggedValue::Primitive(Primitive::Duration(d)) => toml::Value::Integer(*d as i64),
|
UntaggedValue::Primitive(Primitive::Duration(i)) => toml::Value::String(i.to_string()),
|
||||||
UntaggedValue::Primitive(Primitive::Date(d)) => toml::Value::String(d.to_string()),
|
UntaggedValue::Primitive(Primitive::Date(d)) => toml::Value::String(d.to_string()),
|
||||||
UntaggedValue::Primitive(Primitive::EndOfStream) => {
|
UntaggedValue::Primitive(Primitive::EndOfStream) => {
|
||||||
toml::Value::String("<End of Stream>".to_string())
|
toml::Value::String("<End of Stream>".to_string())
|
||||||
|
@ -40,15 +40,9 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
|
|||||||
)
|
)
|
||||||
})?))
|
})?))
|
||||||
}
|
}
|
||||||
UntaggedValue::Primitive(Primitive::Duration(secs)) => serde_yaml::Value::Number(
|
UntaggedValue::Primitive(Primitive::Duration(i)) => {
|
||||||
serde_yaml::Number::from(secs.to_f64().ok_or_else(|| {
|
serde_yaml::Value::String(i.to_string())
|
||||||
ShellError::labeled_error(
|
}
|
||||||
"Could not convert to duration",
|
|
||||||
"could not convert to duration",
|
|
||||||
&v.tag,
|
|
||||||
)
|
|
||||||
})?),
|
|
||||||
),
|
|
||||||
UntaggedValue::Primitive(Primitive::Date(d)) => serde_yaml::Value::String(d.to_string()),
|
UntaggedValue::Primitive(Primitive::Date(d)) => serde_yaml::Value::String(d.to_string()),
|
||||||
UntaggedValue::Primitive(Primitive::EndOfStream) => serde_yaml::Value::Null,
|
UntaggedValue::Primitive(Primitive::EndOfStream) => serde_yaml::Value::Null,
|
||||||
UntaggedValue::Primitive(Primitive::BeginningOfStream) => serde_yaml::Value::Null,
|
UntaggedValue::Primitive(Primitive::BeginningOfStream) => serde_yaml::Value::Null,
|
||||||
|
@ -7,13 +7,12 @@ use nu_errors::ShellError;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
hir, Primitive, ShellTypeName, SpannedTypeName, TaggedDictBuilder, UntaggedValue, Value,
|
hir, Primitive, ShellTypeName, SpannedTypeName, TaggedDictBuilder, UntaggedValue, Value,
|
||||||
};
|
};
|
||||||
use nu_source::Tag;
|
use nu_source::{Span, Tag};
|
||||||
use nu_value_ext::ValueExt;
|
use nu_value_ext::ValueExt;
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
use num_traits::Zero;
|
use num_traits::Zero;
|
||||||
use query_interface::{interfaces, vtable_for, ObjectHash};
|
use query_interface::{interfaces, vtable_for, ObjectHash};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new, Serialize)]
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new, Serialize)]
|
||||||
pub struct Operation {
|
pub struct Operation {
|
||||||
@ -87,7 +86,7 @@ pub(crate) enum CompareValues {
|
|||||||
Decimals(BigDecimal, BigDecimal),
|
Decimals(BigDecimal, BigDecimal),
|
||||||
String(String, String),
|
String(String, String),
|
||||||
Date(DateTime<Utc>, DateTime<Utc>),
|
Date(DateTime<Utc>, DateTime<Utc>),
|
||||||
DateDuration(DateTime<Utc>, i64),
|
DateDuration(DateTime<Utc>, BigInt),
|
||||||
Booleans(bool, bool),
|
Booleans(bool, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,14 +98,15 @@ impl CompareValues {
|
|||||||
CompareValues::String(left, right) => left.cmp(right),
|
CompareValues::String(left, right) => left.cmp(right),
|
||||||
CompareValues::Date(left, right) => left.cmp(right),
|
CompareValues::Date(left, right) => left.cmp(right),
|
||||||
CompareValues::DateDuration(left, right) => {
|
CompareValues::DateDuration(left, right) => {
|
||||||
use std::time::Duration;
|
// FIXME: Not sure if I could do something better with the Span.
|
||||||
|
let duration = Primitive::into_chrono_duration(
|
||||||
// Create the datetime we're comparing against, as duration is an offset from now
|
Primitive::Duration(right.clone()),
|
||||||
let right: DateTime<Utc> = if *right < 0 {
|
Span::unknown(),
|
||||||
(SystemTime::now() + Duration::from_secs((*right * -1) as u64)).into()
|
)
|
||||||
} else {
|
.expect("Could not convert nushell Duration into chrono Duration.");
|
||||||
(SystemTime::now() - Duration::from_secs(*right as u64)).into()
|
let right: DateTime<Utc> = Utc::now()
|
||||||
};
|
.checked_sub_signed(duration)
|
||||||
|
.expect("Data overflow");
|
||||||
right.cmp(left)
|
right.cmp(left)
|
||||||
}
|
}
|
||||||
CompareValues::Booleans(left, right) => left.cmp(right),
|
CompareValues::Booleans(left, right) => left.cmp(right),
|
||||||
@ -159,7 +159,7 @@ fn coerce_compare_primitive(
|
|||||||
(String(left), Line(right)) => CompareValues::String(left.clone(), right.clone()),
|
(String(left), Line(right)) => CompareValues::String(left.clone(), right.clone()),
|
||||||
(Line(left), Line(right)) => CompareValues::String(left.clone(), right.clone()),
|
(Line(left), Line(right)) => CompareValues::String(left.clone(), right.clone()),
|
||||||
(Date(left), Date(right)) => CompareValues::Date(*left, *right),
|
(Date(left), Date(right)) => CompareValues::Date(*left, *right),
|
||||||
(Date(left), Duration(right)) => CompareValues::DateDuration(*left, *right),
|
(Date(left), Duration(right)) => CompareValues::DateDuration(*left, right.clone()),
|
||||||
(Boolean(left), Boolean(right)) => CompareValues::Booleans(*left, *right),
|
(Boolean(left), Boolean(right)) => CompareValues::Booleans(*left, *right),
|
||||||
_ => return Err((left.type_name(), right.type_name())),
|
_ => return Err((left.type_name(), right.type_name())),
|
||||||
})
|
})
|
||||||
|
@ -27,7 +27,7 @@ pub enum InlineShape {
|
|||||||
Pattern(String),
|
Pattern(String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Date(DateTime<Utc>),
|
Date(DateTime<Utc>),
|
||||||
Duration(i64),
|
Duration(BigInt),
|
||||||
Path(PathBuf),
|
Path(PathBuf),
|
||||||
Binary(usize),
|
Binary(usize),
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ impl InlineShape {
|
|||||||
Primitive::Pattern(pattern) => InlineShape::Pattern(pattern.clone()),
|
Primitive::Pattern(pattern) => InlineShape::Pattern(pattern.clone()),
|
||||||
Primitive::Boolean(boolean) => InlineShape::Boolean(*boolean),
|
Primitive::Boolean(boolean) => InlineShape::Boolean(*boolean),
|
||||||
Primitive::Date(date) => InlineShape::Date(*date),
|
Primitive::Date(date) => InlineShape::Date(*date),
|
||||||
Primitive::Duration(duration) => InlineShape::Duration(*duration),
|
Primitive::Duration(duration) => InlineShape::Duration(duration.clone()),
|
||||||
Primitive::Path(path) => InlineShape::Path(path.clone()),
|
Primitive::Path(path) => InlineShape::Path(path.clone()),
|
||||||
Primitive::Binary(b) => InlineShape::Binary(b.len()),
|
Primitive::Binary(b) => InlineShape::Binary(b.len()),
|
||||||
Primitive::BeginningOfStream => InlineShape::BeginningOfStream,
|
Primitive::BeginningOfStream => InlineShape::BeginningOfStream,
|
||||||
@ -178,9 +178,10 @@ impl PrettyDebug for FormatInlineShape {
|
|||||||
.to_owned(),
|
.to_owned(),
|
||||||
),
|
),
|
||||||
InlineShape::Date(date) => b::primitive(nu_protocol::format_date(date)),
|
InlineShape::Date(date) => b::primitive(nu_protocol::format_date(date)),
|
||||||
InlineShape::Duration(duration) => {
|
InlineShape::Duration(duration) => b::description(format_primitive(
|
||||||
b::description(format_primitive(&Primitive::Duration(*duration), None))
|
&Primitive::Duration(duration.clone()),
|
||||||
}
|
None,
|
||||||
|
)),
|
||||||
InlineShape::Path(path) => b::primitive(path.display()),
|
InlineShape::Path(path) => b::primitive(path.display()),
|
||||||
InlineShape::Binary(length) => b::opaque(format!("<binary: {} bytes>", length)),
|
InlineShape::Binary(length) => b::opaque(format!("<binary: {} bytes>", length)),
|
||||||
InlineShape::Row(row) => b::delimit(
|
InlineShape::Row(row) => b::delimit(
|
||||||
|
@ -6,7 +6,7 @@ use nu_errors::ShellError;
|
|||||||
use nu_protocol::hir::Operator;
|
use nu_protocol::hir::Operator;
|
||||||
use nu_protocol::ShellTypeName;
|
use nu_protocol::ShellTypeName;
|
||||||
use nu_protocol::{Primitive, Type, UntaggedValue};
|
use nu_protocol::{Primitive, Type, UntaggedValue};
|
||||||
use nu_source::{DebugDocBuilder, PrettyDebug, Tagged};
|
use nu_source::{DebugDocBuilder, PrettyDebug, Span, Tagged};
|
||||||
use nu_table::TextStyle;
|
use nu_table::TextStyle;
|
||||||
use num_traits::Zero;
|
use num_traits::Zero;
|
||||||
|
|
||||||
@ -118,18 +118,20 @@ pub fn compute_values(
|
|||||||
}?;
|
}?;
|
||||||
Ok(UntaggedValue::Primitive(Primitive::Decimal(result)))
|
Ok(UntaggedValue::Primitive(Primitive::Decimal(result)))
|
||||||
}
|
}
|
||||||
(Primitive::Date(x), Primitive::Date(y)) => {
|
(Primitive::Date(x), Primitive::Date(y)) => match operator {
|
||||||
|
Operator::Minus => Ok(UntaggedValue::Primitive(Primitive::from(
|
||||||
|
x.signed_duration_since(*y),
|
||||||
|
))),
|
||||||
|
_ => Err((left.type_name(), right.type_name())),
|
||||||
|
},
|
||||||
|
(Primitive::Date(x), Primitive::Duration(_)) => {
|
||||||
let result = match operator {
|
let result = match operator {
|
||||||
Operator::Minus => Ok(x.signed_duration_since(*y).num_seconds()),
|
Operator::Plus => {
|
||||||
_ => Err((left.type_name(), right.type_name())),
|
// FIXME: Not sure if I could do something better with the Span.
|
||||||
}?;
|
let y = Primitive::into_chrono_duration(rhs.clone(), Span::unknown())
|
||||||
Ok(UntaggedValue::Primitive(Primitive::Duration(result)))
|
.expect("Could not convert nushell Duration into chrono Duration.");
|
||||||
}
|
Ok(x.checked_add_signed(y).expect("Data overflow."))
|
||||||
(Primitive::Date(x), Primitive::Duration(y)) => {
|
}
|
||||||
let result = match operator {
|
|
||||||
Operator::Plus => Ok(x
|
|
||||||
.checked_add_signed(chrono::Duration::seconds(*y as i64))
|
|
||||||
.expect("Overflowing add of duration")),
|
|
||||||
_ => Err((left.type_name(), right.type_name())),
|
_ => Err((left.type_name(), right.type_name())),
|
||||||
}?;
|
}?;
|
||||||
Ok(UntaggedValue::Primitive(Primitive::Date(result)))
|
Ok(UntaggedValue::Primitive(Primitive::Date(result)))
|
||||||
|
@ -168,7 +168,31 @@ fn duration_math() {
|
|||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
assert_eq!(actual.out, "8:00:00:00");
|
assert_eq!(actual.out, "8:00:00:00.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duration_math_with_nanoseconds() {
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: "tests/fixtures/formats", pipeline(
|
||||||
|
r#"
|
||||||
|
= 1w + 10ns
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual.out, "7:00:00:00.00000001");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duration_math_with_negative() {
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: "tests/fixtures/formats", pipeline(
|
||||||
|
r#"
|
||||||
|
= 1d - 1w
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual.out, "-6:00:00:00.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -311,6 +311,9 @@ fn parse_unit(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<ParseErr
|
|||||||
(Unit::Gigabyte, vec!["gb", "GB", "Gb", "gB"]),
|
(Unit::Gigabyte, vec!["gb", "GB", "Gb", "gB"]),
|
||||||
(Unit::Terabyte, vec!["tb", "TB", "Tb", "tB"]),
|
(Unit::Terabyte, vec!["tb", "TB", "Tb", "tB"]),
|
||||||
(Unit::Petabyte, vec!["pb", "PB", "Pb", "pB"]),
|
(Unit::Petabyte, vec!["pb", "PB", "Pb", "pB"]),
|
||||||
|
(Unit::Nanosecond, vec!["ns"]),
|
||||||
|
(Unit::Microsecond, vec!["us"]),
|
||||||
|
(Unit::Millisecond, vec!["ms"]),
|
||||||
(Unit::Second, vec!["s"]),
|
(Unit::Second, vec!["s"]),
|
||||||
(Unit::Minute, vec!["m"]),
|
(Unit::Minute, vec!["m"]),
|
||||||
(Unit::Hour, vec!["h"]),
|
(Unit::Hour, vec!["h"]),
|
||||||
|
@ -12,7 +12,6 @@ doctest = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
nu-errors = {path = "../nu-errors", version = "0.16.1"}
|
nu-errors = {path = "../nu-errors", version = "0.16.1"}
|
||||||
nu-source = {path = "../nu-source", version = "0.16.1"}
|
nu-source = {path = "../nu-source", version = "0.16.1"}
|
||||||
|
|
||||||
ansi_term = "0.12.1"
|
ansi_term = "0.12.1"
|
||||||
bigdecimal = {version = "0.1.2", features = ["serde"]}
|
bigdecimal = {version = "0.1.2", features = ["serde"]}
|
||||||
byte-unit = "3.1.3"
|
byte-unit = "3.1.3"
|
||||||
@ -26,6 +25,7 @@ log = "0.4.8"
|
|||||||
natural = "0.5.0"
|
natural = "0.5.0"
|
||||||
num-bigint = {version = "0.2.6", features = ["serde"]}
|
num-bigint = {version = "0.2.6", features = ["serde"]}
|
||||||
num-traits = "0.2.12"
|
num-traits = "0.2.12"
|
||||||
|
num-integer = "0.1.42"
|
||||||
query_interface = "0.3.5"
|
query_interface = "0.3.5"
|
||||||
serde = {version = "1.0.114", features = ["derive"]}
|
serde = {version = "1.0.114", features = ["derive"]}
|
||||||
serde_bytes = "0.11.5"
|
serde_bytes = "0.11.5"
|
||||||
|
@ -8,7 +8,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use crate::{hir, Primitive, UntaggedValue};
|
use crate::{hir, Primitive, UntaggedValue};
|
||||||
use crate::{PathMember, ShellTypeName};
|
use crate::{PathMember, ShellTypeName};
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use num_traits::ToPrimitive;
|
|
||||||
|
|
||||||
use nu_errors::ParseError;
|
use nu_errors::ParseError;
|
||||||
use nu_source::{
|
use nu_source::{
|
||||||
@ -19,9 +18,9 @@ use nu_source::{IntoSpanned, Span, Spanned, SpannedItem, Tag};
|
|||||||
use bigdecimal::BigDecimal;
|
use bigdecimal::BigDecimal;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use num_bigint::BigInt;
|
use num_bigint::{BigInt, ToBigInt};
|
||||||
use num_traits::identities::Zero;
|
use num_traits::identities::Zero;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::{FromPrimitive, ToPrimitive};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||||
pub struct InternalCommand {
|
pub struct InternalCommand {
|
||||||
@ -296,6 +295,9 @@ pub enum Unit {
|
|||||||
Petabyte,
|
Petabyte,
|
||||||
|
|
||||||
// Duration units
|
// Duration units
|
||||||
|
Nanosecond,
|
||||||
|
Microsecond,
|
||||||
|
Millisecond,
|
||||||
Second,
|
Second,
|
||||||
Minute,
|
Minute,
|
||||||
Hour,
|
Hour,
|
||||||
@ -433,6 +435,17 @@ impl std::ops::Mul<u32> for Number {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToBigInt for Number {
|
||||||
|
fn to_bigint(&self) -> Option<BigInt> {
|
||||||
|
match self {
|
||||||
|
Number::Int(int) => Some(int.clone()),
|
||||||
|
// The BigDecimal to BigInt conversion always return Some().
|
||||||
|
// FIXME: This conversion might not be want we want, it just remove the scale.
|
||||||
|
Number::Decimal(decimal) => decimal.to_bigint(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PrettyDebug for Unit {
|
impl PrettyDebug for Unit {
|
||||||
fn pretty(&self) -> DebugDocBuilder {
|
fn pretty(&self) -> DebugDocBuilder {
|
||||||
b::keyword(self.as_str())
|
b::keyword(self.as_str())
|
||||||
@ -458,25 +471,6 @@ pub fn convert_number_to_u64(number: &Number) -> u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_number_to_i64(number: &Number) -> i64 {
|
|
||||||
match number {
|
|
||||||
Number::Int(big_int) => {
|
|
||||||
if let Some(x) = big_int.to_i64() {
|
|
||||||
x
|
|
||||||
} else {
|
|
||||||
unreachable!("Internal error: convert_number_to_u64 given incompatible number")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Number::Decimal(big_decimal) => {
|
|
||||||
if let Some(x) = big_decimal.to_i64() {
|
|
||||||
x
|
|
||||||
} else {
|
|
||||||
unreachable!("Internal error: convert_number_to_u64 given incompatible number")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Unit {
|
impl Unit {
|
||||||
pub fn as_str(self) -> &'static str {
|
pub fn as_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
@ -486,6 +480,9 @@ impl Unit {
|
|||||||
Unit::Gigabyte => "GB",
|
Unit::Gigabyte => "GB",
|
||||||
Unit::Terabyte => "TB",
|
Unit::Terabyte => "TB",
|
||||||
Unit::Petabyte => "PB",
|
Unit::Petabyte => "PB",
|
||||||
|
Unit::Nanosecond => "ns",
|
||||||
|
Unit::Microsecond => "us",
|
||||||
|
Unit::Millisecond => "ms",
|
||||||
Unit::Second => "s",
|
Unit::Second => "s",
|
||||||
Unit::Minute => "m",
|
Unit::Minute => "m",
|
||||||
Unit::Hour => "h",
|
Unit::Hour => "h",
|
||||||
@ -508,13 +505,68 @@ impl Unit {
|
|||||||
Unit::Petabyte => {
|
Unit::Petabyte => {
|
||||||
bytes(convert_number_to_u64(&size) * 1024 * 1024 * 1024 * 1024 * 1024)
|
bytes(convert_number_to_u64(&size) * 1024 * 1024 * 1024 * 1024 * 1024)
|
||||||
}
|
}
|
||||||
Unit::Second => duration(convert_number_to_i64(&size)),
|
Unit::Nanosecond => duration(size.to_bigint().expect("Conversion should never fail.")),
|
||||||
Unit::Minute => duration(60 * convert_number_to_i64(&size)),
|
Unit::Microsecond => {
|
||||||
Unit::Hour => duration(60 * 60 * convert_number_to_i64(&size)),
|
duration(size.to_bigint().expect("Conversion should never fail.") * 1000)
|
||||||
Unit::Day => duration(24 * 60 * 60 * convert_number_to_i64(&size)),
|
}
|
||||||
Unit::Week => duration(7 * 24 * 60 * 60 * convert_number_to_i64(&size)),
|
Unit::Millisecond => {
|
||||||
Unit::Month => duration(30 * 24 * 60 * 60 * convert_number_to_i64(&size)),
|
duration(size.to_bigint().expect("Conversion should never fail.") * 1000 * 1000)
|
||||||
Unit::Year => duration(365 * 24 * 60 * 60 * convert_number_to_i64(&size)),
|
}
|
||||||
|
Unit::Second => duration(
|
||||||
|
size.to_bigint().expect("Conversion should never fail.") * 1000 * 1000 * 1000,
|
||||||
|
),
|
||||||
|
Unit::Minute => duration(
|
||||||
|
size.to_bigint().expect("Conversion should never fail.") * 60 * 1000 * 1000 * 1000,
|
||||||
|
),
|
||||||
|
Unit::Hour => duration(
|
||||||
|
size.to_bigint().expect("Conversion should never fail.")
|
||||||
|
* 60
|
||||||
|
* 60
|
||||||
|
* 1000
|
||||||
|
* 1000
|
||||||
|
* 1000,
|
||||||
|
),
|
||||||
|
Unit::Day => duration(
|
||||||
|
size.to_bigint().expect("Conversion should never fail.")
|
||||||
|
* 24
|
||||||
|
* 60
|
||||||
|
* 60
|
||||||
|
* 1000
|
||||||
|
* 1000
|
||||||
|
* 1000,
|
||||||
|
),
|
||||||
|
Unit::Week => duration(
|
||||||
|
size.to_bigint().expect("Conversion should never fail.")
|
||||||
|
* 7
|
||||||
|
* 24
|
||||||
|
* 60
|
||||||
|
* 60
|
||||||
|
* 1000
|
||||||
|
* 1000
|
||||||
|
* 1000,
|
||||||
|
),
|
||||||
|
// FIXME: Number of days per month should not always be 30.
|
||||||
|
Unit::Month => duration(
|
||||||
|
size.to_bigint().expect("Conversion should never fail.")
|
||||||
|
* 30
|
||||||
|
* 24
|
||||||
|
* 60
|
||||||
|
* 60
|
||||||
|
* 1000
|
||||||
|
* 1000
|
||||||
|
* 1000,
|
||||||
|
),
|
||||||
|
// FIXME: Number of days per year should not be 365.
|
||||||
|
Unit::Year => duration(
|
||||||
|
size.to_bigint().expect("Conversion should never fail.")
|
||||||
|
* 365
|
||||||
|
* 24
|
||||||
|
* 60
|
||||||
|
* 60
|
||||||
|
* 1000
|
||||||
|
* 1000
|
||||||
|
* 1000,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -523,8 +575,8 @@ pub fn bytes(size: u64) -> UntaggedValue {
|
|||||||
UntaggedValue::Primitive(Primitive::Bytes(size))
|
UntaggedValue::Primitive(Primitive::Bytes(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn duration(secs: i64) -> UntaggedValue {
|
pub fn duration(nanos: BigInt) -> UntaggedValue {
|
||||||
UntaggedValue::Primitive(Primitive::Duration(secs))
|
UntaggedValue::Primitive(Primitive::Duration(nanos))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
|
||||||
|
@ -212,8 +212,8 @@ impl UntaggedValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Helper for creating date duration values
|
/// Helper for creating date duration values
|
||||||
pub fn duration(secs: i64) -> UntaggedValue {
|
pub fn duration(nanos: BigInt) -> UntaggedValue {
|
||||||
UntaggedValue::Primitive(Primitive::Duration(secs))
|
UntaggedValue::Primitive(Primitive::Duration(nanos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper for creating datatime values
|
/// Helper for creating datatime values
|
||||||
|
@ -81,7 +81,7 @@ impl PrettyDebug for Primitive {
|
|||||||
false => b::primitive("$no"),
|
false => b::primitive("$no"),
|
||||||
},
|
},
|
||||||
Primitive::Date(date) => primitive_doc(date, "date"),
|
Primitive::Date(date) => primitive_doc(date, "date"),
|
||||||
Primitive::Duration(duration) => primitive_doc(duration, "seconds"),
|
Primitive::Duration(duration) => primitive_doc(duration, "nanoseconds"),
|
||||||
Primitive::Path(path) => primitive_doc(path, "path"),
|
Primitive::Path(path) => primitive_doc(path, "path"),
|
||||||
Primitive::Binary(_) => b::opaque("binary"),
|
Primitive::Binary(_) => b::opaque("binary"),
|
||||||
Primitive::BeginningOfStream => b::keyword("beginning-of-stream"),
|
Primitive::BeginningOfStream => b::keyword("beginning-of-stream"),
|
||||||
|
@ -7,11 +7,15 @@ use chrono::{DateTime, Utc};
|
|||||||
use nu_errors::{ExpectedRange, ShellError};
|
use nu_errors::{ExpectedRange, ShellError};
|
||||||
use nu_source::{PrettyDebug, Span, SpannedItem};
|
use nu_source::{PrettyDebug, Span, SpannedItem};
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
|
use num_integer::Integer;
|
||||||
use num_traits::cast::{FromPrimitive, ToPrimitive};
|
use num_traits::cast::{FromPrimitive, ToPrimitive};
|
||||||
use num_traits::identities::Zero;
|
use num_traits::identities::Zero;
|
||||||
|
use num_traits::sign::Signed;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
const NANOS_PER_SEC: u32 = 1000000000;
|
||||||
|
|
||||||
/// The most fundamental of structured values in Nu are the Primitive values. These values represent types like integers, strings, booleans, dates, etc that are then used
|
/// The most fundamental of structured values in Nu are the Primitive values. These values represent types like integers, strings, booleans, dates, etc that are then used
|
||||||
/// as the buildig blocks to build up more complex structures.
|
/// as the buildig blocks to build up more complex structures.
|
||||||
///
|
///
|
||||||
@ -40,8 +44,9 @@ pub enum Primitive {
|
|||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
/// A date value, in UTC
|
/// A date value, in UTC
|
||||||
Date(DateTime<Utc>),
|
Date(DateTime<Utc>),
|
||||||
/// A count in the number of seconds
|
/// A count in the number of nanoseconds
|
||||||
Duration(i64),
|
#[serde(with = "serde_bigint")]
|
||||||
|
Duration(BigInt),
|
||||||
/// A range of values
|
/// A range of values
|
||||||
Range(Box<Range>),
|
Range(Box<Range>),
|
||||||
/// A file path
|
/// A file path
|
||||||
@ -75,6 +80,40 @@ impl Primitive {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: This is a bad name, but no other way to differentiate with our own Duration.
|
||||||
|
pub fn into_chrono_duration(self, span: Span) -> Result<chrono::Duration, ShellError> {
|
||||||
|
match self {
|
||||||
|
Primitive::Duration(duration) => {
|
||||||
|
let (secs, nanos) = duration.div_rem(&BigInt::from(NANOS_PER_SEC));
|
||||||
|
let secs = match secs.to_i64() {
|
||||||
|
Some(secs) => secs,
|
||||||
|
None => {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
"Internal duration conversion overflow.",
|
||||||
|
"duration overflow",
|
||||||
|
span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// This should never fail since nanos < 10^9.
|
||||||
|
let nanos = match nanos.to_i64() {
|
||||||
|
Some(nanos) => nanos,
|
||||||
|
None => return Err(ShellError::unexpected("Unexpected i64 overflow")),
|
||||||
|
};
|
||||||
|
let nanos = chrono::Duration::nanoseconds(nanos);
|
||||||
|
// This should also never fail since we are adding less than NANOS_PER_SEC.
|
||||||
|
match chrono::Duration::seconds(secs).checked_add(&nanos) {
|
||||||
|
Some(duration) => Ok(duration),
|
||||||
|
None => Err(ShellError::unexpected("Unexpected duration overflow")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
other => Err(ShellError::type_error(
|
||||||
|
"duration",
|
||||||
|
other.type_name().spanned(span),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_string(self, span: Span) -> Result<String, ShellError> {
|
pub fn into_string(self, span: Span) -> Result<String, ShellError> {
|
||||||
match self {
|
match self {
|
||||||
Primitive::String(s) => Ok(s),
|
Primitive::String(s) => Ok(s),
|
||||||
@ -209,6 +248,20 @@ impl From<f64> for Primitive {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<chrono::Duration> for Primitive {
|
||||||
|
fn from(duration: chrono::Duration) -> Primitive {
|
||||||
|
// FIXME: This is a hack since chrono::Duration does not give access to its 'nanos' field.
|
||||||
|
let secs: i64 = duration.num_seconds();
|
||||||
|
// This will never fail.
|
||||||
|
let nanos: u32 = duration
|
||||||
|
.checked_sub(&chrono::Duration::seconds(secs))
|
||||||
|
.expect("Unexpected overflow")
|
||||||
|
.num_nanoseconds()
|
||||||
|
.expect("Unexpected overflow") as u32;
|
||||||
|
Primitive::Duration(BigInt::from(secs) * NANOS_PER_SEC + nanos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ShellTypeName for Primitive {
|
impl ShellTypeName for Primitive {
|
||||||
/// Get the name of the type of a Primitive value
|
/// Get the name of the type of a Primitive value
|
||||||
fn type_name(&self) -> &'static str {
|
fn type_name(&self) -> &'static str {
|
||||||
@ -254,7 +307,7 @@ pub fn format_primitive(primitive: &Primitive, field_name: Option<&String>) -> S
|
|||||||
_ => byte.format(1),
|
_ => byte.format(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Primitive::Duration(sec) => format_duration(*sec),
|
Primitive::Duration(duration) => format_duration(duration),
|
||||||
Primitive::Int(i) => i.to_string(),
|
Primitive::Int(i) => i.to_string(),
|
||||||
Primitive::Decimal(decimal) => format!("{:.4}", decimal),
|
Primitive::Decimal(decimal) => format!("{:.4}", decimal),
|
||||||
Primitive::Range(range) => format!(
|
Primitive::Range(range) => format!(
|
||||||
@ -297,18 +350,49 @@ pub fn format_primitive(primitive: &Primitive, field_name: Option<&String>) -> S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format a duration in seconds into a string
|
/// Format a duration in nanoseconds into a string
|
||||||
pub fn format_duration(sec: i64) -> String {
|
pub fn format_duration(duration: &BigInt) -> String {
|
||||||
let (minutes, seconds) = (sec / 60, sec % 60);
|
// FIXME: This involves a lot of allocation, but it seems inevitable with BigInt.
|
||||||
let (hours, minutes) = (minutes / 60, minutes % 60);
|
let big_int_1000 = BigInt::from(1000);
|
||||||
let (days, hours) = (hours / 24, hours % 24);
|
let big_int_60 = BigInt::from(60);
|
||||||
|
let big_int_24 = BigInt::from(24);
|
||||||
match (days, hours, minutes, seconds) {
|
// We only want the biggest subvidision to have the negative sign.
|
||||||
(0, 0, 0, 1) => "1 sec".to_owned(),
|
let (sign, duration) = if duration.is_zero() || duration.is_positive() {
|
||||||
(0, 0, 0, s) => format!("{} secs", s),
|
(1, duration.clone())
|
||||||
(0, 0, m, s) => format!("{}:{:02}", m, s),
|
} else {
|
||||||
(0, h, m, s) => format!("{}:{:02}:{:02}", h, m, s),
|
(-1, -duration)
|
||||||
(d, h, m, s) => format!("{}:{:02}:{:02}:{:02}", d, h, m, s),
|
};
|
||||||
|
let (micros, nanos): (BigInt, BigInt) = duration.div_rem(&big_int_1000);
|
||||||
|
let (millis, micros): (BigInt, BigInt) = micros.div_rem(&big_int_1000);
|
||||||
|
let (secs, millis): (BigInt, BigInt) = millis.div_rem(&big_int_1000);
|
||||||
|
let (mins, secs): (BigInt, BigInt) = secs.div_rem(&big_int_60);
|
||||||
|
let (hours, mins): (BigInt, BigInt) = mins.div_rem(&big_int_60);
|
||||||
|
let (days, hours): (BigInt, BigInt) = hours.div_rem(&big_int_24);
|
||||||
|
let decimals = if millis.is_zero() && micros.is_zero() && nanos.is_zero() {
|
||||||
|
String::from("0")
|
||||||
|
} else {
|
||||||
|
format!("{:03}{:03}{:03}", millis, micros, nanos)
|
||||||
|
.trim_end_matches('0')
|
||||||
|
.to_string()
|
||||||
|
};
|
||||||
|
match (
|
||||||
|
days.is_zero(),
|
||||||
|
hours.is_zero(),
|
||||||
|
mins.is_zero(),
|
||||||
|
secs.is_zero(),
|
||||||
|
) {
|
||||||
|
(true, true, true, true) => format!("{}.{}", if sign == 1 { "0" } else { "-0" }, decimals),
|
||||||
|
(true, true, true, _) => format!("{}.{}", sign * secs, decimals),
|
||||||
|
(true, true, _, _) => format!("{}:{:02}.{}", sign * mins, secs, decimals),
|
||||||
|
(true, _, _, _) => format!("{}:{:02}:{:02}.{}", sign * hours, mins, secs, decimals),
|
||||||
|
_ => format!(
|
||||||
|
"{}:{:02}:{:02}:{:02}.{}",
|
||||||
|
sign * days,
|
||||||
|
hours,
|
||||||
|
mins,
|
||||||
|
secs,
|
||||||
|
decimals
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,8 +350,11 @@ pub fn value_to_json_value(v: &Value) -> Result<serde_json::Value, ShellError> {
|
|||||||
UntaggedValue::Primitive(Primitive::Bytes(b)) => serde_json::Value::Number(
|
UntaggedValue::Primitive(Primitive::Bytes(b)) => serde_json::Value::Number(
|
||||||
serde_json::Number::from(b.to_u64().expect("What about really big numbers")),
|
serde_json::Number::from(b.to_u64().expect("What about really big numbers")),
|
||||||
),
|
),
|
||||||
UntaggedValue::Primitive(Primitive::Duration(secs)) => {
|
UntaggedValue::Primitive(Primitive::Duration(i)) => {
|
||||||
serde_json::Value::Number(serde_json::Number::from(*secs))
|
serde_json::Value::Number(serde_json::Number::from(CoerceInto::<i64>::coerce_into(
|
||||||
|
i.tagged(&v.tag),
|
||||||
|
"converting to JSON number",
|
||||||
|
)?))
|
||||||
}
|
}
|
||||||
UntaggedValue::Primitive(Primitive::Date(d)) => serde_json::Value::String(d.to_string()),
|
UntaggedValue::Primitive(Primitive::Date(d)) => serde_json::Value::String(d.to_string()),
|
||||||
UntaggedValue::Primitive(Primitive::EndOfStream) => serde_json::Value::Null,
|
UntaggedValue::Primitive(Primitive::EndOfStream) => serde_json::Value::Null,
|
||||||
|
@ -18,6 +18,7 @@ nu-source = {path = "../nu-source", version = "0.16.1"}
|
|||||||
battery = "0.7.5"
|
battery = "0.7.5"
|
||||||
futures = {version = "0.3", features = ["compat", "io-compat"]}
|
futures = {version = "0.3", features = ["compat", "io-compat"]}
|
||||||
futures-util = "0.3.5"
|
futures-util = "0.3.5"
|
||||||
|
num-bigint = "0.2.6"
|
||||||
|
|
||||||
[dependencies.heim]
|
[dependencies.heim]
|
||||||
default-features = false
|
default-features = false
|
||||||
|
@ -4,6 +4,7 @@ use heim::{disk, host, memory, net, sensors};
|
|||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{TaggedDictBuilder, UntaggedValue, Value};
|
use nu_protocol::{TaggedDictBuilder, UntaggedValue, Value};
|
||||||
use nu_source::Tag;
|
use nu_source::Tag;
|
||||||
|
use num_bigint::BigInt;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -98,9 +99,9 @@ async fn host(tag: Tag) -> Result<Value, ShellError> {
|
|||||||
|
|
||||||
// Uptime
|
// Uptime
|
||||||
if let Ok(uptime) = uptime_result {
|
if let Ok(uptime) = uptime_result {
|
||||||
let uptime = uptime.get::<time::second>().round() as i64;
|
let uptime = uptime.get::<time::nanosecond>().round() as i64;
|
||||||
|
|
||||||
dict.insert_untagged("uptime", UntaggedValue::duration(uptime));
|
dict.insert_untagged("uptime", UntaggedValue::duration(BigInt::from(uptime)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sessions
|
// Sessions
|
||||||
|
Loading…
Reference in New Issue
Block a user