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

456 lines
14 KiB
Rust
Raw Normal View History

pub mod column_path;
mod convert;
mod debug;
pub mod dict;
pub mod evaluate;
pub mod primitive;
Add Range and start Signature support This commit contains two improvements: - Support for a Range syntax (and a corresponding Range value) - Work towards a signature syntax Implementing the Range syntax resulted in cleaning up how operators in the core syntax works. There are now two kinds of infix operators - tight operators (`.` and `..`) - loose operators Tight operators may not be interspersed (`$it.left..$it.right` is a syntax error). Loose operators require whitespace on both sides of the operator, and can be arbitrarily interspersed. Precedence is left to right in the core syntax. Note that delimited syntax (like `( ... )` or `[ ... ]`) is a single token node in the core syntax. A single token node can be parsed from beginning to end in a context-free manner. The rule for `.` is `<token node>.<member>`. The rule for `..` is `<token node>..<token node>`. Loose operators all have the same syntactic rule: `<token node><space><loose op><space><token node>`. The second aspect of this pull request is the beginning of support for a signature syntax. Before implementing signatures, a necessary prerequisite is for the core syntax to support multi-line programs. That work establishes a few things: - `;` and newlines are handled in the core grammar, and both count as "separators" - line comments begin with `#` and continue until the end of the line In this commit, multi-token productions in the core grammar can use separators interchangably with spaces. However, I think we will ultimately want a different rule preventing separators from occurring before an infix operator, so that the end of a line is always unambiguous. This would avoid gratuitous differences between modules and repl usage. We already effectively have this rule, because otherwise `x<newline> | y` would be a single pipeline, but of course that wouldn't work.
2019-12-04 22:14:52 +01:00
pub mod range;
mod serde_bigdecimal;
mod serde_bigint;
use crate::type_name::{ShellTypeName, SpannedTypeName};
use crate::value::dict::Dictionary;
use crate::value::evaluate::Evaluate;
use crate::value::primitive::Primitive;
Add Range and start Signature support This commit contains two improvements: - Support for a Range syntax (and a corresponding Range value) - Work towards a signature syntax Implementing the Range syntax resulted in cleaning up how operators in the core syntax works. There are now two kinds of infix operators - tight operators (`.` and `..`) - loose operators Tight operators may not be interspersed (`$it.left..$it.right` is a syntax error). Loose operators require whitespace on both sides of the operator, and can be arbitrarily interspersed. Precedence is left to right in the core syntax. Note that delimited syntax (like `( ... )` or `[ ... ]`) is a single token node in the core syntax. A single token node can be parsed from beginning to end in a context-free manner. The rule for `.` is `<token node>.<member>`. The rule for `..` is `<token node>..<token node>`. Loose operators all have the same syntactic rule: `<token node><space><loose op><space><token node>`. The second aspect of this pull request is the beginning of support for a signature syntax. Before implementing signatures, a necessary prerequisite is for the core syntax to support multi-line programs. That work establishes a few things: - `;` and newlines are handled in the core grammar, and both count as "separators" - line comments begin with `#` and continue until the end of the line In this commit, multi-token productions in the core grammar can use separators interchangably with spaces. However, I think we will ultimately want a different rule preventing separators from occurring before an infix operator, so that the end of a line is always unambiguous. This would avoid gratuitous differences between modules and repl usage. We already effectively have this rule, because otherwise `x<newline> | y` would be a single pipeline, but of course that wouldn't work.
2019-12-04 22:14:52 +01:00
use crate::value::range::{Range, RangeInclusion};
use crate::{ColumnPath, PathMember};
use bigdecimal::BigDecimal;
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
use nu_errors::ShellError;
Add Range and start Signature support This commit contains two improvements: - Support for a Range syntax (and a corresponding Range value) - Work towards a signature syntax Implementing the Range syntax resulted in cleaning up how operators in the core syntax works. There are now two kinds of infix operators - tight operators (`.` and `..`) - loose operators Tight operators may not be interspersed (`$it.left..$it.right` is a syntax error). Loose operators require whitespace on both sides of the operator, and can be arbitrarily interspersed. Precedence is left to right in the core syntax. Note that delimited syntax (like `( ... )` or `[ ... ]`) is a single token node in the core syntax. A single token node can be parsed from beginning to end in a context-free manner. The rule for `.` is `<token node>.<member>`. The rule for `..` is `<token node>..<token node>`. Loose operators all have the same syntactic rule: `<token node><space><loose op><space><token node>`. The second aspect of this pull request is the beginning of support for a signature syntax. Before implementing signatures, a necessary prerequisite is for the core syntax to support multi-line programs. That work establishes a few things: - `;` and newlines are handled in the core grammar, and both count as "separators" - line comments begin with `#` and continue until the end of the line In this commit, multi-token productions in the core grammar can use separators interchangably with spaces. However, I think we will ultimately want a different rule preventing separators from occurring before an infix operator, so that the end of a line is always unambiguous. This would avoid gratuitous differences between modules and repl usage. We already effectively have this rule, because otherwise `x<newline> | y` would be a single pipeline, but of course that wouldn't work.
2019-12-04 22:14:52 +01:00
use nu_source::{AnchorLocation, HasSpan, Span, Spanned, Tag};
use num_bigint::BigInt;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time::SystemTime;
2020-01-18 20:42:36 +01:00
/// The core structured values that flow through a pipeline
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
pub enum UntaggedValue {
2020-01-18 20:42:36 +01:00
/// A primitive (or fundamental) type of values
Primitive(Primitive),
2020-01-18 20:42:36 +01:00
/// A table row
Row(Dictionary),
2020-01-18 20:42:36 +01:00
/// A full inner (or embedded) table
Table(Vec<Value>),
2020-01-18 20:42:36 +01:00
/// An error value that represents an error that occurred as the values in the pipeline were built
Error(ShellError),
2020-01-18 20:42:36 +01:00
/// A block of Nu code, eg `{ ls | get name }`
Block(Evaluate),
}
impl UntaggedValue {
2020-01-18 20:42:36 +01:00
/// Tags an UntaggedValue so that it can become a Value
pub fn retag(self, tag: impl Into<Tag>) -> Value {
Value {
value: self,
tag: tag.into(),
}
}
2020-01-18 20:42:36 +01:00
/// Get the corresponding descriptors (column names) associated with this value
pub fn data_descriptors(&self) -> Vec<String> {
match self {
UntaggedValue::Primitive(_) => vec![],
UntaggedValue::Row(columns) => columns.entries.keys().map(|x| x.to_string()).collect(),
UntaggedValue::Block(_) => vec![],
UntaggedValue::Table(_) => vec![],
UntaggedValue::Error(_) => vec![],
}
}
2020-01-18 20:42:36 +01:00
/// Convert this UntaggedValue to a Value with the given Tag
pub fn into_value(self, tag: impl Into<Tag>) -> Value {
Value {
value: self,
tag: tag.into(),
}
}
2020-01-18 20:42:36 +01:00
/// Convert this UntaggedValue into a Value with an empty Tag
pub fn into_untagged_value(self) -> Value {
Value {
value: self,
tag: Tag::unknown(),
}
}
2020-01-18 20:42:36 +01:00
/// Returns true if this value represents boolean true
pub fn is_true(&self) -> bool {
match self {
UntaggedValue::Primitive(Primitive::Boolean(true)) => true,
_ => false,
}
}
2020-01-18 20:42:36 +01:00
/// Returns true if the value represents something other than Nothing
pub fn is_some(&self) -> bool {
!self.is_none()
}
2020-01-18 20:42:36 +01:00
/// Returns true if the value represents Nothing
pub fn is_none(&self) -> bool {
match self {
UntaggedValue::Primitive(Primitive::Nothing) => true,
_ => false,
}
}
2020-01-18 20:42:36 +01:00
/// Returns true if the value represents an error
pub fn is_error(&self) -> bool {
match self {
UntaggedValue::Error(_err) => true,
_ => false,
}
}
2020-01-18 20:42:36 +01:00
/// Expect this value to be an error and return it
pub fn expect_error(&self) -> ShellError {
match self {
UntaggedValue::Error(err) => err.clone(),
_ => panic!("Don't call expect_error without first calling is_error"),
}
}
2020-01-18 20:42:36 +01:00
/// Expect this value to be a string and return it
pub fn expect_string(&self) -> &str {
match self {
UntaggedValue::Primitive(Primitive::String(string)) => &string[..],
_ => panic!("expect_string assumes that the value must be a string"),
}
}
2020-01-18 20:42:36 +01:00
/// Helper for creating row values
pub fn row(entries: IndexMap<String, Value>) -> UntaggedValue {
UntaggedValue::Row(entries.into())
}
2020-01-18 20:42:36 +01:00
/// Helper for creating table values
pub fn table(list: &[Value]) -> UntaggedValue {
UntaggedValue::Table(list.to_vec())
}
2020-01-18 20:42:36 +01:00
/// Helper for creating string values
pub fn string(s: impl Into<String>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::String(s.into()))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating line values
pub fn line(s: impl Into<String>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Line(s.into()))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating column-path values
pub fn column_path(s: Vec<impl Into<PathMember>>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::ColumnPath(ColumnPath::new(
s.into_iter().map(|p| p.into()).collect(),
)))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating integer values
pub fn int(i: impl Into<BigInt>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Int(i.into()))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating glob pattern values
pub fn pattern(s: impl Into<String>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::String(s.into()))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating filepath values
pub fn path(s: impl Into<PathBuf>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Path(s.into()))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating bytesize values
pub fn bytes(s: impl Into<u64>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Bytes(s.into()))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating decimal values
pub fn decimal(s: impl Into<BigDecimal>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Decimal(s.into()))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating binary (non-text) buffer values
pub fn binary(binary: Vec<u8>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Binary(binary))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating range values
Add Range and start Signature support This commit contains two improvements: - Support for a Range syntax (and a corresponding Range value) - Work towards a signature syntax Implementing the Range syntax resulted in cleaning up how operators in the core syntax works. There are now two kinds of infix operators - tight operators (`.` and `..`) - loose operators Tight operators may not be interspersed (`$it.left..$it.right` is a syntax error). Loose operators require whitespace on both sides of the operator, and can be arbitrarily interspersed. Precedence is left to right in the core syntax. Note that delimited syntax (like `( ... )` or `[ ... ]`) is a single token node in the core syntax. A single token node can be parsed from beginning to end in a context-free manner. The rule for `.` is `<token node>.<member>`. The rule for `..` is `<token node>..<token node>`. Loose operators all have the same syntactic rule: `<token node><space><loose op><space><token node>`. The second aspect of this pull request is the beginning of support for a signature syntax. Before implementing signatures, a necessary prerequisite is for the core syntax to support multi-line programs. That work establishes a few things: - `;` and newlines are handled in the core grammar, and both count as "separators" - line comments begin with `#` and continue until the end of the line In this commit, multi-token productions in the core grammar can use separators interchangably with spaces. However, I think we will ultimately want a different rule preventing separators from occurring before an infix operator, so that the end of a line is always unambiguous. This would avoid gratuitous differences between modules and repl usage. We already effectively have this rule, because otherwise `x<newline> | y` would be a single pipeline, but of course that wouldn't work.
2019-12-04 22:14:52 +01:00
pub fn range(
left: (Spanned<Primitive>, RangeInclusion),
right: (Spanned<Primitive>, RangeInclusion),
) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Range(Box::new(Range::new(left, right))))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating boolean values
pub fn boolean(s: impl Into<bool>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Boolean(s.into()))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating date duration values
pub fn duration(secs: u64) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Duration(secs))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating datatime values
pub fn system_date(s: SystemTime) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Date(s.into()))
}
pub fn date(d: impl Into<DateTime<Utc>>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Date(d.into()))
}
2020-01-18 20:42:36 +01:00
/// Helper for creating the Nothing value
pub fn nothing() -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Nothing)
}
}
2020-01-18 20:42:36 +01:00
/// The fundamental structured value that flows through the pipeline, with associated metadata
#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Serialize, Deserialize)]
pub struct Value {
pub value: UntaggedValue,
pub tag: Tag,
}
2020-01-18 20:42:36 +01:00
/// Overload deferencing to give back the UntaggedValue inside of a Value
impl std::ops::Deref for Value {
type Target = UntaggedValue;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl Value {
2020-01-18 20:42:36 +01:00
/// Get the corresponding anchor (originating location) for the Value
pub fn anchor(&self) -> Option<AnchorLocation> {
self.tag.anchor()
}
2020-01-18 20:42:36 +01:00
/// Get the name (url, filepath, etc) behind an anchor for the Value
pub fn anchor_name(&self) -> Option<String> {
self.tag.anchor_name()
}
2020-01-18 20:42:36 +01:00
/// Get the metadata for the Value
pub fn tag(&self) -> Tag {
self.tag.clone()
}
2020-01-18 20:42:36 +01:00
/// View the Value as a string, if possible
pub fn as_string(&self) -> Result<String, ShellError> {
match &self.value {
UntaggedValue::Primitive(Primitive::String(string)) => Ok(string.clone()),
UntaggedValue::Primitive(Primitive::Line(line)) => Ok(line.clone() + "\n"),
_ => Err(ShellError::type_error("string", self.spanned_type_name())),
}
}
2020-01-18 20:42:36 +01:00
/// View into the borrowed string contents of a Value, if possible
2019-12-02 17:14:05 +01:00
pub fn as_forgiving_string(&self) -> Result<&str, ShellError> {
match &self.value {
UntaggedValue::Primitive(Primitive::String(string)) => Ok(&string[..]),
_ => Err(ShellError::type_error("string", self.spanned_type_name())),
}
}
2020-01-18 20:42:36 +01:00
/// View the Value as a path, if possible
pub fn as_path(&self) -> Result<PathBuf, ShellError> {
match &self.value {
UntaggedValue::Primitive(Primitive::Path(path)) => Ok(path.clone()),
UntaggedValue::Primitive(Primitive::String(path_str)) => Ok(PathBuf::from(&path_str)),
_ => Err(ShellError::type_error("Path", self.spanned_type_name())),
}
}
Add Range and start Signature support This commit contains two improvements: - Support for a Range syntax (and a corresponding Range value) - Work towards a signature syntax Implementing the Range syntax resulted in cleaning up how operators in the core syntax works. There are now two kinds of infix operators - tight operators (`.` and `..`) - loose operators Tight operators may not be interspersed (`$it.left..$it.right` is a syntax error). Loose operators require whitespace on both sides of the operator, and can be arbitrarily interspersed. Precedence is left to right in the core syntax. Note that delimited syntax (like `( ... )` or `[ ... ]`) is a single token node in the core syntax. A single token node can be parsed from beginning to end in a context-free manner. The rule for `.` is `<token node>.<member>`. The rule for `..` is `<token node>..<token node>`. Loose operators all have the same syntactic rule: `<token node><space><loose op><space><token node>`. The second aspect of this pull request is the beginning of support for a signature syntax. Before implementing signatures, a necessary prerequisite is for the core syntax to support multi-line programs. That work establishes a few things: - `;` and newlines are handled in the core grammar, and both count as "separators" - line comments begin with `#` and continue until the end of the line In this commit, multi-token productions in the core grammar can use separators interchangably with spaces. However, I think we will ultimately want a different rule preventing separators from occurring before an infix operator, so that the end of a line is always unambiguous. This would avoid gratuitous differences between modules and repl usage. We already effectively have this rule, because otherwise `x<newline> | y` would be a single pipeline, but of course that wouldn't work.
2019-12-04 22:14:52 +01:00
2020-01-18 20:42:36 +01:00
/// View the Value as a Primitive value, if possible
Add Range and start Signature support This commit contains two improvements: - Support for a Range syntax (and a corresponding Range value) - Work towards a signature syntax Implementing the Range syntax resulted in cleaning up how operators in the core syntax works. There are now two kinds of infix operators - tight operators (`.` and `..`) - loose operators Tight operators may not be interspersed (`$it.left..$it.right` is a syntax error). Loose operators require whitespace on both sides of the operator, and can be arbitrarily interspersed. Precedence is left to right in the core syntax. Note that delimited syntax (like `( ... )` or `[ ... ]`) is a single token node in the core syntax. A single token node can be parsed from beginning to end in a context-free manner. The rule for `.` is `<token node>.<member>`. The rule for `..` is `<token node>..<token node>`. Loose operators all have the same syntactic rule: `<token node><space><loose op><space><token node>`. The second aspect of this pull request is the beginning of support for a signature syntax. Before implementing signatures, a necessary prerequisite is for the core syntax to support multi-line programs. That work establishes a few things: - `;` and newlines are handled in the core grammar, and both count as "separators" - line comments begin with `#` and continue until the end of the line In this commit, multi-token productions in the core grammar can use separators interchangably with spaces. However, I think we will ultimately want a different rule preventing separators from occurring before an infix operator, so that the end of a line is always unambiguous. This would avoid gratuitous differences between modules and repl usage. We already effectively have this rule, because otherwise `x<newline> | y` would be a single pipeline, but of course that wouldn't work.
2019-12-04 22:14:52 +01:00
pub fn as_primitive(&self) -> Result<Primitive, ShellError> {
match &self.value {
UntaggedValue::Primitive(primitive) => Ok(primitive.clone()),
_ => Err(ShellError::type_error(
"Primitive",
self.spanned_type_name(),
)),
}
}
2020-01-18 20:42:36 +01:00
/// View the Value as unsigned 64-bit, if possible
Add Range and start Signature support This commit contains two improvements: - Support for a Range syntax (and a corresponding Range value) - Work towards a signature syntax Implementing the Range syntax resulted in cleaning up how operators in the core syntax works. There are now two kinds of infix operators - tight operators (`.` and `..`) - loose operators Tight operators may not be interspersed (`$it.left..$it.right` is a syntax error). Loose operators require whitespace on both sides of the operator, and can be arbitrarily interspersed. Precedence is left to right in the core syntax. Note that delimited syntax (like `( ... )` or `[ ... ]`) is a single token node in the core syntax. A single token node can be parsed from beginning to end in a context-free manner. The rule for `.` is `<token node>.<member>`. The rule for `..` is `<token node>..<token node>`. Loose operators all have the same syntactic rule: `<token node><space><loose op><space><token node>`. The second aspect of this pull request is the beginning of support for a signature syntax. Before implementing signatures, a necessary prerequisite is for the core syntax to support multi-line programs. That work establishes a few things: - `;` and newlines are handled in the core grammar, and both count as "separators" - line comments begin with `#` and continue until the end of the line In this commit, multi-token productions in the core grammar can use separators interchangably with spaces. However, I think we will ultimately want a different rule preventing separators from occurring before an infix operator, so that the end of a line is always unambiguous. This would avoid gratuitous differences between modules and repl usage. We already effectively have this rule, because otherwise `x<newline> | y` would be a single pipeline, but of course that wouldn't work.
2019-12-04 22:14:52 +01:00
pub fn as_u64(&self) -> Result<u64, ShellError> {
match &self.value {
UntaggedValue::Primitive(primitive) => primitive.as_u64(self.tag.span),
_ => Err(ShellError::type_error("integer", self.spanned_type_name())),
}
}
Add --help for commands (#1226) * WIP --help works for PerItemCommands. * De-linting * Add more comments (#1228) * Add some more docs * More docs * More docs * More docs (#1229) * Add some more docs * More docs * More docs * Add more docs * External commands: wrap values that contain spaces in quotes (#1214) (#1220) * External commands: wrap values that contain spaces in quotes (#1214) * Add fn's argument_contains_whitespace & add_quotes (#1214) * Fix formatting with cargo fmt * Don't wrap argument in quotes when $it is already quoted (#1214) * Implement --help for internal commands * Externals now spawn independently. (#1230) This commit changes the way we shell out externals when using the `"$it"` argument. Also pipes per row to an external's stdin if no `"$it"` argument is present for external commands. Further separation of logic (preparing the external's command arguments, getting the data for piping, emitting values, spawning processes) will give us a better idea for lower level details regarding external commands until we can find the right abstractions for making them more generic and unify within the pipeline calling logic of Nu internal's and external's. * Poll externals quicker. (#1231) * WIP --help works for PerItemCommands. * De-linting * Implement --help for internal commands * Make having --help the default * Update test to include new default switch Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com> Co-authored-by: Koenraad Verheyden <mail@koenraadverheyden.com> Co-authored-by: Andrés N. Robalino <andres@androbtech.com>
2020-01-17 23:46:18 +01:00
2020-01-18 20:42:36 +01:00
/// View the Value as boolean, if possible
Add --help for commands (#1226) * WIP --help works for PerItemCommands. * De-linting * Add more comments (#1228) * Add some more docs * More docs * More docs * More docs (#1229) * Add some more docs * More docs * More docs * Add more docs * External commands: wrap values that contain spaces in quotes (#1214) (#1220) * External commands: wrap values that contain spaces in quotes (#1214) * Add fn's argument_contains_whitespace & add_quotes (#1214) * Fix formatting with cargo fmt * Don't wrap argument in quotes when $it is already quoted (#1214) * Implement --help for internal commands * Externals now spawn independently. (#1230) This commit changes the way we shell out externals when using the `"$it"` argument. Also pipes per row to an external's stdin if no `"$it"` argument is present for external commands. Further separation of logic (preparing the external's command arguments, getting the data for piping, emitting values, spawning processes) will give us a better idea for lower level details regarding external commands until we can find the right abstractions for making them more generic and unify within the pipeline calling logic of Nu internal's and external's. * Poll externals quicker. (#1231) * WIP --help works for PerItemCommands. * De-linting * Implement --help for internal commands * Make having --help the default * Update test to include new default switch Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com> Co-authored-by: Koenraad Verheyden <mail@koenraadverheyden.com> Co-authored-by: Andrés N. Robalino <andres@androbtech.com>
2020-01-17 23:46:18 +01:00
pub fn as_bool(&self) -> Result<bool, ShellError> {
match &self.value {
UntaggedValue::Primitive(Primitive::Boolean(p)) => Ok(*p),
_ => Err(ShellError::type_error("boolean", self.spanned_type_name())),
}
}
}
impl Into<Value> for String {
fn into(self) -> Value {
let end = self.len();
Value {
value: self.into(),
tag: Tag {
anchor: None,
span: Span::new(0, end),
},
}
}
}
impl Into<UntaggedValue> for &str {
2020-01-18 20:42:36 +01:00
/// Convert a string slice into an UntaggedValue
fn into(self) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::String(self.to_string()))
}
}
impl Into<UntaggedValue> for Value {
2020-01-18 20:42:36 +01:00
/// Convert a Value into an UntaggedValue
fn into(self) -> UntaggedValue {
self.value
}
}
2020-01-18 20:42:36 +01:00
/// Convert a borrowed Value into a borrowed UntaggedValue
impl<'a> Into<&'a UntaggedValue> for &'a Value {
fn into(self) -> &'a UntaggedValue {
&self.value
}
}
impl HasSpan for Value {
2020-01-18 20:42:36 +01:00
/// Return the corresponding Span for the Value
fn span(&self) -> Span {
self.tag.span
}
}
impl ShellTypeName for Value {
2020-01-18 20:42:36 +01:00
/// Get the type name for the Value
fn type_name(&self) -> &'static str {
ShellTypeName::type_name(&self.value)
}
}
impl ShellTypeName for UntaggedValue {
2020-01-18 20:42:36 +01:00
/// Get the type name for the UntaggedValue
fn type_name(&self) -> &'static str {
match &self {
UntaggedValue::Primitive(p) => p.type_name(),
UntaggedValue::Row(_) => "row",
UntaggedValue::Table(_) => "table",
UntaggedValue::Error(_) => "error",
UntaggedValue::Block(_) => "block",
}
}
}
impl From<Primitive> for UntaggedValue {
2020-01-18 20:42:36 +01:00
/// Convert a Primitive to an UntaggedValue
fn from(input: Primitive) -> UntaggedValue {
UntaggedValue::Primitive(input)
}
}
impl From<String> for UntaggedValue {
2020-01-18 20:42:36 +01:00
/// Convert a String to an UntaggedValue
fn from(input: String) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::String(input))
}
}
impl From<ShellError> for UntaggedValue {
fn from(e: ShellError) -> Self {
UntaggedValue::Error(e)
}
}
impl num_traits::Zero for Value {
fn zero() -> Self {
Value {
value: UntaggedValue::Primitive(Primitive::zero()),
tag: Tag::unknown(),
}
}
fn is_zero(&self) -> bool {
match &self.value {
UntaggedValue::Primitive(primitive) => primitive.is_zero(),
UntaggedValue::Row(row) => row.entries.is_empty(),
UntaggedValue::Table(rows) => rows.is_empty(),
_ => false,
}
}
}
impl std::ops::Mul for Value {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
let tag = self.tag.clone();
match (&*self, &*rhs) {
(UntaggedValue::Primitive(left), UntaggedValue::Primitive(right)) => {
let left = left.clone();
let right = right.clone();
UntaggedValue::from(left.mul(right)).into_value(tag)
}
(_, _) => unimplemented!("Internal error: can't multiply non-primitives."),
}
}
}
impl std::ops::Add for Value {
type Output = Self;
fn add(self, rhs: Self) -> Self {
let tag = self.tag.clone();
match (&*self, &*rhs) {
(UntaggedValue::Primitive(left), UntaggedValue::Primitive(right)) => {
let left = left.clone();
let right = right.clone();
UntaggedValue::from(left.add(right)).into_value(tag)
}
(_, _) => unimplemented!("Internal error: can't add non-primitives."),
}
}
}
pub fn merge_descriptors(values: &[Value]) -> Vec<String> {
let mut ret: Vec<String> = vec![];
let value_column = "<value>".to_string();
for value in values {
let descs = value.data_descriptors();
if descs.is_empty() {
if !ret.contains(&value_column) {
ret.push("<value>".to_string());
}
} else {
for desc in value.data_descriptors() {
if !ret.contains(&desc) {
ret.push(desc);
}
}
}
}
ret
}