forked from extern/nushell
Move nu-data out of nu-cli (#2369)
* WIP for moving nu-data out * Refactor nu-data out of nu-cli * Remove unwraps * Remove unwraps
This commit is contained in:
@ -1,35 +0,0 @@
|
||||
use indexmap::IndexMap;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
use nu_value_ext::as_string;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn group(
|
||||
values: &Value,
|
||||
grouper: &Option<Box<dyn Fn(usize, &Value) -> Result<String, ShellError> + Send>>,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
|
||||
let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
|
||||
|
||||
for (idx, value) in values.table_entries().enumerate() {
|
||||
let group_key = if let Some(ref grouper) = grouper {
|
||||
grouper(idx, &value)
|
||||
} else {
|
||||
as_string(&value)
|
||||
};
|
||||
|
||||
let group = groups.entry(group_key?).or_insert(vec![]);
|
||||
group.push((*value).clone());
|
||||
}
|
||||
|
||||
let mut out = TaggedDictBuilder::new(&tag);
|
||||
|
||||
for (k, v) in groups.iter() {
|
||||
out.insert_untagged(k, UntaggedValue::table(v));
|
||||
}
|
||||
|
||||
Ok(out.into_value())
|
||||
}
|
@ -1,274 +0,0 @@
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
use crate::data::value::compute_values;
|
||||
use derive_new::new;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::Operator;
|
||||
use nu_protocol::{UntaggedValue, Value};
|
||||
use nu_source::{SpannedItem, Tag, TaggedItem};
|
||||
use nu_value_ext::ValueExt;
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new)]
|
||||
pub struct Labels {
|
||||
pub x: Vec<String>,
|
||||
pub y: Vec<String>,
|
||||
}
|
||||
|
||||
impl Labels {
|
||||
pub fn at(&self, idx: usize) -> Option<&str> {
|
||||
if let Some(k) = self.x.get(idx) {
|
||||
Some(&k[..])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grouped(&self) -> impl Iterator<Item = &String> {
|
||||
self.x.iter()
|
||||
}
|
||||
|
||||
pub fn grouping_total(&self) -> Value {
|
||||
UntaggedValue::int(self.x.len()).into_untagged_value()
|
||||
}
|
||||
|
||||
pub fn splits(&self) -> impl Iterator<Item = &String> {
|
||||
self.y.iter()
|
||||
}
|
||||
|
||||
pub fn splits_total(&self) -> Value {
|
||||
UntaggedValue::int(self.y.len()).into_untagged_value()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new)]
|
||||
pub struct Range {
|
||||
pub start: Value,
|
||||
pub end: Value,
|
||||
}
|
||||
|
||||
fn formula(
|
||||
acc_begin: Value,
|
||||
calculator: Box<dyn Fn(Vec<&Value>) -> Result<Value, ShellError> + Send + Sync + 'static>,
|
||||
) -> Box<dyn Fn(&Value, Vec<&Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
|
||||
Box::new(move |acc, datax| -> Result<Value, ShellError> {
|
||||
let result = match compute_values(Operator::Multiply, &acc, &acc_begin) {
|
||||
Ok(v) => v.into_untagged_value(),
|
||||
Err((left_type, right_type)) => {
|
||||
return Err(ShellError::coerce_error(
|
||||
left_type.spanned_unknown(),
|
||||
right_type.spanned_unknown(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
match calculator(datax) {
|
||||
Ok(total) => Ok(match compute_values(Operator::Plus, &result, &total) {
|
||||
Ok(v) => v.into_untagged_value(),
|
||||
Err((left_type, right_type)) => {
|
||||
return Err(ShellError::coerce_error(
|
||||
left_type.spanned_unknown(),
|
||||
right_type.spanned_unknown(),
|
||||
))
|
||||
}
|
||||
}),
|
||||
Err(reason) => Err(reason),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reducer_for(
|
||||
command: Reduction,
|
||||
) -> Box<dyn Fn(&Value, Vec<&Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
|
||||
match command {
|
||||
Reduction::Accumulate => Box::new(formula(
|
||||
UntaggedValue::int(1).into_untagged_value(),
|
||||
Box::new(sum),
|
||||
)),
|
||||
_ => Box::new(formula(
|
||||
UntaggedValue::int(0).into_untagged_value(),
|
||||
Box::new(sum),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max(values: &Value, tag: impl Into<Tag>) -> Result<&Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
|
||||
values
|
||||
.table_entries()
|
||||
.filter_map(|dataset| dataset.table_entries().max())
|
||||
.max()
|
||||
.ok_or_else(|| ShellError::labeled_error("err", "err", &tag))
|
||||
}
|
||||
|
||||
pub fn sum(data: Vec<&Value>) -> Result<Value, ShellError> {
|
||||
let mut acc = UntaggedValue::int(0);
|
||||
|
||||
for value in data {
|
||||
match value.value {
|
||||
UntaggedValue::Primitive(_) => {
|
||||
acc = match compute_values(Operator::Plus, &acc, &value) {
|
||||
Ok(v) => v,
|
||||
Err((left_type, right_type)) => {
|
||||
return Err(ShellError::coerce_error(
|
||||
left_type.spanned_unknown(),
|
||||
right_type.spanned_unknown(),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Attempted to compute the sum of a value that cannot be summed.",
|
||||
"value appears here",
|
||||
value.tag.span,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(acc.into_untagged_value())
|
||||
}
|
||||
|
||||
pub fn sort_columns(
|
||||
values: &[String],
|
||||
format: &Option<Box<dyn Fn(&Value, String) -> Result<String, ShellError>>>,
|
||||
) -> Result<Vec<String>, ShellError> {
|
||||
let mut keys = vec![];
|
||||
|
||||
if let Some(fmt) = format {
|
||||
for k in values.iter() {
|
||||
let k = k.clone().tagged_unknown();
|
||||
let v =
|
||||
crate::data::value::Date::naive_from_str(k.borrow_tagged())?.into_untagged_value();
|
||||
keys.push(fmt(&v, k.to_string())?);
|
||||
}
|
||||
} else {
|
||||
keys = values.to_vec();
|
||||
}
|
||||
|
||||
keys.sort();
|
||||
Ok(keys)
|
||||
}
|
||||
|
||||
pub fn sort(planes: &Labels, values: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
|
||||
let mut x = vec![];
|
||||
for column in planes.splits() {
|
||||
let key = column.clone().tagged_unknown();
|
||||
let groups = values
|
||||
.get_data_by_key(key.borrow_spanned())
|
||||
.ok_or_else(|| {
|
||||
ShellError::labeled_error("unknown column", "unknown column", key.span())
|
||||
})?;
|
||||
|
||||
let mut y = vec![];
|
||||
for inner_column in planes.grouped() {
|
||||
let key = inner_column.clone().tagged_unknown();
|
||||
let grouped = groups.get_data_by_key(key.borrow_spanned());
|
||||
|
||||
if let Some(grouped) = grouped {
|
||||
y.push(grouped.table_entries().cloned().collect::<Vec<_>>());
|
||||
} else {
|
||||
let empty = UntaggedValue::table(&[]).into_value(&tag);
|
||||
y.push(empty.table_entries().cloned().collect::<Vec<_>>());
|
||||
}
|
||||
}
|
||||
|
||||
x.push(
|
||||
UntaggedValue::table(&y.iter().cloned().flatten().collect::<Vec<Value>>())
|
||||
.into_value(&tag),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(UntaggedValue::table(&x).into_value(&tag))
|
||||
}
|
||||
|
||||
pub fn evaluate(
|
||||
values: &Value,
|
||||
evaluator: &Option<Box<dyn Fn(usize, &Value) -> Result<Value, ShellError> + Send>>,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
|
||||
let mut x = vec![];
|
||||
for split in values.table_entries() {
|
||||
let mut y = vec![];
|
||||
|
||||
for (idx, subset) in split.table_entries().enumerate() {
|
||||
let mut set = vec![];
|
||||
|
||||
if let Some(ref evaluator) = evaluator {
|
||||
let value = evaluator(idx, subset)?;
|
||||
|
||||
set.push(value);
|
||||
} else {
|
||||
set.push(UntaggedValue::int(1).into_value(&tag));
|
||||
}
|
||||
|
||||
y.push(UntaggedValue::table(&set).into_value(&tag));
|
||||
}
|
||||
|
||||
x.push(UntaggedValue::table(&y).into_value(&tag));
|
||||
}
|
||||
|
||||
Ok(UntaggedValue::table(&x).into_value(&tag))
|
||||
}
|
||||
|
||||
pub enum Reduction {
|
||||
#[allow(dead_code)]
|
||||
Count,
|
||||
Accumulate,
|
||||
}
|
||||
|
||||
pub fn reduce(values: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
let reduce_with = reducer_for(Reduction::Accumulate);
|
||||
|
||||
let mut datasets = vec![];
|
||||
for dataset in values.table_entries() {
|
||||
let mut acc = UntaggedValue::int(0).into_value(&tag);
|
||||
|
||||
let mut subsets = vec![];
|
||||
for subset in dataset.table_entries() {
|
||||
acc = reduce_with(&acc, subset.table_entries().collect::<Vec<_>>())?;
|
||||
subsets.push(acc.clone());
|
||||
}
|
||||
datasets.push(UntaggedValue::table(&subsets).into_value(&tag));
|
||||
}
|
||||
|
||||
Ok(UntaggedValue::table(&datasets).into_value(&tag))
|
||||
}
|
||||
|
||||
pub fn percentages(
|
||||
maxima: &Value,
|
||||
values: &Value,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
|
||||
let mut x = vec![];
|
||||
for split in values.table_entries() {
|
||||
x.push(
|
||||
UntaggedValue::table(
|
||||
&split
|
||||
.table_entries()
|
||||
.filter_map(|s| {
|
||||
let hundred = UntaggedValue::decimal(100);
|
||||
|
||||
match compute_values(Operator::Divide, &hundred, &maxima) {
|
||||
Ok(v) => match compute_values(Operator::Multiply, &s, &v) {
|
||||
Ok(v) => Some(v.into_untagged_value()),
|
||||
Err(_) => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.into_value(&tag),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(UntaggedValue::table(&x).into_value(&tag))
|
||||
}
|
@ -1,296 +0,0 @@
|
||||
pub mod group;
|
||||
pub mod split;
|
||||
|
||||
mod internal;
|
||||
|
||||
pub use crate::utils::data::group::group;
|
||||
pub use crate::utils::data::split::split;
|
||||
|
||||
use crate::utils::data::internal::*;
|
||||
|
||||
use derive_new::new;
|
||||
use getset::Getters;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Getters, Clone, new)]
|
||||
pub struct Model {
|
||||
pub labels: Labels,
|
||||
pub ranges: (Range, Range),
|
||||
|
||||
pub data: Value,
|
||||
pub percentages: Value,
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub struct Operation<'a> {
|
||||
pub grouper: Option<Box<dyn Fn(usize, &Value) -> Result<String, ShellError> + Send>>,
|
||||
pub splitter: Option<Box<dyn Fn(usize, &Value) -> Result<String, ShellError> + Send>>,
|
||||
pub format: Option<Box<dyn Fn(&Value, String) -> Result<String, ShellError>>>,
|
||||
pub eval: &'a Option<Box<dyn Fn(usize, &Value) -> Result<Value, ShellError> + Send>>,
|
||||
}
|
||||
|
||||
pub fn report(
|
||||
values: &Value,
|
||||
options: Operation,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Model, ShellError> {
|
||||
let tag = tag.into();
|
||||
|
||||
let grouped = group(&values, &options.grouper, &tag)?;
|
||||
let splitted = split(&grouped, &options.splitter, &tag)?;
|
||||
|
||||
let x = grouped
|
||||
.row_entries()
|
||||
.map(|(key, _)| key.clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let x = if options.format.is_some() {
|
||||
sort_columns(&x, &options.format)
|
||||
} else {
|
||||
sort_columns(&x, &None)
|
||||
}?;
|
||||
|
||||
let mut y = splitted
|
||||
.row_entries()
|
||||
.map(|(key, _)| key.clone())
|
||||
.collect::<Vec<_>>();
|
||||
y.sort();
|
||||
|
||||
let planes = Labels { x, y };
|
||||
let sorted = sort(&planes, &splitted, &tag)?;
|
||||
|
||||
let evaluated = evaluate(
|
||||
&sorted,
|
||||
if options.eval.is_some() {
|
||||
options.eval
|
||||
} else {
|
||||
&None
|
||||
},
|
||||
&tag,
|
||||
)?;
|
||||
|
||||
let group_labels = planes.grouping_total();
|
||||
|
||||
let reduced = reduce(&evaluated, &tag)?;
|
||||
|
||||
let max = max(&reduced, &tag)?.clone();
|
||||
let maxima = max.clone();
|
||||
|
||||
let percents = percentages(&maxima, &reduced, &tag)?;
|
||||
|
||||
Ok(Model {
|
||||
labels: planes,
|
||||
ranges: (
|
||||
Range {
|
||||
start: UntaggedValue::int(0).into_untagged_value(),
|
||||
end: group_labels,
|
||||
},
|
||||
Range {
|
||||
start: UntaggedValue::int(0).into_untagged_value(),
|
||||
end: max,
|
||||
},
|
||||
),
|
||||
data: reduced,
|
||||
percentages: percents,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod helpers {
|
||||
use super::{report, Labels, Model, Operation, Range};
|
||||
use bigdecimal::BigDecimal;
|
||||
use indexmap::indexmap;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{UntaggedValue, Value};
|
||||
use nu_source::{Tag, TaggedItem};
|
||||
use nu_value_ext::ValueExt;
|
||||
use num_bigint::BigInt;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
pub fn int(s: impl Into<BigInt>) -> Value {
|
||||
UntaggedValue::int(s).into_untagged_value()
|
||||
}
|
||||
|
||||
pub fn decimal(f: impl Into<BigDecimal>) -> Value {
|
||||
UntaggedValue::decimal(f.into()).into_untagged_value()
|
||||
}
|
||||
|
||||
pub fn string(input: impl Into<String>) -> Value {
|
||||
UntaggedValue::string(input.into()).into_untagged_value()
|
||||
}
|
||||
|
||||
pub fn row(entries: IndexMap<String, Value>) -> Value {
|
||||
UntaggedValue::row(entries).into_untagged_value()
|
||||
}
|
||||
|
||||
pub fn table(list: &[Value]) -> Value {
|
||||
UntaggedValue::table(list).into_untagged_value()
|
||||
}
|
||||
|
||||
pub fn date(input: impl Into<String>) -> Value {
|
||||
let key = input.into().tagged_unknown();
|
||||
crate::data::value::Date::naive_from_str(key.borrow_tagged())
|
||||
.unwrap()
|
||||
.into_untagged_value()
|
||||
}
|
||||
|
||||
pub fn committers() -> Vec<Value> {
|
||||
vec![
|
||||
row(indexmap! {
|
||||
"date".into() => date("2019-07-23"),
|
||||
"name".into() => string("AR"),
|
||||
"country".into() => string("EC"),
|
||||
"chickens".into() => int(10),
|
||||
}),
|
||||
row(indexmap! {
|
||||
"date".into() => date("2019-07-23"),
|
||||
"name".into() => string("JT"),
|
||||
"country".into() => string("NZ"),
|
||||
"chickens".into() => int(5),
|
||||
}),
|
||||
row(indexmap! {
|
||||
"date".into() => date("2019-10-10"),
|
||||
"name".into() => string("YK"),
|
||||
"country".into() => string("US"),
|
||||
"chickens".into() => int(6),
|
||||
}),
|
||||
row(indexmap! {
|
||||
"date".into() => date("2019-09-24"),
|
||||
"name".into() => string("AR"),
|
||||
"country".into() => string("EC"),
|
||||
"chickens".into() => int(20),
|
||||
}),
|
||||
row(indexmap! {
|
||||
"date".into() => date("2019-10-10"),
|
||||
"name".into() => string("JT"),
|
||||
"country".into() => string("NZ"),
|
||||
"chickens".into() => int(15),
|
||||
}),
|
||||
row(indexmap! {
|
||||
"date".into() => date("2019-09-24"),
|
||||
"name".into() => string("YK"),
|
||||
"country".into() => string("US"),
|
||||
"chickens".into() => int(4),
|
||||
}),
|
||||
row(indexmap! {
|
||||
"date".into() => date("2019-10-10"),
|
||||
"name".into() => string("AR"),
|
||||
"country".into() => string("EC"),
|
||||
"chickens".into() => int(30),
|
||||
}),
|
||||
row(indexmap! {
|
||||
"date".into() => date("2019-09-24"),
|
||||
"name".into() => string("JT"),
|
||||
"country".into() => string("NZ"),
|
||||
"chickens".into() => int(10),
|
||||
}),
|
||||
row(indexmap! {
|
||||
"date".into() => date("2019-07-23"),
|
||||
"name".into() => string("YK"),
|
||||
"country".into() => string("US"),
|
||||
"chickens".into() => int(2),
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn committers_grouped_by_date() -> Value {
|
||||
let sample = table(&committers());
|
||||
|
||||
let grouper = Box::new(move |_, row: &Value| {
|
||||
let key = String::from("date").tagged_unknown();
|
||||
let group_key = row.get_data_by_key(key.borrow_spanned()).unwrap();
|
||||
|
||||
group_key.format("%Y-%m-%d")
|
||||
});
|
||||
|
||||
crate::utils::data::group(&sample, &Some(grouper), Tag::unknown()).unwrap()
|
||||
}
|
||||
|
||||
pub fn date_formatter(
|
||||
fmt: &'static str,
|
||||
) -> Box<dyn Fn(&Value, String) -> Result<String, ShellError>> {
|
||||
Box::new(move |date: &Value, _: String| date.format(&fmt))
|
||||
}
|
||||
|
||||
fn assert_without_checking_percentages(report_a: Model, report_b: Model) {
|
||||
assert_eq!(report_a.labels.x, report_b.labels.x);
|
||||
assert_eq!(report_a.labels.y, report_b.labels.y);
|
||||
assert_eq!(report_a.ranges, report_b.ranges);
|
||||
assert_eq!(report_a.data, report_b.data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepares_report_using_accumulating_value() {
|
||||
let committers = table(&committers());
|
||||
|
||||
let by_date = Box::new(move |_, row: &Value| {
|
||||
let key = String::from("date").tagged_unknown();
|
||||
let key = row.get_data_by_key(key.borrow_spanned()).unwrap();
|
||||
|
||||
let callback = date_formatter("%Y-%m-%d");
|
||||
callback(&key, "nothing".to_string())
|
||||
});
|
||||
|
||||
let by_country = Box::new(move |_, row: &Value| {
|
||||
let key = String::from("country").tagged_unknown();
|
||||
let key = row.get_data_by_key(key.borrow_spanned()).unwrap();
|
||||
nu_value_ext::as_string(&key)
|
||||
});
|
||||
|
||||
let options = Operation {
|
||||
grouper: Some(by_date),
|
||||
splitter: Some(by_country),
|
||||
format: Some(date_formatter("%Y-%m-%d")),
|
||||
eval: /* value to be used for accumulation */ &Some(Box::new(move |_, value: &Value| {
|
||||
let chickens_key = String::from("chickens").tagged_unknown();
|
||||
|
||||
value
|
||||
.get_data_by_key(chickens_key.borrow_spanned())
|
||||
.ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
"unknown column",
|
||||
"unknown column",
|
||||
chickens_key.span(),
|
||||
)
|
||||
})
|
||||
})),
|
||||
};
|
||||
|
||||
assert_without_checking_percentages(
|
||||
report(&committers, options, Tag::unknown()).unwrap(),
|
||||
Model {
|
||||
labels: Labels {
|
||||
x: vec![
|
||||
String::from("2019-07-23"),
|
||||
String::from("2019-09-24"),
|
||||
String::from("2019-10-10"),
|
||||
],
|
||||
y: vec![String::from("EC"), String::from("NZ"), String::from("US")],
|
||||
},
|
||||
ranges: (
|
||||
Range {
|
||||
start: int(0),
|
||||
end: int(3),
|
||||
},
|
||||
Range {
|
||||
start: int(0),
|
||||
end: int(60),
|
||||
},
|
||||
),
|
||||
data: table(&[
|
||||
table(&[int(10), int(30), int(60)]),
|
||||
table(&[int(5), int(15), int(30)]),
|
||||
table(&[int(2), int(6), int(12)]),
|
||||
]),
|
||||
percentages: table(&[
|
||||
table(&[decimal(16.66), decimal(50), decimal(100)]),
|
||||
table(&[decimal(8.33), decimal(25), decimal(50)]),
|
||||
table(&[decimal(3.33), decimal(10), decimal(20)]),
|
||||
]),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{SpannedTypeName, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
|
||||
use crate::utils::data::group;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn split(
|
||||
value: &Value,
|
||||
splitter: &Option<Box<dyn Fn(usize, &Value) -> Result<String, ShellError> + Send>>,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
|
||||
let mut splits = indexmap::IndexMap::new();
|
||||
let mut out = TaggedDictBuilder::new(&tag);
|
||||
|
||||
if splitter.is_none() {
|
||||
out.insert_untagged("table", UntaggedValue::table(&[value.clone()]));
|
||||
return Ok(out.into_value());
|
||||
}
|
||||
|
||||
for (column, value) in value.row_entries() {
|
||||
if !&value.is_table() {
|
||||
return Err(ShellError::type_error(
|
||||
"a table value",
|
||||
value.spanned_type_name(),
|
||||
));
|
||||
}
|
||||
|
||||
match group(&value, splitter, &tag) {
|
||||
Ok(grouped) => {
|
||||
for (split_label, subset) in grouped.row_entries() {
|
||||
let s = splits
|
||||
.entry(split_label.clone())
|
||||
.or_insert(indexmap::IndexMap::new());
|
||||
|
||||
if !&subset.is_table() {
|
||||
return Err(ShellError::type_error(
|
||||
"a table value",
|
||||
subset.spanned_type_name(),
|
||||
));
|
||||
}
|
||||
|
||||
s.insert(column.clone(), subset.clone());
|
||||
}
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
let mut out = TaggedDictBuilder::new(&tag);
|
||||
|
||||
for (k, v) in splits.into_iter() {
|
||||
out.insert_untagged(k, UntaggedValue::row(v));
|
||||
}
|
||||
|
||||
Ok(out.into_value())
|
||||
}
|
Reference in New Issue
Block a user