2021-01-29 13:43:35 +00:00
|
|
|
pub mod shape;
|
2019-11-04 15:47:03 +00:00
|
|
|
|
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
|
|
|
use bigdecimal::BigDecimal;
|
2020-12-12 18:18:03 +00:00
|
|
|
use chrono::{DateTime, FixedOffset, Utc};
|
2019-05-26 06:54:41 +00:00
|
|
|
use derive_new::new;
|
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
|
|
|
use nu_errors::ShellError;
|
|
|
|
use nu_protocol::{
|
2020-04-13 07:59:57 +00:00
|
|
|
hir, Primitive, ShellTypeName, SpannedTypeName, TaggedDictBuilder, UntaggedValue, Value,
|
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
|
|
|
};
|
2020-07-10 17:48:11 +00:00
|
|
|
use nu_source::{Span, Tag};
|
2019-12-09 18:52:01 +00:00
|
|
|
use nu_value_ext::ValueExt;
|
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
|
|
|
use num_bigint::BigInt;
|
2019-08-02 19:15:07 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
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,
|
2020-04-18 01:50:58 +00:00
|
|
|
pub(crate) operator: hir::Operator,
|
2019-08-29 11:08:28 +00:00
|
|
|
pub(crate) right: Value,
|
2019-05-26 06:54:41 +00:00
|
|
|
}
|
|
|
|
|
2019-08-02 19:15:07 +00:00
|
|
|
#[derive(Serialize, Deserialize)]
|
2019-07-03 20:31:15 +00:00
|
|
|
pub enum Switch {
|
|
|
|
Present,
|
|
|
|
Absent,
|
|
|
|
}
|
|
|
|
|
2019-11-21 14:33:14 +00:00
|
|
|
impl std::convert::TryFrom<Option<&Value>> for Switch {
|
2019-07-03 20:31:15 +00:00
|
|
|
type Error = ShellError;
|
|
|
|
|
2019-11-21 14:33:14 +00:00
|
|
|
fn try_from(value: Option<&Value>) -> Result<Switch, ShellError> {
|
2019-07-03 20:31:15 +00:00
|
|
|
match value {
|
|
|
|
None => Ok(Switch::Absent),
|
2019-11-21 14:33:14 +00:00
|
|
|
Some(value) => match &value.value {
|
|
|
|
UntaggedValue::Primitive(Primitive::Boolean(true)) => Ok(Switch::Present),
|
|
|
|
_ => Err(ShellError::type_error("Boolean", value.spanned_type_name())),
|
2019-07-03 20:31:15 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-18 07:00:02 +00:00
|
|
|
pub fn select_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> 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();
|
|
|
|
|
2020-01-11 06:45:09 +00:00
|
|
|
for column_name in fields {
|
|
|
|
match descs.iter().find(|d| *d == column_name) {
|
|
|
|
None => out.insert_untagged(column_name, UntaggedValue::nothing()),
|
2019-11-21 14:33:14 +00:00
|
|
|
Some(desc) => out.insert_value(desc.clone(), obj.get_data(desc).borrow().clone()),
|
2019-05-15 18:14:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 14:33:14 +00:00
|
|
|
out.into_value()
|
2019-05-10 16:59:12 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 07:00:02 +00:00
|
|
|
pub fn reject_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> 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 {
|
2019-11-21 14:33:14 +00:00
|
|
|
out.insert_value(desc.clone(), obj.get_data(&desc).borrow().clone())
|
2019-05-15 21:44:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 14:33:14 +00:00
|
|
|
out.into_value()
|
2019-05-15 21:44:06 +00:00
|
|
|
}
|
2019-05-16 02:42:44 +00:00
|
|
|
|
2020-08-18 07:00:02 +00:00
|
|
|
pub enum CompareValues {
|
2021-05-14 08:35:09 +00:00
|
|
|
Ints(i64, i64),
|
|
|
|
Filesizes(u64, u64),
|
|
|
|
BigInts(BigInt, BigInt),
|
2019-09-01 16:20:31 +00:00
|
|
|
Decimals(BigDecimal, BigDecimal),
|
2019-05-28 06:45:18 +00:00
|
|
|
String(String, String),
|
2020-12-12 18:18:03 +00:00
|
|
|
Date(DateTime<FixedOffset>, DateTime<FixedOffset>),
|
|
|
|
DateDuration(DateTime<FixedOffset>, BigInt),
|
2020-04-21 00:30:01 +00:00
|
|
|
Booleans(bool, bool),
|
2019-05-28 06:45:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CompareValues {
|
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
|
|
|
pub fn compare(&self) -> std::cmp::Ordering {
|
2019-05-28 06:45:18 +00:00
|
|
|
match self {
|
2021-05-14 08:35:09 +00:00
|
|
|
CompareValues::BigInts(left, right) => left.cmp(right),
|
2019-05-28 06:45:18 +00:00
|
|
|
CompareValues::Ints(left, right) => left.cmp(right),
|
2021-05-14 08:35:09 +00:00
|
|
|
CompareValues::Filesizes(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::String(left, right) => left.cmp(right),
|
2019-11-17 05:48:48 +00:00
|
|
|
CompareValues::Date(left, right) => left.cmp(right),
|
|
|
|
CompareValues::DateDuration(left, right) => {
|
2020-07-10 17:48:11 +00:00
|
|
|
// FIXME: Not sure if I could do something better with the Span.
|
|
|
|
let duration = Primitive::into_chrono_duration(
|
|
|
|
Primitive::Duration(right.clone()),
|
|
|
|
Span::unknown(),
|
|
|
|
)
|
|
|
|
.expect("Could not convert nushell Duration into chrono Duration.");
|
2020-12-12 18:18:03 +00:00
|
|
|
let right: DateTime<FixedOffset> = Utc::now()
|
2020-07-10 17:48:11 +00:00
|
|
|
.checked_sub_signed(duration)
|
2020-12-12 18:18:03 +00:00
|
|
|
.expect("Data overflow")
|
|
|
|
.into();
|
2019-11-17 05:48:48 +00:00
|
|
|
right.cmp(left)
|
|
|
|
}
|
2020-04-21 00:30:01 +00:00
|
|
|
CompareValues::Booleans(left, right) => left.cmp(right),
|
2019-05-28 06:45:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-18 07:00:02 +00:00
|
|
|
pub fn coerce_compare(
|
2019-11-21 17:18:00 +00:00
|
|
|
left: &UntaggedValue,
|
|
|
|
right: &UntaggedValue,
|
2019-11-04 15:47:03 +00:00
|
|
|
) -> Result<CompareValues, (&'static str, &'static str)> {
|
2019-11-21 17:18:00 +00:00
|
|
|
match (left, right) {
|
2019-11-21 14:33:14 +00:00
|
|
|
(UntaggedValue::Primitive(left), UntaggedValue::Primitive(right)) => {
|
|
|
|
coerce_compare_primitive(left, right)
|
|
|
|
}
|
2019-05-28 06:45:18 +00:00
|
|
|
|
2019-06-24 00:55:31 +00:00
|
|
|
_ => Err((left.type_name(), right.type_name())),
|
2019-05-28 06:45:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-07 17:30:11 +00:00
|
|
|
pub fn coerce_compare_primitive(
|
2019-06-24 00:55:31 +00:00
|
|
|
left: &Primitive,
|
|
|
|
right: &Primitive,
|
2019-11-04 15:47:03 +00:00
|
|
|
) -> Result<CompareValues, (&'static str, &'static str)> {
|
2019-05-28 06:45:18 +00:00
|
|
|
use Primitive::*;
|
|
|
|
|
2019-06-24 00:55:31 +00:00
|
|
|
Ok(match (left, right) {
|
2021-05-14 08:35:09 +00:00
|
|
|
(Int(left), Int(right)) => CompareValues::Ints(*left, *right),
|
|
|
|
(Int(left), BigInt(right)) => {
|
|
|
|
CompareValues::BigInts(num_bigint::BigInt::from(*left), right.clone())
|
|
|
|
}
|
|
|
|
(BigInt(left), Int(right)) => {
|
|
|
|
CompareValues::BigInts(left.clone(), num_bigint::BigInt::from(*right))
|
|
|
|
}
|
|
|
|
(BigInt(left), BigInt(right)) => CompareValues::BigInts(left.clone(), right.clone()),
|
|
|
|
|
2019-09-01 16:20:31 +00:00
|
|
|
(Int(left), Decimal(right)) => {
|
2021-05-14 08:35:09 +00:00
|
|
|
CompareValues::Decimals(BigDecimal::from(*left), right.clone())
|
|
|
|
}
|
|
|
|
(BigInt(left), Decimal(right)) => {
|
|
|
|
CompareValues::Decimals(BigDecimal::from(left.clone()), right.clone())
|
2019-09-01 16:20:31 +00:00
|
|
|
}
|
|
|
|
(Decimal(left), Decimal(right)) => CompareValues::Decimals(left.clone(), right.clone()),
|
|
|
|
(Decimal(left), Int(right)) => {
|
2021-05-14 08:35:09 +00:00
|
|
|
CompareValues::Decimals(left.clone(), BigDecimal::from(*right))
|
2019-09-01 16:20:31 +00:00
|
|
|
}
|
2021-05-14 08:35:09 +00:00
|
|
|
(Decimal(left), BigInt(right)) => {
|
2021-01-29 22:35:18 +00:00
|
|
|
CompareValues::Decimals(left.clone(), BigDecimal::from(right.clone()))
|
2019-08-30 23:37:23 +00:00
|
|
|
}
|
2021-05-14 08:35:09 +00:00
|
|
|
(Decimal(left), Filesize(right)) => {
|
|
|
|
CompareValues::Decimals(left.clone(), BigDecimal::from(*right))
|
|
|
|
}
|
|
|
|
(Filesize(left), Filesize(right)) => CompareValues::Filesizes(*left, *right),
|
2020-07-11 02:17:37 +00:00
|
|
|
(Filesize(left), Decimal(right)) => {
|
2021-05-14 08:35:09 +00:00
|
|
|
CompareValues::Decimals(BigDecimal::from(*left), right.clone())
|
2019-08-30 23:37:23 +00:00
|
|
|
}
|
2020-05-18 19:18:46 +00:00
|
|
|
(Nothing, Nothing) => CompareValues::Booleans(true, true),
|
2019-06-24 00:55:31 +00:00
|
|
|
(String(left), String(right)) => CompareValues::String(left.clone(), right.clone()),
|
2019-12-06 15:28:26 +00:00
|
|
|
(Date(left), Date(right)) => CompareValues::Date(*left, *right),
|
2020-07-10 17:48:11 +00:00
|
|
|
(Date(left), Duration(right)) => CompareValues::DateDuration(*left, right.clone()),
|
2020-04-21 00:30:01 +00:00
|
|
|
(Boolean(left), Boolean(right)) => CompareValues::Booleans(*left, *right),
|
2021-03-05 20:07:54 +00:00
|
|
|
(Boolean(left), Nothing) => CompareValues::Booleans(*left, false),
|
|
|
|
(Nothing, Boolean(right)) => CompareValues::Booleans(false, *right),
|
2021-01-08 07:30:41 +00:00
|
|
|
(FilePath(left), String(right)) => {
|
2020-12-28 20:52:28 +00:00
|
|
|
CompareValues::String(left.as_path().display().to_string(), right.clone())
|
|
|
|
}
|
2021-01-08 07:30:41 +00:00
|
|
|
(String(left), FilePath(right)) => {
|
2020-12-28 20:52:28 +00:00
|
|
|
CompareValues::String(left.clone(), right.as_path().display().to_string())
|
|
|
|
}
|
2019-06-24 00:55:31 +00:00
|
|
|
_ => return Err((left.type_name(), right.type_name())),
|
|
|
|
})
|
2019-05-28 06:45:18 +00:00
|
|
|
}
|
2019-10-18 12:08:04 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
|
|
|
use nu_errors::ShellError;
|
2020-10-09 01:04:19 +00:00
|
|
|
use nu_protocol::UntaggedValue;
|
|
|
|
use nu_source::SpannedItem;
|
|
|
|
use nu_test_support::value::*;
|
|
|
|
use nu_value_ext::ValueExt;
|
2019-10-18 12:08:04 +00:00
|
|
|
|
2020-10-09 01:04:19 +00:00
|
|
|
use indexmap::indexmap;
|
2019-10-18 12:08:04 +00:00
|
|
|
|
2019-10-20 11:55:56 +00:00
|
|
|
#[test]
|
2020-01-02 07:07:17 +00:00
|
|
|
fn gets_matching_field_from_a_row() -> Result<(), ShellError> {
|
2019-12-04 19:52:31 +00:00
|
|
|
let row = UntaggedValue::row(indexmap! {
|
2019-12-31 07:36:08 +00:00
|
|
|
"amigos".into() => table(&[string("andres"),string("jonathan"),string("yehuda")])
|
2019-11-21 14:33:14 +00:00
|
|
|
})
|
|
|
|
.into_untagged_value();
|
2019-10-18 12:08:04 +00:00
|
|
|
|
|
|
|
assert_eq!(
|
2020-01-02 07:07:17 +00:00
|
|
|
row.get_data_by_key("amigos".spanned_unknown())
|
|
|
|
.ok_or_else(|| ShellError::unexpected("Failure during testing"))?,
|
2019-12-31 07:36:08 +00:00
|
|
|
table(&[string("andres"), string("jonathan"), string("yehuda")])
|
2019-10-18 12:08:04 +00:00
|
|
|
);
|
2020-01-02 07:07:17 +00:00
|
|
|
|
|
|
|
Ok(())
|
2019-10-18 12:08:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-01-02 07:07:17 +00:00
|
|
|
fn gets_matching_field_from_nested_rows_inside_a_row() -> Result<(), ShellError> {
|
2020-10-20 09:07:13 +00:00
|
|
|
let field_path = column_path("package.version").as_column_path()?;
|
2019-10-18 12:08:04 +00:00
|
|
|
|
2019-10-20 11:55:56 +00:00
|
|
|
let (version, tag) = string("0.4.0").into_parts();
|
|
|
|
|
2019-12-04 19:52:31 +00:00
|
|
|
let value = UntaggedValue::row(indexmap! {
|
2019-10-20 11:55:56 +00:00
|
|
|
"package".into() =>
|
|
|
|
row(indexmap! {
|
|
|
|
"name".into() => string("nu"),
|
|
|
|
"version".into() => string("0.4.0")
|
|
|
|
})
|
|
|
|
});
|
2019-10-18 12:08:04 +00:00
|
|
|
|
2019-10-20 11:55:56 +00:00
|
|
|
assert_eq!(
|
2020-01-02 07:07:17 +00:00
|
|
|
*value.into_value(tag).get_data_by_column_path(
|
2020-10-20 09:07:13 +00:00
|
|
|
&field_path.item,
|
2020-01-02 07:07:17 +00:00
|
|
|
Box::new(error_callback("package.version"))
|
|
|
|
)?,
|
2019-10-20 11:55:56 +00:00
|
|
|
version
|
2020-01-02 07:07:17 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
2019-10-18 12:08:04 +00:00
|
|
|
}
|
|
|
|
|
2019-11-04 15:47:03 +00:00
|
|
|
#[test]
|
2020-01-02 07:07:17 +00:00
|
|
|
fn gets_first_matching_field_from_rows_with_same_field_inside_a_table() -> Result<(), ShellError>
|
|
|
|
{
|
2020-10-20 09:07:13 +00:00
|
|
|
let field_path = column_path("package.authors.name").as_column_path()?;
|
2019-11-04 15:47:03 +00:00
|
|
|
|
|
|
|
let (_, tag) = string("Andrés N. Robalino").into_parts();
|
|
|
|
|
2019-12-04 19:52:31 +00:00
|
|
|
let value = UntaggedValue::row(indexmap! {
|
2019-11-04 15:47:03 +00:00
|
|
|
"package".into() => row(indexmap! {
|
|
|
|
"name".into() => string("nu"),
|
|
|
|
"version".into() => string("0.4.0"),
|
2019-12-31 07:36:08 +00:00
|
|
|
"authors".into() => table(&[
|
2019-11-04 15:47:03 +00:00
|
|
|
row(indexmap!{"name".into() => string("Andrés N. Robalino")}),
|
|
|
|
row(indexmap!{"name".into() => string("Jonathan Turner")}),
|
|
|
|
row(indexmap!{"name".into() => string("Yehuda Katz")})
|
|
|
|
])
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
assert_eq!(
|
2020-01-02 07:07:17 +00:00
|
|
|
value.into_value(tag).get_data_by_column_path(
|
2020-10-20 09:07:13 +00:00
|
|
|
&field_path.item,
|
2020-01-02 07:07:17 +00:00
|
|
|
Box::new(error_callback("package.authors.name"))
|
|
|
|
)?,
|
2019-12-31 07:36:08 +00:00
|
|
|
table(&[
|
2019-11-04 15:47:03 +00:00
|
|
|
string("Andrés N. Robalino"),
|
|
|
|
string("Jonathan Turner"),
|
|
|
|
string("Yehuda Katz")
|
|
|
|
])
|
2020-01-02 07:07:17 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
2019-11-04 15:47:03 +00:00
|
|
|
}
|
|
|
|
|
2019-10-30 10:55:26 +00:00
|
|
|
#[test]
|
2020-01-02 07:07:17 +00:00
|
|
|
fn column_path_that_contains_just_a_number_gets_a_row_from_a_table() -> Result<(), ShellError> {
|
2020-10-20 09:07:13 +00:00
|
|
|
let field_path = column_path("package.authors.0").as_column_path()?;
|
2019-10-30 10:55:26 +00:00
|
|
|
|
|
|
|
let (_, tag) = string("Andrés N. Robalino").into_parts();
|
|
|
|
|
2019-12-04 19:52:31 +00:00
|
|
|
let value = UntaggedValue::row(indexmap! {
|
2019-10-30 10:55:26 +00:00
|
|
|
"package".into() => row(indexmap! {
|
|
|
|
"name".into() => string("nu"),
|
|
|
|
"version".into() => string("0.4.0"),
|
2019-12-31 07:36:08 +00:00
|
|
|
"authors".into() => table(&[
|
2019-10-30 10:55:26 +00:00
|
|
|
row(indexmap!{"name".into() => string("Andrés N. Robalino")}),
|
|
|
|
row(indexmap!{"name".into() => string("Jonathan Turner")}),
|
|
|
|
row(indexmap!{"name".into() => string("Yehuda Katz")})
|
|
|
|
])
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
assert_eq!(
|
2020-01-02 07:07:17 +00:00
|
|
|
*value.into_value(tag).get_data_by_column_path(
|
2020-10-20 09:07:13 +00:00
|
|
|
&field_path.item,
|
2020-01-02 07:07:17 +00:00
|
|
|
Box::new(error_callback("package.authors.0"))
|
|
|
|
)?,
|
2019-12-04 19:52:31 +00:00
|
|
|
UntaggedValue::row(indexmap! {
|
2019-10-30 10:55:26 +00:00
|
|
|
"name".into() => string("Andrés N. Robalino")
|
|
|
|
})
|
|
|
|
);
|
2020-01-02 07:07:17 +00:00
|
|
|
|
|
|
|
Ok(())
|
2019-10-30 10:55:26 +00:00
|
|
|
}
|
|
|
|
|
2019-10-31 19:20:22 +00:00
|
|
|
#[test]
|
2020-01-02 07:07:17 +00:00
|
|
|
fn column_path_that_contains_just_a_number_gets_a_row_from_a_row() -> Result<(), ShellError> {
|
2020-10-20 09:07:13 +00:00
|
|
|
let field_path = column_path(r#"package.authors."0""#).as_column_path()?;
|
2019-10-31 19:20:22 +00:00
|
|
|
|
|
|
|
let (_, tag) = string("Andrés N. Robalino").into_parts();
|
|
|
|
|
2019-12-04 19:52:31 +00:00
|
|
|
let value = UntaggedValue::row(indexmap! {
|
2019-10-31 19:20:22 +00:00
|
|
|
"package".into() => row(indexmap! {
|
|
|
|
"name".into() => string("nu"),
|
|
|
|
"version".into() => string("0.4.0"),
|
|
|
|
"authors".into() => row(indexmap! {
|
|
|
|
"0".into() => row(indexmap!{"name".into() => string("Andrés N. Robalino")}),
|
|
|
|
"1".into() => row(indexmap!{"name".into() => string("Jonathan Turner")}),
|
|
|
|
"2".into() => row(indexmap!{"name".into() => string("Yehuda Katz")}),
|
|
|
|
})
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
assert_eq!(
|
2020-01-02 07:07:17 +00:00
|
|
|
*value.into_value(tag).get_data_by_column_path(
|
2020-10-20 09:07:13 +00:00
|
|
|
&field_path.item,
|
2020-01-02 07:07:17 +00:00
|
|
|
Box::new(error_callback("package.authors.\"0\""))
|
|
|
|
)?,
|
2019-12-04 19:52:31 +00:00
|
|
|
UntaggedValue::row(indexmap! {
|
2019-10-31 19:20:22 +00:00
|
|
|
"name".into() => string("Andrés N. Robalino")
|
|
|
|
})
|
|
|
|
);
|
2020-01-02 07:07:17 +00:00
|
|
|
|
|
|
|
Ok(())
|
2019-10-31 19:20:22 +00:00
|
|
|
}
|
|
|
|
|
2019-10-18 12:08:04 +00:00
|
|
|
#[test]
|
2020-01-02 07:07:17 +00:00
|
|
|
fn replaces_matching_field_from_a_row() -> Result<(), ShellError> {
|
2020-10-20 09:07:13 +00:00
|
|
|
let field_path = column_path("amigos").as_column_path()?;
|
2019-10-18 12:08:04 +00:00
|
|
|
|
2019-12-04 19:52:31 +00:00
|
|
|
let sample = UntaggedValue::row(indexmap! {
|
2019-12-31 07:36:08 +00:00
|
|
|
"amigos".into() => table(&[
|
2019-10-20 11:55:56 +00:00
|
|
|
string("andres"),
|
|
|
|
string("jonathan"),
|
|
|
|
string("yehuda"),
|
|
|
|
]),
|
|
|
|
});
|
2019-10-18 12:08:04 +00:00
|
|
|
|
2019-11-21 14:33:14 +00:00
|
|
|
let replacement = string("jonas");
|
2019-10-20 11:55:56 +00:00
|
|
|
|
|
|
|
let actual = sample
|
2019-11-21 14:33:14 +00:00
|
|
|
.into_untagged_value()
|
2020-10-20 09:07:13 +00:00
|
|
|
.replace_data_at_column_path(&field_path.item, replacement)
|
2020-01-02 17:51:20 +00:00
|
|
|
.ok_or_else(|| ShellError::untagged_runtime_error("Could not replace column"))?;
|
2019-10-20 11:55:56 +00:00
|
|
|
|
|
|
|
assert_eq!(actual, row(indexmap! {"amigos".into() => string("jonas")}));
|
2020-01-02 07:07:17 +00:00
|
|
|
|
|
|
|
Ok(())
|
2019-10-20 11:55:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-01-02 07:07:17 +00:00
|
|
|
fn replaces_matching_field_from_nested_rows_inside_a_row() -> Result<(), ShellError> {
|
2020-10-20 09:07:13 +00:00
|
|
|
let field_path = column_path(r#"package.authors."los.3.caballeros""#).as_column_path()?;
|
2019-10-20 11:55:56 +00:00
|
|
|
|
2019-12-04 19:52:31 +00:00
|
|
|
let sample = UntaggedValue::row(indexmap! {
|
2019-10-20 11:55:56 +00:00
|
|
|
"package".into() => row(indexmap! {
|
|
|
|
"authors".into() => row(indexmap! {
|
2019-12-31 07:36:08 +00:00
|
|
|
"los.3.mosqueteros".into() => table(&[string("andres::yehuda::jonathan")]),
|
|
|
|
"los.3.amigos".into() => table(&[string("andres::yehuda::jonathan")]),
|
|
|
|
"los.3.caballeros".into() => table(&[string("andres::yehuda::jonathan")])
|
2019-10-20 11:55:56 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
2019-12-31 07:36:08 +00:00
|
|
|
let replacement = table(&[string("yehuda::jonathan::andres")]);
|
2019-11-21 14:33:14 +00:00
|
|
|
let tag = replacement.tag.clone();
|
2019-10-20 11:55:56 +00:00
|
|
|
|
|
|
|
let actual = sample
|
2020-01-02 17:51:20 +00:00
|
|
|
.into_value(&tag)
|
2020-10-20 09:07:13 +00:00
|
|
|
.replace_data_at_column_path(&field_path.item, replacement.clone())
|
2020-01-02 17:51:20 +00:00
|
|
|
.ok_or_else(|| {
|
|
|
|
ShellError::labeled_error(
|
|
|
|
"Could not replace column",
|
|
|
|
"could not replace column",
|
|
|
|
&tag,
|
|
|
|
)
|
|
|
|
})?;
|
2019-10-20 11:55:56 +00:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
actual,
|
2019-12-04 19:52:31 +00:00
|
|
|
UntaggedValue::row(indexmap! {
|
2019-10-20 11:55:56 +00:00
|
|
|
"package".into() => row(indexmap! {
|
|
|
|
"authors".into() => row(indexmap! {
|
2019-12-31 07:36:08 +00:00
|
|
|
"los.3.mosqueteros".into() => table(&[string("andres::yehuda::jonathan")]),
|
|
|
|
"los.3.amigos".into() => table(&[string("andres::yehuda::jonathan")]),
|
|
|
|
"los.3.caballeros".into() => replacement})})})
|
2019-11-21 14:33:14 +00:00
|
|
|
.into_value(tag)
|
2019-10-20 11:55:56 +00:00
|
|
|
);
|
2020-01-02 07:07:17 +00:00
|
|
|
|
|
|
|
Ok(())
|
2019-10-20 11:55:56 +00:00
|
|
|
}
|
|
|
|
#[test]
|
2020-01-02 07:07:17 +00:00
|
|
|
fn replaces_matching_field_from_rows_inside_a_table() -> Result<(), ShellError> {
|
2020-10-20 09:07:13 +00:00
|
|
|
let field_path =
|
|
|
|
column_path(r#"shell_policy.releases."nu.version.arepa""#).as_column_path()?;
|
2019-10-20 11:55:56 +00:00
|
|
|
|
2019-12-04 19:52:31 +00:00
|
|
|
let sample = UntaggedValue::row(indexmap! {
|
2019-10-20 11:55:56 +00:00
|
|
|
"shell_policy".into() => row(indexmap! {
|
2019-12-31 07:36:08 +00:00
|
|
|
"releases".into() => table(&[
|
2019-10-20 11:55:56 +00:00
|
|
|
row(indexmap! {
|
|
|
|
"nu.version.arepa".into() => row(indexmap! {
|
|
|
|
"code".into() => string("0.4.0"), "tag_line".into() => string("GitHub-era")
|
|
|
|
})
|
|
|
|
}),
|
|
|
|
row(indexmap! {
|
|
|
|
"nu.version.taco".into() => row(indexmap! {
|
|
|
|
"code".into() => string("0.3.0"), "tag_line".into() => string("GitHub-era")
|
|
|
|
})
|
|
|
|
}),
|
|
|
|
row(indexmap! {
|
|
|
|
"nu.version.stable".into() => row(indexmap! {
|
|
|
|
"code".into() => string("0.2.0"), "tag_line".into() => string("GitHub-era")
|
|
|
|
})
|
|
|
|
})
|
|
|
|
])
|
2019-10-18 12:08:04 +00:00
|
|
|
})
|
|
|
|
});
|
|
|
|
|
2019-11-21 14:33:14 +00:00
|
|
|
let replacement = row(indexmap! {
|
2019-10-20 11:55:56 +00:00
|
|
|
"code".into() => string("0.5.0"),
|
|
|
|
"tag_line".into() => string("CABALLEROS")
|
2019-11-21 14:33:14 +00:00
|
|
|
});
|
|
|
|
let tag = replacement.tag.clone();
|
2019-10-20 11:55:56 +00:00
|
|
|
|
|
|
|
let actual = sample
|
2019-11-21 14:33:14 +00:00
|
|
|
.into_value(tag.clone())
|
2020-10-20 09:07:13 +00:00
|
|
|
.replace_data_at_column_path(&field_path.item, replacement.clone())
|
2020-01-02 17:51:20 +00:00
|
|
|
.ok_or_else(|| {
|
|
|
|
ShellError::labeled_error(
|
|
|
|
"Could not replace column",
|
|
|
|
"could not replace column",
|
|
|
|
&tag,
|
|
|
|
)
|
|
|
|
})?;
|
2019-10-20 11:55:56 +00:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
actual,
|
2019-12-04 19:52:31 +00:00
|
|
|
UntaggedValue::row(indexmap! {
|
2019-10-20 11:55:56 +00:00
|
|
|
"shell_policy".into() => row(indexmap! {
|
2019-12-31 07:36:08 +00:00
|
|
|
"releases".into() => table(&[
|
2019-10-20 11:55:56 +00:00
|
|
|
row(indexmap! {
|
2019-11-21 14:33:14 +00:00
|
|
|
"nu.version.arepa".into() => replacement
|
2019-10-20 11:55:56 +00:00
|
|
|
}),
|
|
|
|
row(indexmap! {
|
|
|
|
"nu.version.taco".into() => row(indexmap! {
|
|
|
|
"code".into() => string("0.3.0"), "tag_line".into() => string("GitHub-era")
|
|
|
|
})
|
|
|
|
}),
|
|
|
|
row(indexmap! {
|
|
|
|
"nu.version.stable".into() => row(indexmap! {
|
|
|
|
"code".into() => string("0.2.0"), "tag_line".into() => string("GitHub-era")
|
|
|
|
})
|
|
|
|
})
|
|
|
|
])
|
|
|
|
})
|
2019-11-21 14:33:14 +00:00
|
|
|
}).into_value(&tag)
|
2019-10-20 11:55:56 +00:00
|
|
|
);
|
2020-01-02 07:07:17 +00:00
|
|
|
|
|
|
|
Ok(())
|
2019-10-18 12:08:04 +00:00
|
|
|
}
|
|
|
|
}
|