2019-07-23 22:22:11 +00:00
|
|
|
use crate::context::CommandRegistry;
|
2019-05-11 22:59:57 +00:00
|
|
|
use crate::errors::ShellError;
|
2019-06-22 03:43:37 +00:00
|
|
|
use crate::evaluate::{evaluate_baseline_expr, Scope};
|
2019-08-01 01:58:42 +00:00
|
|
|
use crate::object::TaggedDictBuilder;
|
|
|
|
use crate::parser::{hir, Operator};
|
2019-05-23 04:30:43 +00:00
|
|
|
use crate::prelude::*;
|
2019-06-22 20:46:16 +00:00
|
|
|
use crate::Text;
|
2019-05-15 18:14:51 +00:00
|
|
|
use chrono::{DateTime, Utc};
|
|
|
|
use chrono_humanize::Humanize;
|
2019-05-26 06:54:41 +00:00
|
|
|
use derive_new::new;
|
2019-08-02 19:15:07 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2019-06-22 03:43:37 +00:00
|
|
|
use std::fmt;
|
2019-07-15 21:16:27 +00:00
|
|
|
use std::path::PathBuf;
|
2019-05-15 18:14:51 +00:00
|
|
|
use std::time::SystemTime;
|
2019-05-10 16:59:12 +00:00
|
|
|
|
2019-06-30 06:46:49 +00:00
|
|
|
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize, Serialize)]
|
2019-05-10 16:59:12 +00:00
|
|
|
pub enum Primitive {
|
|
|
|
Nothing,
|
|
|
|
Int(i64),
|
2019-08-30 17:29:04 +00:00
|
|
|
Decimal(Decimal),
|
2019-06-30 06:46:49 +00:00
|
|
|
Bytes(u64),
|
2019-05-10 16:59:12 +00:00
|
|
|
String(String),
|
|
|
|
Boolean(bool),
|
2019-05-15 18:14:51 +00:00
|
|
|
Date(DateTime<Utc>),
|
2019-07-15 21:16:27 +00:00
|
|
|
Path(PathBuf),
|
2019-06-27 16:47:24 +00:00
|
|
|
|
2019-07-27 07:45:00 +00:00
|
|
|
// Stream markers (used as bookend markers rather than actual values)
|
|
|
|
BeginningOfStream,
|
2019-06-27 16:47:24 +00:00
|
|
|
EndOfStream,
|
2019-05-10 16:59:12 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 17:29:04 +00:00
|
|
|
impl From<i64> for Primitive {
|
|
|
|
fn from(int: i64) -> Primitive {
|
|
|
|
Primitive::Int(int)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Decimal> for Primitive {
|
|
|
|
fn from(decimal: Decimal) -> Primitive {
|
|
|
|
Primitive::Decimal(decimal)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<f64> for Primitive {
|
|
|
|
fn from(float: f64) -> Primitive {
|
|
|
|
Primitive::Decimal(Decimal::from_f64(float).unwrap())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-10 16:59:12 +00:00
|
|
|
impl Primitive {
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn type_name(&self) -> String {
|
2019-06-03 05:11:21 +00:00
|
|
|
use Primitive::*;
|
|
|
|
|
|
|
|
match self {
|
|
|
|
Nothing => "nothing",
|
2019-07-27 07:45:00 +00:00
|
|
|
BeginningOfStream => "beginning-of-stream",
|
2019-06-27 16:47:24 +00:00
|
|
|
EndOfStream => "end-of-stream",
|
2019-07-15 21:16:27 +00:00
|
|
|
Path(_) => "path",
|
2019-06-03 05:11:21 +00:00
|
|
|
Int(_) => "int",
|
2019-08-30 17:29:04 +00:00
|
|
|
Decimal(_) => "decimal",
|
2019-06-03 05:11:21 +00:00
|
|
|
Bytes(_) => "bytes",
|
|
|
|
String(_) => "string",
|
|
|
|
Boolean(_) => "boolean",
|
|
|
|
Date(_) => "date",
|
|
|
|
}
|
|
|
|
.to_string()
|
|
|
|
}
|
|
|
|
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2019-06-22 03:43:37 +00:00
|
|
|
use Primitive::*;
|
|
|
|
|
|
|
|
match self {
|
|
|
|
Nothing => write!(f, "Nothing"),
|
2019-07-27 07:45:00 +00:00
|
|
|
BeginningOfStream => write!(f, "BeginningOfStream"),
|
2019-06-27 16:47:24 +00:00
|
|
|
EndOfStream => write!(f, "EndOfStream"),
|
2019-06-22 03:43:37 +00:00
|
|
|
Int(int) => write!(f, "{}", int),
|
2019-07-15 21:16:27 +00:00
|
|
|
Path(path) => write!(f, "{}", path.display()),
|
2019-08-30 17:29:04 +00:00
|
|
|
Decimal(decimal) => write!(f, "{}", decimal),
|
2019-06-22 03:43:37 +00:00
|
|
|
Bytes(bytes) => write!(f, "{}", bytes),
|
|
|
|
String(string) => write!(f, "{:?}", string),
|
|
|
|
Boolean(boolean) => write!(f, "{}", boolean),
|
|
|
|
Date(date) => write!(f, "{}", date),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-30 17:29:04 +00:00
|
|
|
pub fn number(number: impl Into<Number>) -> Primitive {
|
|
|
|
let number = number.into();
|
|
|
|
|
|
|
|
match number {
|
|
|
|
Number::Int(int) => Primitive::Int(int),
|
|
|
|
Number::Decimal(decimal) => Primitive::Decimal(decimal),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-04 05:11:56 +00:00
|
|
|
pub fn format(&self, field_name: Option<&String>) -> String {
|
2019-05-10 16:59:12 +00:00
|
|
|
match self {
|
2019-08-16 20:03:29 +00:00
|
|
|
Primitive::Nothing => String::new(),
|
|
|
|
Primitive::BeginningOfStream => String::new(),
|
|
|
|
Primitive::EndOfStream => String::new(),
|
2019-07-15 21:16:27 +00:00
|
|
|
Primitive::Path(p) => format!("{}", p.display()),
|
2019-05-15 18:14:51 +00:00
|
|
|
Primitive::Bytes(b) => {
|
2019-06-30 06:46:49 +00:00
|
|
|
let byte = byte_unit::Byte::from_bytes(*b as u128);
|
2019-05-15 22:23:36 +00:00
|
|
|
|
|
|
|
if byte.get_bytes() == 0u128 {
|
2019-08-24 05:31:50 +00:00
|
|
|
return "—".to_string();
|
2019-05-15 22:23:36 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 05:05:14 +00:00
|
|
|
let byte = byte.get_appropriate_unit(false);
|
2019-05-15 18:14:51 +00:00
|
|
|
|
|
|
|
match byte.get_unit() {
|
2019-08-24 05:31:50 +00:00
|
|
|
byte_unit::ByteUnit::B => format!("{} B ", byte.get_value()),
|
2019-05-15 18:14:51 +00:00
|
|
|
_ => format!("{}", byte.format(1)),
|
|
|
|
}
|
|
|
|
}
|
2019-05-10 16:59:12 +00:00
|
|
|
Primitive::Int(i) => format!("{}", i),
|
2019-08-30 17:29:04 +00:00
|
|
|
Primitive::Decimal(decimal) => format!("{}", decimal),
|
2019-05-15 18:14:51 +00:00
|
|
|
Primitive::String(s) => format!("{}", s),
|
|
|
|
Primitive::Boolean(b) => match (b, field_name) {
|
|
|
|
(true, None) => format!("Yes"),
|
|
|
|
(false, None) => format!("No"),
|
2019-07-03 17:37:09 +00:00
|
|
|
(true, Some(s)) if !s.is_empty() => format!("{}", s),
|
|
|
|
(false, Some(s)) if !s.is_empty() => format!(""),
|
2019-06-04 21:42:31 +00:00
|
|
|
(true, Some(_)) => format!("Yes"),
|
|
|
|
(false, Some(_)) => format!("No"),
|
2019-05-15 18:14:51 +00:00
|
|
|
},
|
2019-05-15 22:23:36 +00:00
|
|
|
Primitive::Date(d) => format!("{}", d.humanize()),
|
2019-05-10 16:59:12 +00:00
|
|
|
}
|
|
|
|
}
|
2019-08-24 05:31:50 +00:00
|
|
|
|
|
|
|
pub fn style(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
Primitive::Bytes(0) => "c", // centre 'missing' indicator
|
2019-08-30 17:29:04 +00:00
|
|
|
Primitive::Int(_) | Primitive::Bytes(_) | Primitive::Decimal(_) => "r",
|
2019-08-26 18:19:05 +00:00
|
|
|
_ => "",
|
2019-08-24 05:31:50 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-10 16:59:12 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 02:01:37 +00:00
|
|
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new, Serialize)]
|
2019-05-26 06:54:41 +00:00
|
|
|
pub struct Operation {
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) left: Value,
|
|
|
|
pub(crate) operator: Operator,
|
|
|
|
pub(crate) right: Value,
|
2019-05-26 06:54:41 +00:00
|
|
|
}
|
|
|
|
|
2019-08-02 19:15:07 +00:00
|
|
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Serialize, Deserialize, new)]
|
2019-05-28 06:45:18 +00:00
|
|
|
pub struct Block {
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) expressions: Vec<hir::Expression>,
|
|
|
|
pub(crate) source: Text,
|
|
|
|
pub(crate) span: Span,
|
2019-05-28 06:45:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Block {
|
2019-08-01 01:58:42 +00:00
|
|
|
pub fn invoke(&self, value: &Tagged<Value>) -> Result<Tagged<Value>, ShellError> {
|
2019-07-08 16:44:53 +00:00
|
|
|
let scope = Scope::new(value.clone());
|
2019-06-29 08:55:42 +00:00
|
|
|
|
|
|
|
if self.expressions.len() == 0 {
|
2019-08-05 08:54:29 +00:00
|
|
|
return Ok(Value::nothing().simple_spanned(self.span));
|
2019-06-29 08:55:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut last = None;
|
|
|
|
|
|
|
|
for expr in self.expressions.iter() {
|
2019-07-23 22:22:11 +00:00
|
|
|
last = Some(evaluate_baseline_expr(
|
|
|
|
&expr,
|
|
|
|
&CommandRegistry::empty(),
|
|
|
|
&scope,
|
|
|
|
&self.source,
|
|
|
|
)?)
|
2019-06-29 08:55:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(last.unwrap())
|
2019-05-28 06:45:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-30 06:46:49 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Serialize, Deserialize)]
|
2019-05-10 16:59:12 +00:00
|
|
|
pub enum Value {
|
|
|
|
Primitive(Primitive),
|
2019-05-15 21:44:06 +00:00
|
|
|
Object(crate::object::Dictionary),
|
2019-07-15 18:34:44 +00:00
|
|
|
#[serde(with = "serde_bytes")]
|
2019-07-04 05:11:56 +00:00
|
|
|
Binary(Vec<u8>),
|
2019-08-01 01:58:42 +00:00
|
|
|
List(Vec<Tagged<Value>>),
|
2019-06-22 03:43:37 +00:00
|
|
|
#[allow(unused)]
|
2019-05-28 06:45:18 +00:00
|
|
|
Block(Block),
|
2019-05-10 16:59:12 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 17:29:04 +00:00
|
|
|
impl Into<Value> for Number {
|
|
|
|
fn into(self) -> Value {
|
|
|
|
match self {
|
|
|
|
Number::Int(int) => Value::int(int),
|
|
|
|
Number::Decimal(decimal) => Value::decimal(decimal),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
pub fn debug_list(values: &Vec<Tagged<Value>>) -> ValuesDebug<'_> {
|
2019-06-22 03:43:37 +00:00
|
|
|
ValuesDebug { values }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ValuesDebug<'a> {
|
2019-08-01 01:58:42 +00:00
|
|
|
values: &'a Vec<Tagged<Value>>,
|
2019-06-22 03:43:37 +00:00
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
impl fmt::Debug for ValuesDebug<'_> {
|
2019-06-22 03:43:37 +00:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
f.debug_list()
|
|
|
|
.entries(self.values.iter().map(|i| i.debug()))
|
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ValueDebug<'a> {
|
2019-08-01 01:58:42 +00:00
|
|
|
value: &'a Tagged<Value>,
|
2019-06-22 03:43:37 +00:00
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
impl fmt::Debug for ValueDebug<'_> {
|
2019-06-22 03:43:37 +00:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2019-07-08 16:44:53 +00:00
|
|
|
match self.value.item() {
|
2019-06-22 03:43:37 +00:00
|
|
|
Value::Primitive(p) => p.debug(f),
|
|
|
|
Value::Object(o) => o.debug(f),
|
|
|
|
Value::List(l) => debug_list(l).fmt(f),
|
|
|
|
Value::Block(_) => write!(f, "[[block]]"),
|
2019-07-04 05:11:56 +00:00
|
|
|
Value::Binary(_) => write!(f, "[[binary]]"),
|
2019-06-22 03:43:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-01 01:58:42 +00:00
|
|
|
impl Tagged<Value> {
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn tagged_type_name(&self) -> Tagged<String> {
|
2019-06-24 00:55:31 +00:00
|
|
|
let name = self.type_name();
|
2019-08-10 20:18:14 +00:00
|
|
|
Tagged::from_item(name, self.tag())
|
2019-06-24 00:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
impl std::convert::TryFrom<&Tagged<Value>> for Block {
|
2019-07-03 20:31:15 +00:00
|
|
|
type Error = ShellError;
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
fn try_from(value: &Tagged<Value>) -> Result<Block, ShellError> {
|
2019-07-03 20:31:15 +00:00
|
|
|
match value.item() {
|
|
|
|
Value::Block(block) => Ok(block.clone()),
|
|
|
|
v => Err(ShellError::type_error(
|
|
|
|
"Block",
|
|
|
|
value.copy_span(v.type_name()),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
impl std::convert::TryFrom<&Tagged<Value>> for i64 {
|
2019-07-03 20:31:15 +00:00
|
|
|
type Error = ShellError;
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
fn try_from(value: &Tagged<Value>) -> Result<i64, ShellError> {
|
2019-07-03 20:31:15 +00:00
|
|
|
match value.item() {
|
|
|
|
Value::Primitive(Primitive::Int(int)) => Ok(*int),
|
|
|
|
v => Err(ShellError::type_error(
|
|
|
|
"Integer",
|
|
|
|
value.copy_span(v.type_name()),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
impl std::convert::TryFrom<&Tagged<Value>> for String {
|
2019-08-26 14:16:34 +00:00
|
|
|
type Error = ShellError;
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
fn try_from(value: &Tagged<Value>) -> Result<String, ShellError> {
|
2019-08-26 14:16:34 +00:00
|
|
|
match value.item() {
|
|
|
|
Value::Primitive(Primitive::String(s)) => Ok(s.clone()),
|
|
|
|
v => Err(ShellError::type_error(
|
|
|
|
"String",
|
|
|
|
value.copy_span(v.type_name()),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
impl std::convert::TryFrom<&Tagged<Value>> for Vec<u8> {
|
2019-08-26 14:16:34 +00:00
|
|
|
type Error = ShellError;
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
fn try_from(value: &Tagged<Value>) -> Result<Vec<u8>, ShellError> {
|
2019-08-26 14:16:34 +00:00
|
|
|
match value.item() {
|
|
|
|
Value::Binary(b) => Ok(b.clone()),
|
|
|
|
v => Err(ShellError::type_error(
|
|
|
|
"Binary",
|
|
|
|
value.copy_span(v.type_name()),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
impl<'a> std::convert::TryFrom<&'a Tagged<Value>> for &'a crate::object::Dictionary {
|
2019-08-26 14:16:34 +00:00
|
|
|
type Error = ShellError;
|
|
|
|
|
|
|
|
fn try_from(value: &'a Tagged<Value>) -> Result<&'a crate::object::Dictionary, ShellError> {
|
|
|
|
match value.item() {
|
|
|
|
Value::Object(d) => Ok(d),
|
|
|
|
v => Err(ShellError::type_error(
|
|
|
|
"Dictionary",
|
|
|
|
value.copy_span(v.type_name()),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-02 19:15:07 +00:00
|
|
|
#[derive(Serialize, Deserialize)]
|
2019-07-03 20:31:15 +00:00
|
|
|
pub enum Switch {
|
|
|
|
Present,
|
|
|
|
Absent,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Switch {
|
2019-07-24 04:10:48 +00:00
|
|
|
#[allow(unused)]
|
2019-07-03 20:31:15 +00:00
|
|
|
pub fn is_present(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
Switch::Present => true,
|
|
|
|
Switch::Absent => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
impl std::convert::TryFrom<Option<&Tagged<Value>>> for Switch {
|
2019-07-03 20:31:15 +00:00
|
|
|
type Error = ShellError;
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
fn try_from(value: Option<&Tagged<Value>>) -> Result<Switch, ShellError> {
|
2019-07-03 20:31:15 +00:00
|
|
|
match value {
|
|
|
|
None => Ok(Switch::Absent),
|
|
|
|
Some(value) => match value.item() {
|
|
|
|
Value::Primitive(Primitive::Boolean(true)) => Ok(Switch::Present),
|
|
|
|
v => Err(ShellError::type_error(
|
|
|
|
"Boolean",
|
|
|
|
value.copy_span(v.type_name()),
|
|
|
|
)),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-01 01:58:42 +00:00
|
|
|
impl Tagged<Value> {
|
2019-08-29 12:16:11 +00:00
|
|
|
pub(crate) fn debug(&self) -> ValueDebug<'_> {
|
2019-07-08 16:44:53 +00:00
|
|
|
ValueDebug { value: self }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:23:36 +00:00
|
|
|
impl Value {
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn type_name(&self) -> String {
|
2019-06-03 05:11:21 +00:00
|
|
|
match self {
|
|
|
|
Value::Primitive(p) => p.type_name(),
|
|
|
|
Value::Object(_) => format!("object"),
|
|
|
|
Value::List(_) => format!("list"),
|
|
|
|
Value::Block(_) => format!("block"),
|
2019-07-04 05:11:56 +00:00
|
|
|
Value::Binary(_) => format!("binary"),
|
2019-06-03 05:11:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add support for ~ expansion
This ended up being a bit of a yak shave. The basic idea in this commit is to
expand `~` in paths, but only in paths.
The way this is accomplished is by doing the expansion inside of the code that
parses literal syntax for `SyntaxType::Path`.
As a quick refresher: every command is entitled to expand its arguments in a
custom way. While this could in theory be used for general-purpose macros,
today the expansion facility is limited to syntactic hints.
For example, the syntax `where cpu > 0` expands under the hood to
`where { $it.cpu > 0 }`. This happens because the first argument to `where`
is defined as a `SyntaxType::Block`, and the parser coerces binary expressions
whose left-hand-side looks like a member into a block when the command is
expecting one.
This is mildly more magical than what most programming languages would do,
but we believe that it makes sense to allow commands to fine-tune the syntax
because of the domain nushell is in (command-line shells).
The syntactic expansions supported by this facility are relatively limited.
For example, we don't allow `$it` to become a bare word, simply because the
command asks for a string in the relevant position. That would quickly
become more confusing than it's worth.
This PR adds a new `SyntaxType` rule: `SyntaxType::Path`. When a command
declares a parameter as a `SyntaxType::Path`, string literals and bare
words passed as an argument to that parameter are processed using the
path expansion rules. Right now, that only means that `~` is expanded into
the home directory, but additional rules are possible in the future.
By restricting this expansion to a syntactic expansion when passed as an
argument to a command expecting a path, we avoid making `~` a generally
reserved character. This will also allow us to give good tab completion
for paths with `~` characters in them when a command is expecting a path.
In order to accomplish the above, this commit changes the parsing functions
to take a `Context` instead of just a `CommandRegistry`. From the perspective
of macro expansion, you can think of the `CommandRegistry` as a dictionary
of in-scope macros, and the `Context` as the compile-time state used in
expansion. This could gain additional functionality over time as we find
more uses for the expansion system.
2019-08-26 19:21:03 +00:00
|
|
|
// TODO: This is basically a legacy construct, I think
|
2019-07-04 05:11:56 +00:00
|
|
|
pub fn data_descriptors(&self) -> Vec<String> {
|
2019-05-10 16:59:12 +00:00
|
|
|
match self {
|
2019-07-03 17:37:09 +00:00
|
|
|
Value::Primitive(_) => vec![],
|
|
|
|
Value::Object(o) => o
|
|
|
|
.entries
|
|
|
|
.keys()
|
|
|
|
.into_iter()
|
|
|
|
.map(|x| x.to_string())
|
|
|
|
.collect(),
|
|
|
|
Value::Block(_) => vec![],
|
2019-05-15 22:58:44 +00:00
|
|
|
Value::List(_) => vec![],
|
2019-07-04 05:11:56 +00:00
|
|
|
Value::Binary(_) => vec![],
|
2019-05-10 16:59:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
pub(crate) fn get_data_by_key(&self, name: &str) -> Option<&Tagged<Value>> {
|
2019-05-17 15:55:50 +00:00
|
|
|
match self {
|
|
|
|
Value::Object(o) => o.get_data_by_key(name),
|
2019-06-11 06:26:03 +00:00
|
|
|
Value::List(l) => {
|
|
|
|
for item in l {
|
|
|
|
match item {
|
2019-08-01 01:58:42 +00:00
|
|
|
Tagged {
|
2019-07-08 16:44:53 +00:00
|
|
|
item: Value::Object(o),
|
|
|
|
..
|
|
|
|
} => match o.get_data_by_key(name) {
|
2019-06-11 06:26:03 +00:00
|
|
|
Some(v) => return Some(v),
|
|
|
|
None => {}
|
2019-06-13 21:47:25 +00:00
|
|
|
},
|
2019-06-11 06:26:03 +00:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2019-05-28 06:45:18 +00:00
|
|
|
_ => None,
|
2019-05-17 15:55:50 +00:00
|
|
|
}
|
2019-06-14 01:59:13 +00:00
|
|
|
}
|
|
|
|
|
2019-07-16 19:10:25 +00:00
|
|
|
#[allow(unused)]
|
2019-08-29 12:16:11 +00:00
|
|
|
pub(crate) fn get_data_by_index(&self, idx: usize) -> Option<&Tagged<Value>> {
|
2019-06-14 01:59:13 +00:00
|
|
|
match self {
|
|
|
|
Value::List(l) => l.iter().nth(idx),
|
|
|
|
_ => None,
|
|
|
|
}
|
2019-05-17 15:55:50 +00:00
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
pub fn get_data_by_path(&self, tag: Tag, path: &str) -> Option<Tagged<&Value>> {
|
2019-07-18 01:32:19 +00:00
|
|
|
let mut current = self;
|
|
|
|
for p in path.split(".") {
|
|
|
|
match current.get_data_by_key(p) {
|
|
|
|
Some(v) => current = v,
|
|
|
|
None => return None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-05 08:54:29 +00:00
|
|
|
Some(Tagged::from_item(current, tag))
|
2019-07-18 01:32:19 +00:00
|
|
|
}
|
|
|
|
|
2019-07-22 03:52:57 +00:00
|
|
|
pub fn insert_data_at_path(
|
2019-08-29 12:16:11 +00:00
|
|
|
&self,
|
2019-08-05 08:54:29 +00:00
|
|
|
tag: Tag,
|
2019-07-22 03:52:57 +00:00
|
|
|
path: &str,
|
|
|
|
new_value: Value,
|
2019-08-01 01:58:42 +00:00
|
|
|
) -> Option<Tagged<Value>> {
|
2019-07-22 03:52:57 +00:00
|
|
|
let mut new_obj = self.clone();
|
|
|
|
|
|
|
|
let split_path: Vec<_> = path.split(".").collect();
|
|
|
|
|
|
|
|
if let Value::Object(ref mut o) = new_obj {
|
|
|
|
let mut current = o;
|
2019-08-20 03:36:52 +00:00
|
|
|
|
|
|
|
if split_path.len() == 1 {
|
|
|
|
// Special case for inserting at the top level
|
|
|
|
current
|
|
|
|
.entries
|
|
|
|
.insert(path.to_string(), Tagged::from_item(new_value, tag));
|
|
|
|
return Some(Tagged::from_item(new_obj, tag));
|
|
|
|
}
|
|
|
|
|
|
|
|
for idx in 0..split_path.len() {
|
2019-07-22 03:52:57 +00:00
|
|
|
match current.entries.get_mut(split_path[idx]) {
|
|
|
|
Some(next) => {
|
|
|
|
if idx == (split_path.len() - 2) {
|
|
|
|
match &mut next.item {
|
|
|
|
Value::Object(o) => {
|
|
|
|
o.entries.insert(
|
|
|
|
split_path[idx + 1].to_string(),
|
2019-08-05 08:54:29 +00:00
|
|
|
Tagged::from_item(new_value, tag),
|
2019-07-22 03:52:57 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
2019-08-05 08:54:29 +00:00
|
|
|
return Some(Tagged::from_item(new_obj, tag));
|
2019-07-22 03:52:57 +00:00
|
|
|
} else {
|
|
|
|
match next.item {
|
|
|
|
Value::Object(ref mut o) => {
|
|
|
|
current = o;
|
|
|
|
}
|
|
|
|
_ => return None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => return None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2019-07-18 01:32:19 +00:00
|
|
|
pub fn replace_data_at_path(
|
2019-08-29 12:16:11 +00:00
|
|
|
&self,
|
2019-08-05 08:54:29 +00:00
|
|
|
tag: Tag,
|
2019-07-18 01:32:19 +00:00
|
|
|
path: &str,
|
|
|
|
replaced_value: Value,
|
2019-08-01 01:58:42 +00:00
|
|
|
) -> Option<Tagged<Value>> {
|
2019-07-18 01:32:19 +00:00
|
|
|
let mut new_obj = self.clone();
|
|
|
|
|
|
|
|
let split_path: Vec<_> = path.split(".").collect();
|
|
|
|
|
|
|
|
if let Value::Object(ref mut o) = new_obj {
|
|
|
|
let mut current = o;
|
|
|
|
for idx in 0..split_path.len() {
|
|
|
|
match current.entries.get_mut(split_path[idx]) {
|
|
|
|
Some(next) => {
|
|
|
|
if idx == (split_path.len() - 1) {
|
2019-08-05 08:54:29 +00:00
|
|
|
*next = Tagged::from_item(replaced_value, tag);
|
|
|
|
return Some(Tagged::from_item(new_obj, tag));
|
2019-07-18 01:32:19 +00:00
|
|
|
} else {
|
|
|
|
match next.item {
|
|
|
|
Value::Object(ref mut o) => {
|
|
|
|
current = o;
|
|
|
|
}
|
|
|
|
_ => return None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => return None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2019-08-29 12:16:11 +00:00
|
|
|
pub fn get_data(&self, desc: &String) -> MaybeOwned<'_, Value> {
|
2019-05-10 16:59:12 +00:00
|
|
|
match self {
|
2019-05-24 18:48:33 +00:00
|
|
|
p @ Value::Primitive(_) => MaybeOwned::Borrowed(p),
|
2019-05-10 16:59:12 +00:00
|
|
|
Value::Object(o) => o.get_data(desc),
|
2019-05-28 06:45:18 +00:00
|
|
|
Value::Block(_) => MaybeOwned::Owned(Value::nothing()),
|
2019-05-23 04:30:43 +00:00
|
|
|
Value::List(_) => MaybeOwned::Owned(Value::nothing()),
|
2019-07-04 05:11:56 +00:00
|
|
|
Value::Binary(_) => MaybeOwned::Owned(Value::nothing()),
|
2019-05-15 18:14:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn format_leaf(&self, desc: Option<&String>) -> String {
|
2019-05-10 16:59:12 +00:00
|
|
|
match self {
|
2019-06-04 21:42:31 +00:00
|
|
|
Value::Primitive(p) => p.format(desc),
|
2019-06-29 08:55:42 +00:00
|
|
|
Value::Block(b) => itertools::join(
|
|
|
|
b.expressions
|
|
|
|
.iter()
|
|
|
|
.map(|e| e.source(&b.source).to_string()),
|
|
|
|
"; ",
|
|
|
|
),
|
2019-07-30 01:54:33 +00:00
|
|
|
Value::Object(_) => format!("[{}]", self.type_name()),
|
|
|
|
Value::List(l) => format!(
|
|
|
|
"[{} {}]",
|
|
|
|
l.len(),
|
|
|
|
if l.len() == 1 { "item" } else { "items" }
|
|
|
|
),
|
2019-07-04 05:11:56 +00:00
|
|
|
Value::Binary(_) => format!("<binary>"),
|
2019-05-10 16:59:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn style_leaf(&self) -> &'static str {
|
2019-08-24 05:31:50 +00:00
|
|
|
match self {
|
|
|
|
Value::Primitive(p) => p.style(),
|
2019-08-26 18:19:05 +00:00
|
|
|
_ => "",
|
2019-08-24 05:31:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-22 03:43:37 +00:00
|
|
|
#[allow(unused)]
|
2019-08-30 17:29:04 +00:00
|
|
|
pub(crate) fn compare(
|
|
|
|
&self,
|
|
|
|
operator: &Operator,
|
|
|
|
other: &Value,
|
|
|
|
) -> Result<bool, (String, String)> {
|
2019-05-28 06:45:18 +00:00
|
|
|
match operator {
|
|
|
|
_ => {
|
|
|
|
let coerced = coerce_compare(self, other)?;
|
|
|
|
let ordering = coerced.compare();
|
|
|
|
|
|
|
|
use std::cmp::Ordering;
|
|
|
|
|
|
|
|
let result = match (operator, ordering) {
|
|
|
|
(Operator::Equal, Ordering::Equal) => true,
|
2019-05-30 04:19:46 +00:00
|
|
|
(Operator::NotEqual, Ordering::Less)
|
|
|
|
| (Operator::NotEqual, Ordering::Greater) => true,
|
2019-05-28 06:45:18 +00:00
|
|
|
(Operator::LessThan, Ordering::Less) => true,
|
|
|
|
(Operator::GreaterThan, Ordering::Greater) => true,
|
|
|
|
(Operator::GreaterThanOrEqual, Ordering::Greater)
|
|
|
|
| (Operator::GreaterThanOrEqual, Ordering::Equal) => true,
|
|
|
|
(Operator::LessThanOrEqual, Ordering::Less)
|
|
|
|
| (Operator::LessThanOrEqual, Ordering::Equal) => true,
|
|
|
|
_ => false,
|
|
|
|
};
|
|
|
|
|
2019-06-24 00:55:31 +00:00
|
|
|
Ok(result)
|
2019-05-28 06:45:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-01 05:50:16 +00:00
|
|
|
#[allow(unused)]
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn is_string(&self, expected: &str) -> bool {
|
2019-06-01 05:50:16 +00:00
|
|
|
match self {
|
|
|
|
Value::Primitive(Primitive::String(s)) if s == expected => true,
|
|
|
|
other => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 11:08:28 +00:00
|
|
|
// pub(crate) fn as_pair(&self) -> Result<(Tagged<Value>, Tagged<Value>), ShellError> {
|
2019-08-09 20:49:43 +00:00
|
|
|
// match self {
|
|
|
|
// Value::List(list) if list.len() == 2 => Ok((list[0].clone(), list[1].clone())),
|
|
|
|
// other => Err(ShellError::string(format!(
|
|
|
|
// "Expected pair, got {:?}",
|
|
|
|
// other
|
|
|
|
// ))),
|
|
|
|
// }
|
|
|
|
// }
|
2019-06-01 05:50:16 +00:00
|
|
|
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn as_string(&self) -> Result<String, ShellError> {
|
2019-05-11 22:59:57 +00:00
|
|
|
match self {
|
2019-06-22 22:20:13 +00:00
|
|
|
Value::Primitive(Primitive::String(s)) => Ok(s.clone()),
|
2019-06-19 05:51:24 +00:00
|
|
|
Value::Primitive(Primitive::Boolean(x)) => Ok(format!("{}", x)),
|
2019-08-30 17:29:04 +00:00
|
|
|
Value::Primitive(Primitive::Decimal(x)) => Ok(format!("{}", x)),
|
2019-06-19 05:51:24 +00:00
|
|
|
Value::Primitive(Primitive::Int(x)) => Ok(format!("{}", x)),
|
|
|
|
Value::Primitive(Primitive::Bytes(x)) => Ok(format!("{}", x)),
|
2019-05-11 22:59:57 +00:00
|
|
|
// TODO: this should definitely be more general with better errors
|
2019-05-13 17:30:51 +00:00
|
|
|
other => Err(ShellError::string(format!(
|
|
|
|
"Expected string, got {:?}",
|
|
|
|
other
|
|
|
|
))),
|
2019-05-11 22:59:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn as_i64(&self) -> Result<i64, ShellError> {
|
2019-05-26 06:54:41 +00:00
|
|
|
match self {
|
2019-05-28 06:45:18 +00:00
|
|
|
Value::Primitive(Primitive::Int(i)) => Ok(*i),
|
2019-06-30 06:46:49 +00:00
|
|
|
Value::Primitive(Primitive::Bytes(b)) => Ok(*b as i64),
|
2019-05-26 06:54:41 +00:00
|
|
|
// TODO: this should definitely be more general with better errors
|
|
|
|
other => Err(ShellError::string(format!(
|
2019-05-28 06:45:18 +00:00
|
|
|
"Expected integer, got {:?}",
|
2019-05-26 06:54:41 +00:00
|
|
|
other
|
|
|
|
))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn is_true(&self) -> bool {
|
2019-05-28 06:45:18 +00:00
|
|
|
match self {
|
|
|
|
Value::Primitive(Primitive::Boolean(true)) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-27 04:56:48 +00:00
|
|
|
pub fn string(s: impl Into<String>) -> Value {
|
2019-05-10 16:59:12 +00:00
|
|
|
Value::Primitive(Primitive::String(s.into()))
|
|
|
|
}
|
|
|
|
|
Add support for ~ expansion
This ended up being a bit of a yak shave. The basic idea in this commit is to
expand `~` in paths, but only in paths.
The way this is accomplished is by doing the expansion inside of the code that
parses literal syntax for `SyntaxType::Path`.
As a quick refresher: every command is entitled to expand its arguments in a
custom way. While this could in theory be used for general-purpose macros,
today the expansion facility is limited to syntactic hints.
For example, the syntax `where cpu > 0` expands under the hood to
`where { $it.cpu > 0 }`. This happens because the first argument to `where`
is defined as a `SyntaxType::Block`, and the parser coerces binary expressions
whose left-hand-side looks like a member into a block when the command is
expecting one.
This is mildly more magical than what most programming languages would do,
but we believe that it makes sense to allow commands to fine-tune the syntax
because of the domain nushell is in (command-line shells).
The syntactic expansions supported by this facility are relatively limited.
For example, we don't allow `$it` to become a bare word, simply because the
command asks for a string in the relevant position. That would quickly
become more confusing than it's worth.
This PR adds a new `SyntaxType` rule: `SyntaxType::Path`. When a command
declares a parameter as a `SyntaxType::Path`, string literals and bare
words passed as an argument to that parameter are processed using the
path expansion rules. Right now, that only means that `~` is expanded into
the home directory, but additional rules are possible in the future.
By restricting this expansion to a syntactic expansion when passed as an
argument to a command expecting a path, we avoid making `~` a generally
reserved character. This will also allow us to give good tab completion
for paths with `~` characters in them when a command is expecting a path.
In order to accomplish the above, this commit changes the parsing functions
to take a `Context` instead of just a `CommandRegistry`. From the perspective
of macro expansion, you can think of the `CommandRegistry` as a dictionary
of in-scope macros, and the `Context` as the compile-time state used in
expansion. This could gain additional functionality over time as we find
more uses for the expansion system.
2019-08-26 19:21:03 +00:00
|
|
|
pub fn path(s: impl Into<PathBuf>) -> Value {
|
|
|
|
Value::Primitive(Primitive::Path(s.into()))
|
|
|
|
}
|
|
|
|
|
2019-06-30 06:46:49 +00:00
|
|
|
pub fn bytes(s: impl Into<u64>) -> Value {
|
2019-05-15 18:14:51 +00:00
|
|
|
Value::Primitive(Primitive::Bytes(s.into()))
|
|
|
|
}
|
|
|
|
|
2019-06-27 04:56:48 +00:00
|
|
|
pub fn int(s: impl Into<i64>) -> Value {
|
2019-05-10 16:59:12 +00:00
|
|
|
Value::Primitive(Primitive::Int(s.into()))
|
|
|
|
}
|
|
|
|
|
2019-08-30 17:29:04 +00:00
|
|
|
pub fn decimal(s: impl Into<Decimal>) -> Value {
|
|
|
|
Value::Primitive(Primitive::Decimal(s.into()))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn number(s: impl Into<Number>) -> Value {
|
|
|
|
let num = s.into();
|
|
|
|
|
|
|
|
match num {
|
|
|
|
Number::Int(int) => Value::int(int),
|
|
|
|
Number::Decimal(decimal) => Value::decimal(decimal),
|
|
|
|
}
|
2019-05-17 16:59:25 +00:00
|
|
|
}
|
|
|
|
|
2019-06-27 04:56:48 +00:00
|
|
|
pub fn boolean(s: impl Into<bool>) -> Value {
|
2019-05-16 02:42:44 +00:00
|
|
|
Value::Primitive(Primitive::Boolean(s.into()))
|
|
|
|
}
|
|
|
|
|
2019-06-27 04:56:48 +00:00
|
|
|
pub fn system_date(s: SystemTime) -> Value {
|
2019-05-15 18:14:51 +00:00
|
|
|
Value::Primitive(Primitive::Date(s.into()))
|
|
|
|
}
|
|
|
|
|
2019-06-01 05:50:16 +00:00
|
|
|
#[allow(unused)]
|
2019-06-27 04:56:48 +00:00
|
|
|
pub fn date_from_str(s: &str) -> Result<Value, ShellError> {
|
2019-06-01 05:50:16 +00:00
|
|
|
let date = DateTime::parse_from_rfc3339(s)
|
|
|
|
.map_err(|err| ShellError::string(&format!("Date parse error: {}", err)))?;
|
|
|
|
|
|
|
|
let date = date.with_timezone(&chrono::offset::Utc);
|
|
|
|
|
|
|
|
Ok(Value::Primitive(Primitive::Date(date)))
|
|
|
|
}
|
|
|
|
|
2019-06-27 04:56:48 +00:00
|
|
|
pub fn nothing() -> Value {
|
2019-05-10 16:59:12 +00:00
|
|
|
Value::Primitive(Primitive::Nothing)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add support for ~ expansion
This ended up being a bit of a yak shave. The basic idea in this commit is to
expand `~` in paths, but only in paths.
The way this is accomplished is by doing the expansion inside of the code that
parses literal syntax for `SyntaxType::Path`.
As a quick refresher: every command is entitled to expand its arguments in a
custom way. While this could in theory be used for general-purpose macros,
today the expansion facility is limited to syntactic hints.
For example, the syntax `where cpu > 0` expands under the hood to
`where { $it.cpu > 0 }`. This happens because the first argument to `where`
is defined as a `SyntaxType::Block`, and the parser coerces binary expressions
whose left-hand-side looks like a member into a block when the command is
expecting one.
This is mildly more magical than what most programming languages would do,
but we believe that it makes sense to allow commands to fine-tune the syntax
because of the domain nushell is in (command-line shells).
The syntactic expansions supported by this facility are relatively limited.
For example, we don't allow `$it` to become a bare word, simply because the
command asks for a string in the relevant position. That would quickly
become more confusing than it's worth.
This PR adds a new `SyntaxType` rule: `SyntaxType::Path`. When a command
declares a parameter as a `SyntaxType::Path`, string literals and bare
words passed as an argument to that parameter are processed using the
path expansion rules. Right now, that only means that `~` is expanded into
the home directory, but additional rules are possible in the future.
By restricting this expansion to a syntactic expansion when passed as an
argument to a command expecting a path, we avoid making `~` a generally
reserved character. This will also allow us to give good tab completion
for paths with `~` characters in them when a command is expecting a path.
In order to accomplish the above, this commit changes the parsing functions
to take a `Context` instead of just a `CommandRegistry`. From the perspective
of macro expansion, you can think of the `CommandRegistry` as a dictionary
of in-scope macros, and the `Context` as the compile-time state used in
expansion. This could gain additional functionality over time as we find
more uses for the expansion system.
2019-08-26 19:21:03 +00:00
|
|
|
impl Tagged<Value> {
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn as_path(&self) -> Result<PathBuf, ShellError> {
|
Add support for ~ expansion
This ended up being a bit of a yak shave. The basic idea in this commit is to
expand `~` in paths, but only in paths.
The way this is accomplished is by doing the expansion inside of the code that
parses literal syntax for `SyntaxType::Path`.
As a quick refresher: every command is entitled to expand its arguments in a
custom way. While this could in theory be used for general-purpose macros,
today the expansion facility is limited to syntactic hints.
For example, the syntax `where cpu > 0` expands under the hood to
`where { $it.cpu > 0 }`. This happens because the first argument to `where`
is defined as a `SyntaxType::Block`, and the parser coerces binary expressions
whose left-hand-side looks like a member into a block when the command is
expecting one.
This is mildly more magical than what most programming languages would do,
but we believe that it makes sense to allow commands to fine-tune the syntax
because of the domain nushell is in (command-line shells).
The syntactic expansions supported by this facility are relatively limited.
For example, we don't allow `$it` to become a bare word, simply because the
command asks for a string in the relevant position. That would quickly
become more confusing than it's worth.
This PR adds a new `SyntaxType` rule: `SyntaxType::Path`. When a command
declares a parameter as a `SyntaxType::Path`, string literals and bare
words passed as an argument to that parameter are processed using the
path expansion rules. Right now, that only means that `~` is expanded into
the home directory, but additional rules are possible in the future.
By restricting this expansion to a syntactic expansion when passed as an
argument to a command expecting a path, we avoid making `~` a generally
reserved character. This will also allow us to give good tab completion
for paths with `~` characters in them when a command is expecting a path.
In order to accomplish the above, this commit changes the parsing functions
to take a `Context` instead of just a `CommandRegistry`. From the perspective
of macro expansion, you can think of the `CommandRegistry` as a dictionary
of in-scope macros, and the `Context` as the compile-time state used in
expansion. This could gain additional functionality over time as we find
more uses for the expansion system.
2019-08-26 19:21:03 +00:00
|
|
|
match self.item() {
|
|
|
|
Value::Primitive(Primitive::Path(path)) => Ok(path.clone()),
|
|
|
|
other => Err(ShellError::type_error(
|
|
|
|
"Path",
|
|
|
|
other.type_name().tagged(self.span()),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn select_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> Tagged<Value> {
|
2019-08-05 08:54:29 +00:00
|
|
|
let mut out = TaggedDictBuilder::new(tag);
|
2019-05-15 18:14:51 +00:00
|
|
|
|
|
|
|
let descs = obj.data_descriptors();
|
|
|
|
|
|
|
|
for field in fields {
|
2019-07-13 02:07:06 +00:00
|
|
|
match descs.iter().find(|d| *d == field) {
|
2019-07-09 04:31:26 +00:00
|
|
|
None => out.insert(field, Value::nothing()),
|
|
|
|
Some(desc) => out.insert(desc.clone(), obj.get_data(desc).borrow().clone()),
|
2019-05-15 18:14:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-01 01:58:42 +00:00
|
|
|
out.into_tagged_value()
|
2019-05-10 16:59:12 +00:00
|
|
|
}
|
|
|
|
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn reject_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> Tagged<Value> {
|
2019-08-05 08:54:29 +00:00
|
|
|
let mut out = TaggedDictBuilder::new(tag);
|
2019-05-15 21:44:06 +00:00
|
|
|
|
|
|
|
let descs = obj.data_descriptors();
|
|
|
|
|
|
|
|
for desc in descs {
|
2019-08-29 02:44:08 +00:00
|
|
|
if fields.iter().any(|field| *field == desc) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
out.insert(desc.clone(), obj.get_data(&desc).borrow().clone())
|
2019-05-15 21:44:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-01 01:58:42 +00:00
|
|
|
out.into_tagged_value()
|
2019-05-15 21:44:06 +00:00
|
|
|
}
|
2019-05-16 02:42:44 +00:00
|
|
|
|
2019-05-28 06:45:18 +00:00
|
|
|
#[allow(unused)]
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool {
|
2019-05-16 02:42:44 +00:00
|
|
|
let descs = obj.data_descriptors();
|
2019-07-03 17:37:09 +00:00
|
|
|
match descs.iter().find(|d| *d == field) {
|
2019-05-16 02:42:44 +00:00
|
|
|
None => false,
|
|
|
|
Some(desc) => {
|
2019-07-08 16:44:53 +00:00
|
|
|
let v = obj.get_data(desc).borrow().clone();
|
2019-05-16 02:42:44 +00:00
|
|
|
|
|
|
|
match v {
|
2019-05-16 21:43:36 +00:00
|
|
|
Value::Primitive(Primitive::Boolean(b)) => match (op, rhs) {
|
2019-05-18 01:27:31 +00:00
|
|
|
(Operator::Equal, Value::Primitive(Primitive::Boolean(b2))) => b == *b2,
|
|
|
|
(Operator::NotEqual, Value::Primitive(Primitive::Boolean(b2))) => b != *b2,
|
2019-05-16 21:43:36 +00:00
|
|
|
_ => false,
|
|
|
|
},
|
|
|
|
Value::Primitive(Primitive::Bytes(i)) => match (op, rhs) {
|
2019-06-30 06:46:49 +00:00
|
|
|
(Operator::LessThan, Value::Primitive(Primitive::Int(i2))) => i < (*i2 as u64),
|
2019-05-22 07:12:03 +00:00
|
|
|
(Operator::GreaterThan, Value::Primitive(Primitive::Int(i2))) => {
|
2019-06-30 06:46:49 +00:00
|
|
|
i > (*i2 as u64)
|
2019-05-22 07:12:03 +00:00
|
|
|
}
|
|
|
|
(Operator::LessThanOrEqual, Value::Primitive(Primitive::Int(i2))) => {
|
2019-06-30 06:46:49 +00:00
|
|
|
i <= (*i2 as u64)
|
2019-05-22 07:12:03 +00:00
|
|
|
}
|
|
|
|
(Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Int(i2))) => {
|
2019-06-30 06:46:49 +00:00
|
|
|
i >= (*i2 as u64)
|
2019-05-22 07:12:03 +00:00
|
|
|
}
|
2019-06-30 06:46:49 +00:00
|
|
|
(Operator::Equal, Value::Primitive(Primitive::Int(i2))) => i == (*i2 as u64),
|
|
|
|
(Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => i != (*i2 as u64),
|
2019-05-16 21:43:36 +00:00
|
|
|
_ => false,
|
|
|
|
},
|
|
|
|
Value::Primitive(Primitive::Int(i)) => match (op, rhs) {
|
2019-05-18 01:27:31 +00:00
|
|
|
(Operator::LessThan, Value::Primitive(Primitive::Int(i2))) => i < *i2,
|
|
|
|
(Operator::GreaterThan, Value::Primitive(Primitive::Int(i2))) => i > *i2,
|
|
|
|
(Operator::LessThanOrEqual, Value::Primitive(Primitive::Int(i2))) => i <= *i2,
|
2019-05-22 07:12:03 +00:00
|
|
|
(Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Int(i2))) => {
|
|
|
|
i >= *i2
|
|
|
|
}
|
2019-05-18 01:27:31 +00:00
|
|
|
(Operator::Equal, Value::Primitive(Primitive::Int(i2))) => i == *i2,
|
|
|
|
(Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => i != *i2,
|
2019-05-16 21:43:36 +00:00
|
|
|
_ => false,
|
|
|
|
},
|
2019-08-30 17:29:04 +00:00
|
|
|
Value::Primitive(Primitive::Decimal(i)) => match (op, rhs) {
|
|
|
|
(Operator::LessThan, Value::Primitive(Primitive::Decimal(i2))) => i < *i2,
|
|
|
|
(Operator::GreaterThan, Value::Primitive(Primitive::Decimal(i2))) => i > *i2,
|
|
|
|
(Operator::LessThanOrEqual, Value::Primitive(Primitive::Decimal(i2))) => {
|
|
|
|
i <= *i2
|
|
|
|
}
|
|
|
|
(Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Decimal(i2))) => {
|
2019-05-22 07:12:03 +00:00
|
|
|
i >= *i2
|
|
|
|
}
|
2019-08-30 17:29:04 +00:00
|
|
|
(Operator::Equal, Value::Primitive(Primitive::Decimal(i2))) => i == *i2,
|
|
|
|
(Operator::NotEqual, Value::Primitive(Primitive::Decimal(i2))) => i != *i2,
|
2019-05-22 07:12:03 +00:00
|
|
|
(Operator::LessThan, Value::Primitive(Primitive::Int(i2))) => {
|
2019-08-30 17:29:04 +00:00
|
|
|
i < Decimal::from(*i2)
|
2019-05-22 07:12:03 +00:00
|
|
|
}
|
|
|
|
(Operator::GreaterThan, Value::Primitive(Primitive::Int(i2))) => {
|
2019-08-30 17:29:04 +00:00
|
|
|
i > Decimal::from(*i2)
|
2019-05-22 07:12:03 +00:00
|
|
|
}
|
|
|
|
(Operator::LessThanOrEqual, Value::Primitive(Primitive::Int(i2))) => {
|
2019-08-30 17:29:04 +00:00
|
|
|
i <= Decimal::from(*i2)
|
2019-05-22 07:12:03 +00:00
|
|
|
}
|
|
|
|
(Operator::GreaterThanOrEqual, Value::Primitive(Primitive::Int(i2))) => {
|
2019-08-30 17:29:04 +00:00
|
|
|
i >= Decimal::from(*i2)
|
2019-05-22 07:12:03 +00:00
|
|
|
}
|
|
|
|
(Operator::Equal, Value::Primitive(Primitive::Int(i2))) => {
|
2019-08-30 17:29:04 +00:00
|
|
|
i == Decimal::from(*i2)
|
2019-05-22 07:12:03 +00:00
|
|
|
}
|
|
|
|
(Operator::NotEqual, Value::Primitive(Primitive::Int(i2))) => {
|
2019-08-30 17:29:04 +00:00
|
|
|
i != Decimal::from(*i2)
|
2019-05-22 07:12:03 +00:00
|
|
|
}
|
2019-05-17 16:59:25 +00:00
|
|
|
|
|
|
|
_ => false,
|
|
|
|
},
|
2019-05-16 21:43:36 +00:00
|
|
|
Value::Primitive(Primitive::String(s)) => match (op, rhs) {
|
2019-05-18 01:27:31 +00:00
|
|
|
(Operator::Equal, Value::Primitive(Primitive::String(s2))) => s == *s2,
|
|
|
|
(Operator::NotEqual, Value::Primitive(Primitive::String(s2))) => s != *s2,
|
2019-05-16 21:43:36 +00:00
|
|
|
_ => false,
|
|
|
|
},
|
2019-05-16 02:42:44 +00:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-28 06:45:18 +00:00
|
|
|
|
|
|
|
enum CompareValues {
|
|
|
|
Ints(i64, i64),
|
2019-08-30 17:29:04 +00:00
|
|
|
Decimals(Decimal, Decimal),
|
|
|
|
Bytes(u64, u64),
|
2019-05-28 06:45:18 +00:00
|
|
|
String(String, String),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CompareValues {
|
|
|
|
fn compare(&self) -> std::cmp::Ordering {
|
|
|
|
match self {
|
|
|
|
CompareValues::Ints(left, right) => left.cmp(right),
|
2019-08-30 17:29:04 +00:00
|
|
|
CompareValues::Decimals(left, right) => left.cmp(right),
|
2019-05-28 06:45:18 +00:00
|
|
|
CompareValues::Bytes(left, right) => left.cmp(right),
|
|
|
|
CompareValues::String(left, right) => left.cmp(right),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-24 00:55:31 +00:00
|
|
|
fn coerce_compare(left: &Value, right: &Value) -> Result<CompareValues, (String, String)> {
|
2019-05-28 06:45:18 +00:00
|
|
|
match (left, right) {
|
|
|
|
(Value::Primitive(left), Value::Primitive(right)) => coerce_compare_primitive(left, right),
|
|
|
|
|
2019-06-24 00:55:31 +00:00
|
|
|
_ => Err((left.type_name(), right.type_name())),
|
2019-05-28 06:45:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-24 00:55:31 +00:00
|
|
|
fn coerce_compare_primitive(
|
|
|
|
left: &Primitive,
|
|
|
|
right: &Primitive,
|
|
|
|
) -> Result<CompareValues, (String, String)> {
|
2019-05-28 06:45:18 +00:00
|
|
|
use Primitive::*;
|
|
|
|
|
2019-06-24 00:55:31 +00:00
|
|
|
Ok(match (left, right) {
|
|
|
|
(Int(left), Int(right)) => CompareValues::Ints(*left, *right),
|
2019-08-30 17:29:04 +00:00
|
|
|
(Decimal(left), Decimal(right)) => CompareValues::Decimals(*left, *right),
|
|
|
|
(Decimal(left), Int(right)) => CompareValues::Decimals(*left, (*right).into()),
|
|
|
|
(Int(left), Decimal(right)) => CompareValues::Decimals((*left).into(), *right),
|
|
|
|
(Int(left), Bytes(right)) => CompareValues::Bytes(*left as u64, *right),
|
|
|
|
(Bytes(left), Int(right)) => CompareValues::Bytes(*left, *right as u64),
|
2019-06-24 00:55:31 +00:00
|
|
|
(String(left), String(right)) => CompareValues::String(left.clone(), right.clone()),
|
|
|
|
_ => return Err((left.type_name(), right.type_name())),
|
|
|
|
})
|
2019-05-28 06:45:18 +00:00
|
|
|
}
|