nushell/crates/nu-protocol/src/value/mod.rs

1377 lines
48 KiB
Rust
Raw Normal View History

mod custom_value;
mod from;
2021-09-08 04:26:57 +02:00
mod range;
mod stream;
2021-10-05 04:27:39 +02:00
mod unit;
2021-09-08 04:26:57 +02:00
2021-10-05 04:27:39 +02:00
use chrono::{DateTime, FixedOffset};
use chrono_humanize::HumanTime;
use indexmap::map::IndexMap;
2021-09-08 04:26:57 +02:00
pub use range::*;
2021-10-01 07:11:49 +02:00
use serde::{Deserialize, Serialize};
2021-09-08 04:26:57 +02:00
pub use stream::*;
2021-10-05 04:27:39 +02:00
pub use unit::*;
2021-09-08 04:26:57 +02:00
use std::collections::HashMap;
use std::{cmp::Ordering, fmt::Debug};
2021-08-16 00:33:34 +02:00
2021-10-09 12:29:59 +02:00
use crate::ast::{CellPath, PathMember};
use crate::{did_you_mean, span, BlockId, Config, Span, Spanned, Type};
2021-08-16 00:33:34 +02:00
use crate::ast::Operator;
pub use custom_value::CustomValue;
2021-08-16 00:33:34 +02:00
use crate::ShellError;
2021-09-08 04:26:57 +02:00
/// Core structured values that pass through the pipeline in engine-q
#[derive(Debug, Serialize, Deserialize)]
2021-08-16 00:33:34 +02:00
pub enum Value {
2021-08-28 21:17:30 +02:00
Bool {
val: bool,
span: Span,
},
Int {
val: i64,
span: Span,
},
2021-10-01 08:53:47 +02:00
Filesize {
2021-10-05 04:27:39 +02:00
val: i64,
2021-10-01 08:53:47 +02:00
span: Span,
},
2021-10-01 23:53:13 +02:00
Duration {
2021-10-05 04:27:39 +02:00
val: i64,
span: Span,
},
Date {
val: DateTime<FixedOffset>,
2021-10-01 23:53:13 +02:00
span: Span,
},
Range {
val: Box<Range>,
span: Span,
},
2021-08-28 21:17:30 +02:00
Float {
val: f64,
span: Span,
},
String {
val: String,
span: Span,
},
Record {
cols: Vec<String>,
vals: Vec<Value>,
2021-09-04 08:52:28 +02:00
span: Span,
},
2021-08-28 21:17:30 +02:00
List {
vals: Vec<Value>,
2021-08-28 21:17:30 +02:00
span: Span,
},
Block {
val: BlockId,
span: Span,
},
Nothing {
span: Span,
},
2021-09-06 01:16:27 +02:00
Error {
2021-09-06 06:07:48 +02:00
error: ShellError,
2021-09-06 01:16:27 +02:00
},
2021-09-23 18:42:03 +02:00
Binary {
val: Vec<u8>,
span: Span,
},
2021-10-02 04:59:11 +02:00
CellPath {
val: CellPath,
span: Span,
},
CustomValue {
val: Box<dyn CustomValue>,
span: Span,
},
}
impl Clone for Value {
fn clone(&self) -> Self {
match self {
Value::Bool { val, span } => Value::Bool {
val: *val,
span: *span,
},
Value::Int { val, span } => Value::Int {
val: *val,
span: *span,
},
Value::Filesize { val, span } => Value::Filesize {
val: *val,
span: *span,
},
Value::Duration { val, span } => Value::Duration {
val: *val,
span: *span,
},
Value::Date { val, span } => Value::Date {
val: *val,
span: *span,
},
Value::Range { val, span } => Value::Range {
val: val.clone(),
span: *span,
},
Value::Float { val, span } => Value::Float {
val: *val,
span: *span,
},
Value::String { val, span } => Value::String {
val: val.clone(),
span: *span,
},
Value::Record { cols, vals, span } => Value::Record {
cols: cols.clone(),
vals: vals.clone(),
span: *span,
},
Value::List { vals, span } => Value::List {
vals: vals.clone(),
span: *span,
},
Value::Block { val, span } => Value::Block {
val: *val,
span: *span,
},
Value::Nothing { span } => Value::Nothing { span: *span },
Value::Error { error } => Value::Error {
error: error.clone(),
},
Value::Binary { val, span } => Value::Binary {
val: val.clone(),
span: *span,
},
Value::CellPath { val, span } => Value::CellPath {
val: val.clone(),
span: *span,
},
Value::CustomValue { val, span } => val.clone_value(*span),
}
}
2021-08-16 00:33:34 +02:00
}
impl Value {
pub fn as_string(&self) -> Result<String, ShellError> {
match self {
Value::String { val, .. } => Ok(val.to_string()),
2021-11-09 05:46:26 +01:00
x => Err(ShellError::CantConvert(
"string".into(),
x.get_type().to_string(),
self.span()?,
)),
2021-08-16 00:33:34 +02:00
}
}
2021-11-11 00:14:00 +01:00
pub fn as_block(&self) -> Result<BlockId, ShellError> {
match self {
Value::Block { val, .. } => Ok(*val),
x => Err(ShellError::CantConvert(
"block".into(),
x.get_type().to_string(),
self.span()?,
)),
}
}
pub fn as_record(&self) -> Result<(&[String], &[Value]), ShellError> {
match self {
Value::Record { cols, vals, .. } => Ok((cols, vals)),
x => Err(ShellError::CantConvert(
"record".into(),
x.get_type().to_string(),
self.span()?,
)),
}
}
pub fn as_bool(&self) -> Result<bool, ShellError> {
match self {
Value::Bool { val, .. } => Ok(*val),
x => Err(ShellError::CantConvert(
"boolean".into(),
x.get_type().to_string(),
self.span()?,
)),
}
}
2021-09-08 04:26:57 +02:00
/// Get the span for the current value
2021-10-11 20:45:31 +02:00
pub fn span(&self) -> Result<Span, ShellError> {
2021-08-16 00:33:34 +02:00
match self {
2021-10-11 20:45:31 +02:00
Value::Error { error } => Err(error.clone()),
Value::Bool { span, .. } => Ok(*span),
Value::Int { span, .. } => Ok(*span),
Value::Float { span, .. } => Ok(*span),
Value::Filesize { span, .. } => Ok(*span),
Value::Duration { span, .. } => Ok(*span),
Value::Date { span, .. } => Ok(*span),
Value::Range { span, .. } => Ok(*span),
Value::String { span, .. } => Ok(*span),
Value::Record { span, .. } => Ok(*span),
Value::List { span, .. } => Ok(*span),
Value::Block { span, .. } => Ok(*span),
Value::Nothing { span, .. } => Ok(*span),
Value::Binary { span, .. } => Ok(*span),
Value::CellPath { span, .. } => Ok(*span),
Value::CustomValue { span, .. } => Ok(*span),
2021-08-16 00:33:34 +02:00
}
}
2021-09-08 04:26:57 +02:00
/// Update the value with a new span
2021-08-16 00:33:34 +02:00
pub fn with_span(mut self, new_span: Span) -> Value {
match &mut self {
Value::Bool { span, .. } => *span = new_span,
Value::Int { span, .. } => *span = new_span,
Value::Float { span, .. } => *span = new_span,
2021-10-01 08:53:47 +02:00
Value::Filesize { span, .. } => *span = new_span,
2021-10-01 23:53:13 +02:00
Value::Duration { span, .. } => *span = new_span,
2021-10-05 04:27:39 +02:00
Value::Date { span, .. } => *span = new_span,
Value::Range { span, .. } => *span = new_span,
2021-08-16 00:33:34 +02:00
Value::String { span, .. } => *span = new_span,
Value::Record { span, .. } => *span = new_span,
2021-08-16 00:33:34 +02:00
Value::List { span, .. } => *span = new_span,
Value::Block { span, .. } => *span = new_span,
Value::Nothing { span, .. } => *span = new_span,
2021-09-06 01:16:27 +02:00
Value::Error { .. } => {}
2021-09-23 18:42:03 +02:00
Value::Binary { span, .. } => *span = new_span,
2021-10-02 04:59:11 +02:00
Value::CellPath { span, .. } => *span = new_span,
Value::CustomValue { span, .. } => *span = new_span,
2021-08-16 00:33:34 +02:00
}
self
}
2021-09-08 04:26:57 +02:00
/// Get the type of the current Value
2021-08-16 00:33:34 +02:00
pub fn get_type(&self) -> Type {
match self {
Value::Bool { .. } => Type::Bool,
Value::Int { .. } => Type::Int,
Value::Float { .. } => Type::Float,
2021-10-01 08:53:47 +02:00
Value::Filesize { .. } => Type::Filesize,
2021-10-01 23:53:13 +02:00
Value::Duration { .. } => Type::Duration,
2021-10-05 04:27:39 +02:00
Value::Date { .. } => Type::Date,
Value::Range { .. } => Type::Range,
2021-08-16 00:33:34 +02:00
Value::String { .. } => Type::String,
2021-11-19 05:30:27 +01:00
Value::Record { cols, vals, .. } => Type::Record(
cols.iter()
.zip(vals.iter())
.map(|(x, y)| (x.clone(), y.get_type()))
.collect(),
),
2021-08-16 00:33:34 +02:00
Value::List { .. } => Type::List(Box::new(Type::Unknown)), // FIXME
Value::Nothing { .. } => Type::Nothing,
Value::Block { .. } => Type::Block,
2021-09-06 01:16:27 +02:00
Value::Error { .. } => Type::Error,
2021-09-23 18:42:03 +02:00
Value::Binary { .. } => Type::Binary,
2021-10-02 04:59:11 +02:00
Value::CellPath { .. } => Type::CellPath,
Value::CustomValue { .. } => Type::Custom,
2021-08-16 00:33:34 +02:00
}
}
2021-09-01 23:20:53 +02:00
2021-09-08 04:26:57 +02:00
/// Convert Value into string. Note that Streams will be consumed.
pub fn into_string(self, separator: &str, config: &Config) -> String {
2021-09-01 23:20:53 +02:00
match self {
Value::Bool { val, .. } => val.to_string(),
Value::Int { val, .. } => val.to_string(),
Value::Float { val, .. } => val.to_string(),
Value::Filesize { val, .. } => format_filesize(val, config),
2021-10-05 04:27:39 +02:00
Value::Duration { val, .. } => format_duration(val),
Value::Date { val, .. } => HumanTime::from(val).to_string(),
2021-10-26 00:24:10 +02:00
Value::Range { val, .. } => {
2021-11-09 05:46:26 +01:00
format!(
"{}..{}",
val.from.into_string(", ", config),
val.to.into_string(", ", config)
2021-11-09 05:46:26 +01:00
)
2021-10-26 00:24:10 +02:00
}
2021-09-01 23:20:53 +02:00
Value::String { val, .. } => val,
Value::List { vals: val, .. } => format!(
2021-09-07 00:02:24 +02:00
"[{}]",
val.into_iter()
.map(|x| x.into_string(", ", config))
2021-09-07 00:02:24 +02:00
.collect::<Vec<_>>()
2021-11-09 05:46:26 +01:00
.join(separator)
2021-09-07 00:02:24 +02:00
),
Value::Record { cols, vals, .. } => format!(
"{{{}}}",
cols.iter()
.zip(vals.iter())
.map(|(x, y)| format!("{}: {}", x, y.clone().into_string(", ", config)))
2021-09-07 00:02:24 +02:00
.collect::<Vec<_>>()
2021-11-09 05:46:26 +01:00
.join(separator)
2021-09-07 00:02:24 +02:00
),
2021-09-01 23:20:53 +02:00
Value::Block { val, .. } => format!("<Block {}>", val),
2021-09-02 03:29:43 +02:00
Value::Nothing { .. } => String::new(),
2021-09-06 06:07:48 +02:00
Value::Error { error } => format!("{:?}", error),
2021-09-23 18:42:03 +02:00
Value::Binary { val, .. } => format!("{:?}", val),
2021-10-02 06:55:05 +02:00
Value::CellPath { val, .. } => val.into_string(),
Value::CustomValue { val, .. } => val.value_string(),
2021-10-01 08:01:22 +02:00
}
}
2021-11-09 05:46:26 +01:00
/// Convert Value into string. Note that Streams will be consumed.
pub fn debug_string(self, separator: &str, config: &Config) -> String {
2021-10-01 08:01:22 +02:00
match self {
Value::Bool { val, .. } => val.to_string(),
Value::Int { val, .. } => val.to_string(),
Value::Float { val, .. } => val.to_string(),
Value::Filesize { val, .. } => format_filesize(val, config),
2021-11-04 03:32:35 +01:00
Value::Duration { val, .. } => format_duration(val),
2021-10-05 04:27:39 +02:00
Value::Date { val, .. } => format!("{:?}", val),
2021-10-26 00:24:10 +02:00
Value::Range { val, .. } => {
2021-11-09 05:46:26 +01:00
format!(
"{}..{}",
val.from.into_string(", ", config),
val.to.into_string(", ", config)
2021-11-09 05:46:26 +01:00
)
2021-10-26 00:24:10 +02:00
}
2021-10-01 08:01:22 +02:00
Value::String { val, .. } => val,
2021-11-09 05:46:26 +01:00
Value::List { vals: val, .. } => format!(
"[{}]",
val.into_iter()
.map(|x| x.into_string(", ", config))
2021-11-09 05:46:26 +01:00
.collect::<Vec<_>>()
.join(separator)
),
Value::Record { cols, vals, .. } => format!(
"{{{}}}",
cols.iter()
.zip(vals.iter())
.map(|(x, y)| format!("{}: {}", x, y.clone().into_string(", ", config)))
2021-11-09 05:46:26 +01:00
.collect::<Vec<_>>()
.join(separator)
),
2021-10-01 08:01:22 +02:00
Value::Block { val, .. } => format!("<Block {}>", val),
Value::Nothing { .. } => String::new(),
Value::Error { error } => format!("{:?}", error),
Value::Binary { val, .. } => format!("{:?}", val),
2021-11-09 05:46:26 +01:00
Value::CellPath { val, .. } => val.into_string(),
Value::CustomValue { val, .. } => val.value_string(),
2021-09-01 23:20:53 +02:00
}
}
2021-09-03 04:15:01 +02:00
2021-09-08 04:26:57 +02:00
/// Create a new `Nothing` value
pub fn nothing(span: Span) -> Value {
Value::Nothing { span }
2021-09-03 04:15:01 +02:00
}
2021-09-07 00:02:24 +02:00
/// Follow a given column path into the value: for example accessing nth elements in a stream or list
2021-09-26 20:39:19 +02:00
pub fn follow_cell_path(self, cell_path: &[PathMember]) -> Result<Value, ShellError> {
2021-09-07 00:02:24 +02:00
let mut current = self;
2021-09-26 20:39:19 +02:00
for member in cell_path {
2021-09-07 00:02:24 +02:00
// FIXME: this uses a few extra clones for simplicity, but there may be a way
// to traverse the path without them
match member {
PathMember::Int {
val: count,
span: origin_span,
} => {
// Treat a numeric path member as `nth <val>`
match &mut current {
Value::List { vals: val, .. } => {
2021-09-07 00:02:24 +02:00
if let Some(item) = val.get(*count) {
current = item.clone();
} else {
return Err(ShellError::AccessBeyondEnd(val.len(), *origin_span));
}
}
Value::Binary { val, .. } => {
if let Some(item) = val.get(*count) {
current = Value::Int {
val: *item as i64,
span: *origin_span,
};
} else {
return Err(ShellError::AccessBeyondEnd(val.len(), *origin_span));
}
}
Value::Range { val, .. } => {
if let Some(item) = val.clone().into_range_iter()?.nth(*count) {
current = item.clone();
} else {
return Err(ShellError::AccessBeyondEndOfStream(*origin_span));
}
}
Value::CustomValue { val, .. } => {
current = val.follow_path_int(*count, *origin_span)?;
}
2021-09-07 00:02:24 +02:00
x => {
return Err(ShellError::IncompatiblePathAccess(
format!("{}", x.get_type()),
*origin_span,
))
}
}
}
PathMember::String {
val: column_name,
2021-09-07 00:02:24 +02:00
span: origin_span,
} => match &mut current {
Value::Record { cols, vals, span } => {
2021-11-07 22:48:50 +01:00
let cols = cols.clone();
let span = *span;
2021-09-07 00:02:24 +02:00
2021-11-07 22:48:50 +01:00
if let Some(found) =
cols.iter().zip(vals.iter()).find(|x| x.0 == column_name)
{
current = found.1.clone();
} else if let Some(suggestion) = did_you_mean(&cols, column_name) {
return Err(ShellError::DidYouMean(suggestion, *origin_span));
} else {
return Err(ShellError::CantFindColumn(*origin_span, span));
2021-09-07 00:02:24 +02:00
}
}
Value::List { vals, span } => {
let mut output = vec![];
for val in vals {
output.push(val.clone().follow_cell_path(&[PathMember::String {
val: column_name.clone(),
span: *origin_span,
}])?);
// if let Value::Record { cols, vals, .. } = val {
// for col in cols.iter().enumerate() {
// if col.1 == column_name {
// output.push(vals[col.0].clone());
// }
// }
// }
2021-09-07 09:35:59 +02:00
}
current = Value::List {
vals: output,
span: *span,
};
}
Value::CustomValue { val, .. } => {
current = val.follow_path_string(column_name.clone(), *origin_span)?;
}
2021-09-07 00:02:24 +02:00
x => {
return Err(ShellError::IncompatiblePathAccess(
format!("{}", x.get_type()),
*origin_span,
))
}
},
}
}
Ok(current)
}
2021-09-09 23:47:20 +02:00
2021-11-05 04:59:12 +01:00
/// Follow a given column path into the value: for example accessing nth elements in a stream or list
pub fn update_cell_path(
&mut self,
cell_path: &[PathMember],
callback: Box<dyn FnOnce(&Value) -> Value>,
) -> Result<(), ShellError> {
let orig = self.clone();
let new_val = callback(&orig.follow_cell_path(cell_path)?);
match new_val {
Value::Error { error } => Err(error),
new_val => self.replace_data_at_cell_path(cell_path, new_val),
}
}
pub fn replace_data_at_cell_path(
&mut self,
cell_path: &[PathMember],
new_val: Value,
) -> Result<(), ShellError> {
match cell_path.first() {
Some(path_member) => match path_member {
PathMember::String {
val: col_name,
span,
} => match self {
Value::List { vals, .. } => {
for val in vals.iter_mut() {
match val {
Value::Record { cols, vals, .. } => {
for col in cols.iter().zip(vals) {
if col.0 == col_name {
col.1.replace_data_at_cell_path(
&cell_path[1..],
new_val.clone(),
)?
}
}
}
v => return Err(ShellError::CantFindColumn(*span, v.span()?)),
}
}
}
Value::Record { cols, vals, .. } => {
for col in cols.iter().zip(vals) {
if col.0 == col_name {
col.1
.replace_data_at_cell_path(&cell_path[1..], new_val.clone())?
}
}
}
v => return Err(ShellError::CantFindColumn(*span, v.span()?)),
},
PathMember::Int { val: row_num, span } => match self {
Value::List { vals, .. } => {
if let Some(v) = vals.get_mut(*row_num) {
v.replace_data_at_cell_path(&cell_path[1..], new_val)?
} else {
return Err(ShellError::AccessBeyondEnd(vals.len(), *span));
}
}
v => return Err(ShellError::NotAList(*span, v.span()?)),
},
},
None => {
*self = new_val;
}
}
Ok(())
}
2021-09-09 23:47:20 +02:00
pub fn is_true(&self) -> bool {
matches!(self, Value::Bool { val: true, .. })
}
2021-09-10 04:27:12 +02:00
pub fn columns(&self) -> Vec<String> {
match self {
Value::Record { cols, .. } => cols.clone(),
_ => vec![],
}
}
2021-10-09 08:20:32 +02:00
2021-10-20 07:58:25 +02:00
pub fn string(val: impl Into<String>, span: Span) -> Value {
Value::String {
val: val.into(),
span,
}
}
pub fn int(val: i64, span: Span) -> Value {
Value::Int { val, span }
}
// Only use these for test data. Span::unknown() should not be used in user data
pub fn test_string(s: impl Into<String>) -> Value {
Value::String {
val: s.into(),
span: Span::unknown(),
}
}
// Only use these for test data. Span::unknown() should not be used in user data
pub fn test_int(val: i64) -> Value {
Value::Int {
val,
span: Span::unknown(),
}
}
2021-08-16 00:33:34 +02:00
}
2021-10-25 06:01:02 +02:00
impl Default for Value {
fn default() -> Self {
Value::Nothing {
span: Span::unknown(),
}
2021-10-25 06:01:02 +02:00
}
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
// Compare two floating point numbers. The decision interval for equality is dynamically
// scaled as the value being compared increases in magnitude.
fn compare_floats(val: f64, other: f64) -> Option<Ordering> {
let prec = f64::EPSILON.max(val.abs() * f64::EPSILON);
if (other - val).abs() < prec {
return Some(Ordering::Equal);
}
val.partial_cmp(&other)
}
2021-10-09 15:10:10 +02:00
match (self, other) {
(Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => lhs.partial_cmp(rhs),
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => lhs.partial_cmp(rhs),
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
compare_floats(*lhs, *rhs)
}
2021-10-31 07:54:51 +01:00
(Value::Date { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
lhs.date().to_string().partial_cmp(&rhs.date().to_string())
}
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
lhs.partial_cmp(rhs)
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
compare_floats(*lhs as f64, *rhs)
}
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
compare_floats(*lhs, *rhs as f64)
}
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
lhs.partial_cmp(rhs)
}
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
lhs.partial_cmp(rhs)
}
(Value::Block { val: b1, .. }, Value::Block { val: b2, .. }) if b1 == b2 => {
Some(Ordering::Equal)
}
(Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => lhs.partial_cmp(rhs),
2021-10-09 15:10:10 +02:00
(
Value::Record {
vals: lhs,
cols: lhs_headers,
2021-10-09 15:10:10 +02:00
..
},
Value::Record {
vals: rhs,
cols: rhs_headers,
2021-10-09 15:10:10 +02:00
..
},
) if lhs_headers == rhs_headers && lhs == rhs => Some(Ordering::Equal),
2021-10-11 03:56:19 +02:00
(Value::Binary { val: lhs, .. }, Value::Binary { val: rhs, .. }) => {
lhs.partial_cmp(rhs)
}
2021-11-29 07:50:57 +01:00
(Value::CustomValue { val: lhs, .. }, rhs) => lhs.partial_cmp(rhs),
(Value::Nothing { .. }, Value::Nothing { .. }) => Some(Ordering::Equal),
(_, _) => None,
2021-08-16 00:33:34 +02:00
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(other).map_or(false, Ordering::is_eq)
}
}
2021-08-16 00:33:34 +02:00
impl Value {
pub fn add(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-08-16 00:33:34 +02:00
match (self, rhs) {
2021-10-20 07:58:25 +02:00
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = lhs.checked_add(*rhs) {
Ok(Value::Int { val, span })
} else {
Err(ShellError::OperatorOverflow(
"add operation overflowed".into(),
span,
))
}
}
2021-08-16 00:33:34 +02:00
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
val: *lhs as f64 + *rhs,
span,
}),
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Float {
val: *lhs + *rhs as f64,
span,
}),
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
val: lhs + rhs,
span,
}),
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::String {
val: lhs.to_string() + rhs,
span,
}),
2021-11-09 05:46:26 +01:00
(Value::Date { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
match lhs.checked_add_signed(chrono::Duration::nanoseconds(*rhs)) {
Some(val) => Ok(Value::Date { val, span }),
_ => Err(ShellError::OperatorOverflow(
"addition operation overflowed".into(),
span,
)),
}
}
2021-10-05 04:27:39 +02:00
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
if let Some(val) = lhs.checked_add(*rhs) {
Ok(Value::Duration { val, span })
} else {
Err(ShellError::OperatorOverflow(
"add operation overflowed".into(),
span,
))
}
2021-10-05 04:27:39 +02:00
}
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
if let Some(val) = lhs.checked_add(*rhs) {
Ok(Value::Filesize { val, span })
} else {
Err(ShellError::OperatorOverflow(
"add operation overflowed".into(),
span,
))
}
2021-10-05 04:27:39 +02:00
}
2021-08-16 00:33:34 +02:00
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::Plus, op, rhs)
}
2021-08-16 00:33:34 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-08-16 00:33:34 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-08-16 00:33:34 +02:00
}),
}
}
2021-08-25 21:29:36 +02:00
pub fn sub(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-08-25 21:29:36 +02:00
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = lhs.checked_sub(*rhs) {
Ok(Value::Int { val, span })
} else {
Err(ShellError::OperatorOverflow(
"subtraction operation overflowed".into(),
span,
))
}
}
2021-08-25 21:29:36 +02:00
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
val: *lhs as f64 - *rhs,
span,
}),
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Float {
val: *lhs - *rhs as f64,
span,
}),
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
val: lhs - rhs,
span,
}),
2021-11-09 05:46:26 +01:00
(Value::Date { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
match lhs.checked_sub_signed(chrono::Duration::nanoseconds(*rhs)) {
Some(val) => Ok(Value::Date { val, span }),
_ => Err(ShellError::OperatorOverflow(
"subtraction operation overflowed".into(),
span,
)),
}
}
2021-10-05 04:27:39 +02:00
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
if let Some(val) = lhs.checked_sub(*rhs) {
Ok(Value::Duration { val, span })
} else {
Err(ShellError::OperatorOverflow(
"subtraction operation overflowed".into(),
span,
))
}
2021-10-05 04:27:39 +02:00
}
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
if let Some(val) = lhs.checked_sub(*rhs) {
Ok(Value::Filesize { val, span })
} else {
Err(ShellError::OperatorOverflow(
"add operation overflowed".into(),
span,
))
}
2021-10-05 04:27:39 +02:00
}
2021-08-25 21:29:36 +02:00
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::Minus, op, rhs)
}
2021-08-25 21:29:36 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-08-25 21:29:36 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-08-25 21:29:36 +02:00
}),
}
}
pub fn mul(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-08-25 21:29:36 +02:00
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = lhs.checked_mul(*rhs) {
Ok(Value::Int { val, span })
} else {
Err(ShellError::OperatorOverflow(
"multiply operation overflowed".into(),
span,
))
}
}
2021-08-25 21:29:36 +02:00
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
val: *lhs as f64 * *rhs,
span,
}),
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Float {
val: *lhs * *rhs as f64,
span,
}),
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
val: lhs * rhs,
span,
}),
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::Multiply, op, rhs)
}
2021-08-25 21:29:36 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-08-25 21:29:36 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-08-25 21:29:36 +02:00
}),
}
}
pub fn div(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-08-25 21:29:36 +02:00
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if *rhs != 0 {
if lhs % rhs == 0 {
Ok(Value::Int {
val: lhs / rhs,
span,
})
} else {
Ok(Value::Float {
val: (*lhs as f64) / (*rhs as f64),
span,
})
}
2021-08-25 21:29:36 +02:00
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if *rhs != 0.0 {
Ok(Value::Float {
val: *lhs as f64 / *rhs,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if *rhs != 0 {
Ok(Value::Float {
val: *lhs / *rhs as f64,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if *rhs != 0.0 {
Ok(Value::Float {
val: lhs / rhs,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::Divide, op, rhs)
}
2021-08-25 21:29:36 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-08-25 21:29:36 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-08-25 21:29:36 +02:00
}),
}
}
pub fn lt(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-08-25 21:29:36 +02:00
if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
return lhs.operation(*span, Operator::LessThan, op, rhs);
}
match self.partial_cmp(rhs) {
Some(ordering) => Ok(Value::Bool {
val: matches!(ordering, Ordering::Less),
2021-08-25 21:29:36 +02:00
span,
}),
None => Err(ShellError::OperatorMismatch {
2021-08-25 21:29:36 +02:00
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-08-25 21:29:36 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-08-25 21:29:36 +02:00
}),
}
}
pub fn lte(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-08-25 21:29:36 +02:00
if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
return lhs.operation(*span, Operator::LessThanOrEqual, op, rhs);
}
match self.partial_cmp(rhs) {
Some(ordering) => Ok(Value::Bool {
val: matches!(ordering, Ordering::Less | Ordering::Equal),
2021-08-25 21:29:36 +02:00
span,
}),
None => Err(ShellError::OperatorMismatch {
2021-08-25 21:29:36 +02:00
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-08-25 21:29:36 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-08-25 21:29:36 +02:00
}),
}
}
pub fn gt(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-08-25 21:29:36 +02:00
if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
return lhs.operation(*span, Operator::GreaterThan, op, rhs);
}
match self.partial_cmp(rhs) {
Some(ordering) => Ok(Value::Bool {
val: matches!(ordering, Ordering::Greater),
2021-08-25 21:29:36 +02:00
span,
}),
None => Err(ShellError::OperatorMismatch {
2021-08-25 21:29:36 +02:00
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-08-25 21:29:36 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-08-25 21:29:36 +02:00
}),
}
}
pub fn gte(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-08-25 21:29:36 +02:00
if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
return lhs.operation(*span, Operator::GreaterThanOrEqual, op, rhs);
}
match self.partial_cmp(rhs) {
Some(ordering) => Ok(Value::Bool {
val: matches!(ordering, Ordering::Greater | Ordering::Equal),
2021-08-25 21:29:36 +02:00
span,
}),
None => Err(ShellError::OperatorMismatch {
2021-08-25 21:29:36 +02:00
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-08-25 21:29:36 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-08-25 21:29:36 +02:00
}),
}
}
pub fn eq(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-08-25 21:29:36 +02:00
if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
return lhs.operation(*span, Operator::Equal, op, rhs);
}
match self.partial_cmp(rhs) {
Some(ordering) => Ok(Value::Bool {
val: matches!(ordering, Ordering::Equal),
2021-09-04 08:52:28 +02:00
span,
}),
None => Err(ShellError::OperatorMismatch {
2021-08-25 21:29:36 +02:00
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-08-25 21:29:36 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-08-25 21:29:36 +02:00
}),
}
}
pub fn ne(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-08-25 21:29:36 +02:00
if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
return lhs.operation(*span, Operator::NotEqual, op, rhs);
}
match self.partial_cmp(rhs) {
Some(ordering) => Ok(Value::Bool {
2021-10-09 17:58:58 +02:00
val: !matches!(ordering, Ordering::Equal),
2021-08-25 21:29:36 +02:00
span,
}),
None => Err(ShellError::OperatorMismatch {
2021-08-25 21:29:36 +02:00
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-08-25 21:29:36 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-08-25 21:29:36 +02:00
}),
}
}
2021-10-09 00:53:28 +02:00
pub fn r#in(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-10-09 00:53:28 +02:00
match (self, rhs) {
(lhs, Value::Range { val: rhs, .. }) => Ok(Value::Bool {
val: rhs.contains(lhs),
2021-10-09 00:53:28 +02:00
span,
}),
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::Bool {
val: rhs.contains(lhs),
span,
}),
(lhs, Value::List { vals: rhs, .. }) => Ok(Value::Bool {
val: rhs.contains(lhs),
span,
}),
(Value::String { val: lhs, .. }, Value::Record { cols: rhs, .. }) => Ok(Value::Bool {
val: rhs.contains(lhs),
span,
}),
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::In, op, rhs)
}
2021-10-09 00:53:28 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-10-09 00:53:28 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-10-09 00:53:28 +02:00
}),
}
}
2021-10-09 17:58:58 +02:00
pub fn not_in(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
2021-10-11 20:45:31 +02:00
let span = span(&[self.span()?, rhs.span()?]);
2021-10-09 17:58:58 +02:00
match (self, rhs) {
(lhs, Value::Range { val: rhs, .. }) => Ok(Value::Bool {
val: !rhs.contains(lhs),
span,
}),
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::Bool {
val: !rhs.contains(lhs),
span,
}),
(lhs, Value::List { vals: rhs, .. }) => Ok(Value::Bool {
val: !rhs.contains(lhs),
span,
}),
(Value::String { val: lhs, .. }, Value::Record { cols: rhs, .. }) => Ok(Value::Bool {
val: !rhs.contains(lhs),
span,
}),
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::NotIn, op, rhs)
}
2021-10-09 17:58:58 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
2021-10-11 20:45:31 +02:00
lhs_span: self.span()?,
2021-10-09 17:58:58 +02:00
rhs_ty: rhs.get_type(),
2021-10-11 20:45:31 +02:00
rhs_span: rhs.span()?,
2021-10-09 17:58:58 +02:00
}),
}
}
2021-10-11 22:35:12 +02:00
pub fn contains(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = span(&[self.span()?, rhs.span()?]);
match (self, rhs) {
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::Bool {
val: lhs.contains(rhs),
span,
}),
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::Contains, op, rhs)
}
2021-10-11 22:35:12 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
lhs_span: self.span()?,
rhs_ty: rhs.get_type(),
rhs_span: rhs.span()?,
}),
}
}
pub fn not_contains(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = span(&[self.span()?, rhs.span()?]);
match (self, rhs) {
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::Bool {
val: !lhs.contains(rhs),
span,
}),
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::NotContains, op, rhs)
}
2021-10-11 22:35:12 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
lhs_span: self.span()?,
rhs_ty: rhs.get_type(),
rhs_span: rhs.span()?,
}),
}
}
pub fn modulo(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = span(&[self.span()?, rhs.span()?]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if *rhs != 0 {
Ok(Value::Int {
val: lhs % rhs,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if *rhs != 0.0 {
Ok(Value::Float {
val: *lhs as f64 % *rhs,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if *rhs != 0 {
Ok(Value::Float {
val: *lhs % *rhs as f64,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if *rhs != 0.0 {
Ok(Value::Float {
val: lhs % rhs,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::Modulo, op, rhs)
}
2021-10-11 22:35:12 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
lhs_span: self.span()?,
rhs_ty: rhs.get_type(),
rhs_span: rhs.span()?,
}),
}
}
pub fn and(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = span(&[self.span()?, rhs.span()?]);
match (self, rhs) {
(Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => Ok(Value::Bool {
val: *lhs && *rhs,
span,
}),
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::And, op, rhs)
}
2021-10-11 22:35:12 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
lhs_span: self.span()?,
rhs_ty: rhs.get_type(),
rhs_span: rhs.span()?,
}),
}
}
pub fn or(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = span(&[self.span()?, rhs.span()?]);
match (self, rhs) {
(Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => Ok(Value::Bool {
val: *lhs || *rhs,
span,
}),
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::Or, op, rhs)
}
2021-10-11 22:35:12 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
lhs_span: self.span()?,
rhs_ty: rhs.get_type(),
rhs_span: rhs.span()?,
}),
}
}
pub fn pow(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = span(&[self.span()?, rhs.span()?]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = lhs.checked_pow(*rhs as u32) {
Ok(Value::Int { val, span })
} else {
Err(ShellError::OperatorOverflow(
"pow operation overflowed".into(),
span,
))
}
}
2021-10-11 22:35:12 +02:00
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
val: (*lhs as f64).powf(*rhs),
span,
}),
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Float {
val: lhs.powf(*rhs as f64),
span,
}),
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
val: lhs.powf(*rhs),
span,
}),
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::Pow, op, rhs)
}
2021-10-11 22:35:12 +02:00
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
lhs_span: self.span()?,
rhs_ty: rhs.get_type(),
rhs_span: rhs.span()?,
}),
}
}
2021-08-16 00:33:34 +02:00
}
2021-10-05 04:27:39 +02:00
/// Create a Value::Record from a spanned hashmap
impl From<Spanned<HashMap<String, Value>>> for Value {
fn from(input: Spanned<HashMap<String, Value>>) -> Self {
let span = input.span;
let (cols, vals) = input
.item
.into_iter()
.fold((vec![], vec![]), |mut acc, (k, v)| {
acc.0.push(k);
acc.1.push(v);
acc
});
Value::Record { cols, vals, span }
}
}
/// Create a Value::Record from a spanned indexmap
impl From<Spanned<IndexMap<String, Value>>> for Value {
fn from(input: Spanned<IndexMap<String, Value>>) -> Self {
let span = input.span;
let (cols, vals) = input
.item
.into_iter()
.fold((vec![], vec![]), |mut acc, (k, v)| {
acc.0.push(k);
acc.1.push(v);
acc
});
Value::Record { cols, vals, span }
}
}
2021-10-05 04:27:39 +02:00
/// Format a duration in nanoseconds into a string
pub fn format_duration(duration: i64) -> String {
let (sign, duration) = if duration >= 0 {
(1, duration)
} else {
(-1, -duration)
};
let (micros, nanos): (i64, i64) = (duration / 1000, duration % 1000);
let (millis, micros): (i64, i64) = (micros / 1000, micros % 1000);
let (secs, millis): (i64, i64) = (millis / 1000, millis % 1000);
let (mins, secs): (i64, i64) = (secs / 60, secs % 60);
let (hours, mins): (i64, i64) = (mins / 60, mins % 60);
let (days, hours): (i64, i64) = (hours / 24, hours % 24);
let mut output_prep = vec![];
if days != 0 {
output_prep.push(format!("{}day", days));
}
if hours != 0 {
output_prep.push(format!("{}hr", hours));
}
if mins != 0 {
output_prep.push(format!("{}min", mins));
}
// output 0sec for zero duration
if duration == 0 || secs != 0 {
output_prep.push(format!("{}sec", secs));
}
if millis != 0 {
output_prep.push(format!("{}ms", millis));
}
if micros != 0 {
output_prep.push(format!("{}us", micros));
}
if nanos != 0 {
output_prep.push(format!("{}ns", nanos));
}
format!(
"{}{}",
if sign == -1 { "-" } else { "" },
output_prep.join(" ")
)
}
fn format_filesize(num_bytes: i64, config: &Config) -> String {
2021-10-05 04:27:39 +02:00
let byte = byte_unit::Byte::from_bytes(num_bytes as u128);
if byte.get_bytes() == 0u128 {
return "".to_string();
}
let byte = byte.get_appropriate_unit(config.filesize_metric);
2021-10-05 04:27:39 +02:00
match byte.get_unit() {
byte_unit::ByteUnit::B => format!("{} B ", byte.get_value()),
_ => byte.format(1),
}
}