mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 07:00:37 +02:00
Dataframe feature (#361)
* custom value trait * functions for custom value trait * custom trait behind flag * open dataframe command * command to-df for basic types * follow path for dataframe * dataframe operations * dataframe not default feature * custom as default feature * corrected examples in command
This commit is contained in:
42
crates/nu-protocol/src/value/custom_value.rs
Normal file
42
crates/nu-protocol/src/value/custom_value.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::{ast::Operator, ShellError, Span, Value};
|
||||
|
||||
// Trait definition for a custom value
|
||||
#[typetag::serde(tag = "type")]
|
||||
pub trait CustomValue: fmt::Debug + Send + Sync {
|
||||
fn clone_value(&self, span: Span) -> Value;
|
||||
|
||||
// Define string representation of the custom value
|
||||
fn value_string(&self) -> String;
|
||||
|
||||
// Converts the custom value to a base nushell value
|
||||
// This is used to represent the custom value using the table representations
|
||||
// That already exist in nushell
|
||||
fn to_base_value(&self, span: Span) -> Result<Value, ShellError>;
|
||||
|
||||
// Json representation of custom value
|
||||
fn to_json(&self) -> nu_json::Value {
|
||||
nu_json::Value::Null
|
||||
}
|
||||
|
||||
// Any representation used to downcast object to its original type
|
||||
fn as_any(&self) -> &dyn std::any::Any;
|
||||
|
||||
// Follow cell path functions
|
||||
fn follow_path_int(&self, count: usize, span: Span) -> Result<Value, ShellError>;
|
||||
fn follow_path_string(&self, column_name: String, span: Span) -> Result<Value, ShellError>;
|
||||
|
||||
// Definition of an operation between the object that implements the trait
|
||||
// and another Value.
|
||||
// The Operator enum is used to indicate the expected operation
|
||||
fn operation(
|
||||
&self,
|
||||
_lhs_span: Span,
|
||||
operator: Operator,
|
||||
op: Span,
|
||||
_right: &Value,
|
||||
) -> Result<Value, ShellError> {
|
||||
Err(ShellError::UnsupportedOperator(operator, op))
|
||||
}
|
||||
}
|
115
crates/nu-protocol/src/value/from.rs
Normal file
115
crates/nu-protocol/src/value/from.rs
Normal file
@ -0,0 +1,115 @@
|
||||
use crate::{ShellError, Span, Value};
|
||||
|
||||
impl From<u8> for Value {
|
||||
fn from(val: u8) -> Self {
|
||||
Value::Int {
|
||||
val: val as i64,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for Value {
|
||||
fn from(val: u16) -> Self {
|
||||
Value::Int {
|
||||
val: val as i64,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Value {
|
||||
fn from(val: u32) -> Self {
|
||||
Value::Int {
|
||||
val: val as i64,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Value {
|
||||
fn from(val: u64) -> Self {
|
||||
Value::Int {
|
||||
val: val as i64,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i8> for Value {
|
||||
fn from(val: i8) -> Self {
|
||||
Value::Int {
|
||||
val: val as i64,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i16> for Value {
|
||||
fn from(val: i16) -> Self {
|
||||
Value::Int {
|
||||
val: val as i64,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Value {
|
||||
fn from(val: i32) -> Self {
|
||||
Value::Int {
|
||||
val: val as i64,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Value {
|
||||
fn from(val: i64) -> Self {
|
||||
Value::Int {
|
||||
val: val as i64,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Value {
|
||||
fn from(val: f32) -> Self {
|
||||
Value::Float {
|
||||
val: val as f64,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Value {
|
||||
fn from(val: f64) -> Self {
|
||||
Value::Float {
|
||||
val: val as f64,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn as_f64(&self) -> Result<f64, ShellError> {
|
||||
match self {
|
||||
Value::Float { val, .. } => Ok(*val),
|
||||
x => Err(ShellError::CantConvert(
|
||||
"f64".into(),
|
||||
x.get_type().to_string(),
|
||||
self.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_i64(&self) -> Result<i64, ShellError> {
|
||||
match self {
|
||||
Value::Int { val, .. } => Ok(*val),
|
||||
x => Err(ShellError::CantConvert(
|
||||
"rf64".into(),
|
||||
x.get_type().to_string(),
|
||||
self.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
mod custom_value;
|
||||
mod from;
|
||||
mod range;
|
||||
mod stream;
|
||||
mod unit;
|
||||
@ -15,10 +17,16 @@ use std::{cmp::Ordering, fmt::Debug};
|
||||
use crate::ast::{CellPath, PathMember};
|
||||
use crate::{did_you_mean, span, BlockId, Config, Span, Spanned, Type};
|
||||
|
||||
#[cfg(feature = "custom")]
|
||||
use crate::ast::Operator;
|
||||
|
||||
#[cfg(feature = "custom")]
|
||||
pub use custom_value::CustomValue;
|
||||
|
||||
use crate::ShellError;
|
||||
|
||||
/// Core structured values that pass through the pipeline in engine-q
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum Value {
|
||||
Bool {
|
||||
val: bool,
|
||||
@ -79,6 +87,77 @@ pub enum Value {
|
||||
val: CellPath,
|
||||
span: Span,
|
||||
},
|
||||
#[cfg(feature = "custom")]
|
||||
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,
|
||||
},
|
||||
#[cfg(feature = "custom")]
|
||||
Value::CustomValue { val, span } => val.clone_value(*span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
@ -144,6 +223,8 @@ impl Value {
|
||||
Value::Nothing { span, .. } => Ok(*span),
|
||||
Value::Binary { span, .. } => Ok(*span),
|
||||
Value::CellPath { span, .. } => Ok(*span),
|
||||
#[cfg(feature = "custom")]
|
||||
Value::CustomValue { span, .. } => Ok(*span),
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,6 +246,8 @@ impl Value {
|
||||
Value::Error { .. } => {}
|
||||
Value::Binary { span, .. } => *span = new_span,
|
||||
Value::CellPath { span, .. } => *span = new_span,
|
||||
#[cfg(feature = "custom")]
|
||||
Value::CustomValue { span, .. } => *span = new_span,
|
||||
}
|
||||
|
||||
self
|
||||
@ -193,6 +276,8 @@ impl Value {
|
||||
Value::Error { .. } => Type::Error,
|
||||
Value::Binary { .. } => Type::Binary,
|
||||
Value::CellPath { .. } => Type::CellPath,
|
||||
#[cfg(feature = "custom")]
|
||||
Value::CustomValue { .. } => Type::Custom,
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,6 +318,8 @@ impl Value {
|
||||
Value::Error { error } => format!("{:?}", error),
|
||||
Value::Binary { val, .. } => format!("{:?}", val),
|
||||
Value::CellPath { val, .. } => val.into_string(),
|
||||
#[cfg(feature = "custom")]
|
||||
Value::CustomValue { val, .. } => val.value_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,6 +360,8 @@ impl Value {
|
||||
Value::Error { error } => format!("{:?}", error),
|
||||
Value::Binary { val, .. } => format!("{:?}", val),
|
||||
Value::CellPath { val, .. } => val.into_string(),
|
||||
#[cfg(feature = "custom")]
|
||||
Value::CustomValue { val, .. } => val.value_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,6 +407,10 @@ impl Value {
|
||||
return Err(ShellError::AccessBeyondEndOfStream(*origin_span));
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "custom")]
|
||||
Value::CustomValue { val, .. } => {
|
||||
current = val.follow_path_int(*count, *origin_span)?;
|
||||
}
|
||||
x => {
|
||||
return Err(ShellError::IncompatiblePathAccess(
|
||||
format!("{}", x.get_type()),
|
||||
@ -365,6 +458,10 @@ impl Value {
|
||||
span: *span,
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "custom")]
|
||||
Value::CustomValue { val, .. } => {
|
||||
current = val.follow_path_string(column_name.clone(), *origin_span)?;
|
||||
}
|
||||
x => {
|
||||
return Err(ShellError::IncompatiblePathAccess(
|
||||
format!("{}", x.get_type()),
|
||||
@ -627,6 +724,11 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::Plus, op, rhs)
|
||||
}
|
||||
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type(),
|
||||
@ -692,6 +794,11 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::Minus, op, rhs)
|
||||
}
|
||||
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type(),
|
||||
@ -727,6 +834,10 @@ impl Value {
|
||||
val: lhs * rhs,
|
||||
span,
|
||||
}),
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::Multiply, op, rhs)
|
||||
}
|
||||
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
@ -788,6 +899,10 @@ impl Value {
|
||||
Err(ShellError::DivisionByZero(op))
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::Divide, op, rhs)
|
||||
}
|
||||
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
@ -801,6 +916,11 @@ impl Value {
|
||||
pub fn lt(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
|
||||
let span = span(&[self.span()?, rhs.span()?]);
|
||||
|
||||
#[cfg(feature = "custom")]
|
||||
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),
|
||||
@ -818,6 +938,11 @@ impl Value {
|
||||
pub fn lte(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
|
||||
let span = span(&[self.span()?, rhs.span()?]);
|
||||
|
||||
#[cfg(feature = "custom")]
|
||||
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),
|
||||
@ -835,6 +960,11 @@ impl Value {
|
||||
pub fn gt(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
|
||||
let span = span(&[self.span()?, rhs.span()?]);
|
||||
|
||||
#[cfg(feature = "custom")]
|
||||
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),
|
||||
@ -852,6 +982,11 @@ impl Value {
|
||||
pub fn gte(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
|
||||
let span = span(&[self.span()?, rhs.span()?]);
|
||||
|
||||
#[cfg(feature = "custom")]
|
||||
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),
|
||||
@ -869,6 +1004,11 @@ impl Value {
|
||||
pub fn eq(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
|
||||
let span = span(&[self.span()?, rhs.span()?]);
|
||||
|
||||
#[cfg(feature = "custom")]
|
||||
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),
|
||||
@ -886,6 +1026,11 @@ impl Value {
|
||||
pub fn ne(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
|
||||
let span = span(&[self.span()?, rhs.span()?]);
|
||||
|
||||
#[cfg(feature = "custom")]
|
||||
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 {
|
||||
val: !matches!(ordering, Ordering::Equal),
|
||||
@ -921,6 +1066,10 @@ impl Value {
|
||||
val: rhs.contains(lhs),
|
||||
span,
|
||||
}),
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::In, op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type(),
|
||||
@ -951,6 +1100,10 @@ impl Value {
|
||||
val: !rhs.contains(lhs),
|
||||
span,
|
||||
}),
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::NotIn, op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type(),
|
||||
@ -969,6 +1122,10 @@ impl Value {
|
||||
val: lhs.contains(rhs),
|
||||
span,
|
||||
}),
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::Contains, op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type(),
|
||||
@ -987,6 +1144,10 @@ impl Value {
|
||||
val: !lhs.contains(rhs),
|
||||
span,
|
||||
}),
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::NotContains, op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type(),
|
||||
@ -1041,6 +1202,10 @@ impl Value {
|
||||
Err(ShellError::DivisionByZero(op))
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::Modulo, op, rhs)
|
||||
}
|
||||
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
@ -1060,6 +1225,10 @@ impl Value {
|
||||
val: *lhs && *rhs,
|
||||
span,
|
||||
}),
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::And, op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type(),
|
||||
@ -1078,6 +1247,10 @@ impl Value {
|
||||
val: *lhs || *rhs,
|
||||
span,
|
||||
}),
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::Or, op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type(),
|
||||
@ -1114,6 +1287,10 @@ impl Value {
|
||||
val: lhs.powf(*rhs),
|
||||
span,
|
||||
}),
|
||||
#[cfg(feature = "custom")]
|
||||
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||
lhs.operation(*span, Operator::Pow, op, rhs)
|
||||
}
|
||||
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
|
Reference in New Issue
Block a user