mirror of
https://github.com/nushell/nushell
synced 2024-12-28 14:03:09 +00:00
Switch duration back to bigint (#3554)
This commit is contained in:
parent
7d78f40bf6
commit
4bca36f479
7 changed files with 66 additions and 35 deletions
|
@ -77,7 +77,7 @@ pub enum CompareValues {
|
||||||
Decimals(BigDecimal, BigDecimal),
|
Decimals(BigDecimal, BigDecimal),
|
||||||
String(String, String),
|
String(String, String),
|
||||||
Date(DateTime<FixedOffset>, DateTime<FixedOffset>),
|
Date(DateTime<FixedOffset>, DateTime<FixedOffset>),
|
||||||
DateDuration(DateTime<FixedOffset>, i64),
|
DateDuration(DateTime<FixedOffset>, BigInt),
|
||||||
Booleans(bool, bool),
|
Booleans(bool, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,8 +92,10 @@ impl CompareValues {
|
||||||
CompareValues::Date(left, right) => left.cmp(right),
|
CompareValues::Date(left, right) => left.cmp(right),
|
||||||
CompareValues::DateDuration(left, right) => {
|
CompareValues::DateDuration(left, right) => {
|
||||||
// FIXME: Not sure if I could do something better with the Span.
|
// FIXME: Not sure if I could do something better with the Span.
|
||||||
let duration =
|
let duration = Primitive::into_chrono_duration(
|
||||||
Primitive::into_chrono_duration(Primitive::Duration(*right), Span::unknown())
|
Primitive::Duration(right.clone()),
|
||||||
|
Span::unknown(),
|
||||||
|
)
|
||||||
.expect("Could not convert nushell Duration into chrono Duration.");
|
.expect("Could not convert nushell Duration into chrono Duration.");
|
||||||
let right: DateTime<FixedOffset> = Utc::now()
|
let right: DateTime<FixedOffset> = Utc::now()
|
||||||
.checked_sub_signed(duration)
|
.checked_sub_signed(duration)
|
||||||
|
@ -158,7 +160,7 @@ pub fn coerce_compare_primitive(
|
||||||
(Nothing, Nothing) => CompareValues::Booleans(true, true),
|
(Nothing, Nothing) => CompareValues::Booleans(true, true),
|
||||||
(String(left), String(right)) => CompareValues::String(left.clone(), right.clone()),
|
(String(left), String(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),
|
||||||
(Boolean(left), Nothing) => CompareValues::Booleans(*left, false),
|
(Boolean(left), Nothing) => CompareValues::Booleans(*left, false),
|
||||||
(Nothing, Boolean(right)) => CompareValues::Booleans(false, *right),
|
(Nothing, Boolean(right)) => CompareValues::Booleans(false, *right),
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub enum InlineShape {
|
||||||
GlobPattern(String),
|
GlobPattern(String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Date(DateTime<FixedOffset>),
|
Date(DateTime<FixedOffset>),
|
||||||
Duration(i64),
|
Duration(BigInt),
|
||||||
FilePath(PathBuf),
|
FilePath(PathBuf),
|
||||||
Binary(usize),
|
Binary(usize),
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ impl InlineShape {
|
||||||
Primitive::GlobPattern(pattern) => InlineShape::GlobPattern(pattern.clone()),
|
Primitive::GlobPattern(pattern) => InlineShape::GlobPattern(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::FilePath(path) => InlineShape::FilePath(path.clone()),
|
Primitive::FilePath(path) => InlineShape::FilePath(path.clone()),
|
||||||
Primitive::Binary(b) => InlineShape::Binary(b.len()),
|
Primitive::Binary(b) => InlineShape::Binary(b.len()),
|
||||||
Primitive::BeginningOfStream => InlineShape::BeginningOfStream,
|
Primitive::BeginningOfStream => InlineShape::BeginningOfStream,
|
||||||
|
@ -304,9 +304,10 @@ impl PrettyDebug for FormatInlineShape {
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
),
|
),
|
||||||
InlineShape::Date(date) => DbgDocBldr::primitive(nu_protocol::format_date(date)),
|
InlineShape::Date(date) => DbgDocBldr::primitive(nu_protocol::format_date(date)),
|
||||||
InlineShape::Duration(duration) => {
|
InlineShape::Duration(duration) => DbgDocBldr::description(format_primitive(
|
||||||
DbgDocBldr::description(format_primitive(&Primitive::Duration(*duration), None))
|
&Primitive::Duration(duration.clone()),
|
||||||
}
|
None,
|
||||||
|
)),
|
||||||
InlineShape::FilePath(path) => DbgDocBldr::primitive(path.display()),
|
InlineShape::FilePath(path) => DbgDocBldr::primitive(path.display()),
|
||||||
InlineShape::Binary(length) => {
|
InlineShape::Binary(length) => {
|
||||||
DbgDocBldr::opaque(format!("<binary: {} bytes>", length))
|
DbgDocBldr::opaque(format!("<binary: {} bytes>", length))
|
||||||
|
|
|
@ -35,7 +35,7 @@ impl FromValue for Tagged<num_bigint::BigInt> {
|
||||||
Value {
|
Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::Duration(i)),
|
value: UntaggedValue::Primitive(Primitive::Duration(i)),
|
||||||
..
|
..
|
||||||
} => Ok(BigInt::from(*i).tagged(tag)),
|
} => Ok(i.clone().tagged(tag)),
|
||||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||||
"Can't convert to integer",
|
"Can't convert to integer",
|
||||||
"can't convert to integer",
|
"can't convert to integer",
|
||||||
|
@ -59,7 +59,7 @@ impl FromValue for num_bigint::BigInt {
|
||||||
Value {
|
Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::Duration(i)),
|
value: UntaggedValue::Primitive(Primitive::Duration(i)),
|
||||||
..
|
..
|
||||||
} => Ok(BigInt::from(*i)),
|
} => Ok(i.clone()),
|
||||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||||
"Can't convert to integer",
|
"Can't convert to integer",
|
||||||
"can't convert to integer",
|
"can't convert to integer",
|
||||||
|
|
|
@ -646,8 +646,8 @@ pub fn filesize(size_in_bytes: Number) -> UntaggedValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn duration(nanos: i64) -> UntaggedValue {
|
pub fn duration(nanos: impl Into<BigInt>) -> UntaggedValue {
|
||||||
UntaggedValue::Primitive(Primitive::Duration(nanos))
|
UntaggedValue::Primitive(Primitive::Duration(nanos.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)]
|
||||||
|
|
|
@ -250,8 +250,8 @@ impl UntaggedValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper for creating date duration values
|
/// Helper for creating date duration values
|
||||||
pub fn duration(nanos: i64) -> UntaggedValue {
|
pub fn duration(nanos: impl Into<BigInt>) -> UntaggedValue {
|
||||||
UntaggedValue::Primitive(Primitive::Duration(nanos))
|
UntaggedValue::Primitive(Primitive::Duration(nanos.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper for creating datatime values
|
/// Helper for creating datatime values
|
||||||
|
@ -369,10 +369,10 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// View the Value as a Duration (i64), if possible
|
/// View the Value as a Duration (BigInt), if possible
|
||||||
pub fn as_duration(&self) -> Result<i64, ShellError> {
|
pub fn as_duration(&self) -> Result<BigInt, ShellError> {
|
||||||
match &self.value {
|
match &self.value {
|
||||||
UntaggedValue::Primitive(Primitive::Duration(dur)) => Ok(*dur),
|
UntaggedValue::Primitive(Primitive::Duration(dur)) => Ok(dur.clone()),
|
||||||
_ => Err(ShellError::type_error("bigint", self.spanned_type_name())),
|
_ => Err(ShellError::type_error("bigint", self.spanned_type_name())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -836,7 +836,10 @@ impl I64Ext for i64 {
|
||||||
|
|
||||||
fn to_duration_value(&self, the_tag: Tag) -> Value {
|
fn to_duration_value(&self, the_tag: Tag) -> Value {
|
||||||
Value {
|
Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::Duration(*self)),
|
value: UntaggedValue::Primitive(Primitive::Duration(
|
||||||
|
BigInt::from_i64(*self)
|
||||||
|
.expect("Internal error: conversion to big int should not fail"),
|
||||||
|
)),
|
||||||
tag: the_tag,
|
tag: the_tag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub enum Primitive {
|
||||||
/// A date value
|
/// A date value
|
||||||
Date(DateTime<FixedOffset>),
|
Date(DateTime<FixedOffset>),
|
||||||
/// A count in the number of nanoseconds
|
/// A count in the number of nanoseconds
|
||||||
Duration(i64),
|
Duration(BigInt),
|
||||||
/// A range of values
|
/// A range of values
|
||||||
Range(Box<Range>),
|
Range(Box<Range>),
|
||||||
/// A file path
|
/// A file path
|
||||||
|
@ -171,7 +171,13 @@ impl Primitive {
|
||||||
"converting a decimal into a signed 64-bit integer",
|
"converting a decimal into a signed 64-bit integer",
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
Primitive::Duration(duration) => Ok(*duration),
|
Primitive::Duration(duration) => duration.to_i64().ok_or_else(|| {
|
||||||
|
ShellError::range_error(
|
||||||
|
ExpectedRange::I64,
|
||||||
|
&format!("{}", duration).spanned(span),
|
||||||
|
"converting a duration into a signed 64-bit integer",
|
||||||
|
)
|
||||||
|
}),
|
||||||
other => Err(ShellError::type_error(
|
other => Err(ShellError::type_error(
|
||||||
"number",
|
"number",
|
||||||
other.type_name().spanned(span),
|
other.type_name().spanned(span),
|
||||||
|
@ -277,7 +283,10 @@ impl Primitive {
|
||||||
match self {
|
match self {
|
||||||
Primitive::Duration(duration) => {
|
Primitive::Duration(duration) => {
|
||||||
// Divide into seconds because BigInt can be larger than i64
|
// Divide into seconds because BigInt can be larger than i64
|
||||||
let (secs, nanos) = duration.div_rem(&(NANOS_PER_SEC as i64));
|
let (secs, nanos) = duration.div_rem(
|
||||||
|
&BigInt::from_u32(NANOS_PER_SEC)
|
||||||
|
.expect("Internal error: conversion from u32 failed"),
|
||||||
|
);
|
||||||
let secs = match secs.to_i64() {
|
let secs = match secs.to_i64() {
|
||||||
Some(secs) => secs,
|
Some(secs) => secs,
|
||||||
None => {
|
None => {
|
||||||
|
@ -396,7 +405,10 @@ impl From<chrono::Duration> for Primitive {
|
||||||
.expect("Unexpected overflow")
|
.expect("Unexpected overflow")
|
||||||
.num_nanoseconds()
|
.num_nanoseconds()
|
||||||
.expect("Unexpected overflow") as u32;
|
.expect("Unexpected overflow") as u32;
|
||||||
Primitive::Duration(secs * NANOS_PER_SEC as i64 + nanos as i64)
|
Primitive::Duration(
|
||||||
|
BigInt::from_i64(secs * NANOS_PER_SEC as i64 + nanos as i64)
|
||||||
|
.expect("Internal error: can't convert from i64"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,20 +525,24 @@ pub fn format_primitive(primitive: &Primitive, field_name: Option<&String>) -> S
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format a duration in nanoseconds into a string
|
/// Format a duration in nanoseconds into a string
|
||||||
pub fn format_duration(duration: &i64) -> String {
|
pub fn format_duration(duration: &BigInt) -> String {
|
||||||
let is_zero = duration.is_zero();
|
let is_zero = duration.is_zero();
|
||||||
|
// FIXME: This involves a lot of allocation, but it seems inevitable with BigInt.
|
||||||
|
let big_int_1000 = BigInt::from(1000);
|
||||||
|
let big_int_60 = BigInt::from(60);
|
||||||
|
let big_int_24 = BigInt::from(24);
|
||||||
// We only want the biggest subdivision to have the negative sign.
|
// We only want the biggest subdivision to have the negative sign.
|
||||||
let (sign, duration) = if duration.is_zero() || duration.is_positive() {
|
let (sign, duration) = if duration.is_zero() || duration.is_positive() {
|
||||||
(1, *duration)
|
(1, duration.clone())
|
||||||
} else {
|
} else {
|
||||||
(-1, -duration)
|
(-1, -duration)
|
||||||
};
|
};
|
||||||
let (micros, nanos): (i64, i64) = duration.div_rem(&1000);
|
let (micros, nanos): (BigInt, BigInt) = duration.div_rem(&big_int_1000);
|
||||||
let (millis, micros): (i64, i64) = micros.div_rem(&1000);
|
let (millis, micros): (BigInt, BigInt) = micros.div_rem(&big_int_1000);
|
||||||
let (secs, millis): (i64, i64) = millis.div_rem(&1000);
|
let (secs, millis): (BigInt, BigInt) = millis.div_rem(&big_int_1000);
|
||||||
let (mins, secs): (i64, i64) = secs.div_rem(&60);
|
let (mins, secs): (BigInt, BigInt) = secs.div_rem(&big_int_60);
|
||||||
let (hours, mins): (i64, i64) = mins.div_rem(&60);
|
let (hours, mins): (BigInt, BigInt) = mins.div_rem(&big_int_60);
|
||||||
let (days, hours): (i64, i64) = hours.div_rem(&24);
|
let (days, hours): (BigInt, BigInt) = hours.div_rem(&big_int_24);
|
||||||
|
|
||||||
let mut output_prep = vec![];
|
let mut output_prep = vec![];
|
||||||
|
|
||||||
|
|
|
@ -349,9 +349,18 @@ pub fn value_to_json_value(v: &Value) -> Result<serde_json::Value, ShellError> {
|
||||||
UntaggedValue::Primitive(Primitive::Filesize(b)) => serde_json::Value::Number(
|
UntaggedValue::Primitive(Primitive::Filesize(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(i)) => {
|
UntaggedValue::Primitive(Primitive::Duration(i)) => serde_json::Value::Number(
|
||||||
serde_json::Value::Number(serde_json::Number::from(*i))
|
serde_json::Number::from_f64(
|
||||||
}
|
i.to_f64().expect("TODO: What about really big decimals?"),
|
||||||
|
)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
ShellError::labeled_error(
|
||||||
|
"Can not convert big decimal to f64",
|
||||||
|
"cannot convert big decimal to f64",
|
||||||
|
&v.tag,
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
),
|
||||||
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,
|
||||||
UntaggedValue::Primitive(Primitive::BeginningOfStream) => serde_json::Value::Null,
|
UntaggedValue::Primitive(Primitive::BeginningOfStream) => serde_json::Value::Null,
|
||||||
|
|
Loading…
Reference in a new issue