mirror of
https://github.com/nushell/nushell.git
synced 2024-11-25 18:03:51 +01:00
Another batch of removing async_stream (#1978)
This commit is contained in:
parent
bcfb084d4c
commit
40673e4599
60
TODO.md
60
TODO.md
@ -1,60 +0,0 @@
|
|||||||
This pattern is extremely repetitive and can be abstracted:
|
|
||||||
|
|
||||||
```rs
|
|
||||||
let args = args.evaluate_once(registry)?;
|
|
||||||
let tag = args.name_tag();
|
|
||||||
let input = args.input;
|
|
||||||
|
|
||||||
let stream = async_stream! {
|
|
||||||
let values: Vec<Value> = input.values.collect().await;
|
|
||||||
|
|
||||||
let mut concat_string = String::new();
|
|
||||||
let mut latest_tag: Option<Tag> = None;
|
|
||||||
|
|
||||||
for value in values {
|
|
||||||
latest_tag = Some(value_tag.clone());
|
|
||||||
let value_span = value.tag.span;
|
|
||||||
|
|
||||||
match &value.value {
|
|
||||||
UntaggedValue::Primitive(Primitive::String(s)) => {
|
|
||||||
concat_string.push_str(&s);
|
|
||||||
concat_string.push_str("\n");
|
|
||||||
}
|
|
||||||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
|
||||||
"Expected a string from pipeline",
|
|
||||||
"requires string input",
|
|
||||||
name_span,
|
|
||||||
"value originates from here",
|
|
||||||
value_span,
|
|
||||||
)),
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Mandatory and Optional in parse_command
|
|
||||||
|
|
||||||
trace_remaining?
|
|
||||||
|
|
||||||
select_fields and select_fields take unnecessary Tag
|
|
||||||
|
|
||||||
Value#value should be Value#untagged
|
|
||||||
|
|
||||||
Unify dictionary building, probably around a macro
|
|
||||||
|
|
||||||
sys plugin in own crate
|
|
||||||
|
|
||||||
textview in own crate
|
|
||||||
|
|
||||||
Combine atomic and atomic_parse in parser
|
|
||||||
|
|
||||||
at_end_possible_ws needs to be comment and separator sensitive
|
|
||||||
|
|
||||||
Eliminate unnecessary `nodes` parser
|
|
||||||
|
|
||||||
#[derive(HasSpan)]
|
|
||||||
|
|
||||||
Figure out a solution for the duplication in stuff like NumberShape vs. NumberExpressionShape
|
|
||||||
|
|
||||||
use `struct Expander` from signature.rs
|
|
@ -9,7 +9,6 @@ use parking_lot::Mutex;
|
|||||||
use prettytable::format::{FormatBuilder, LinePosition, LineSeparator};
|
use prettytable::format::{FormatBuilder, LinePosition, LineSeparator};
|
||||||
use prettytable::{color, Attr, Cell, Row, Table};
|
use prettytable::{color, Attr, Cell, Row, Table};
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
use textwrap::fill;
|
use textwrap::fill;
|
||||||
|
|
||||||
pub struct Autoview;
|
pub struct Autoview;
|
||||||
@ -115,23 +114,12 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
|||||||
match input_stream.next().await {
|
match input_stream.next().await {
|
||||||
Some(y) => {
|
Some(y) => {
|
||||||
let ctrl_c = context.ctrl_c.clone();
|
let ctrl_c = context.ctrl_c.clone();
|
||||||
let stream = async_stream! {
|
let xy = vec![x, y];
|
||||||
yield Ok(x);
|
let xy_stream = futures::stream::iter(xy)
|
||||||
yield Ok(y);
|
.chain(input_stream)
|
||||||
|
.interruptible(ctrl_c);
|
||||||
|
|
||||||
loop {
|
let stream = InputStream::from_stream(xy_stream);
|
||||||
match input_stream.next().await {
|
|
||||||
Some(z) => {
|
|
||||||
if ctrl_c.load(Ordering::SeqCst) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
yield Ok(z);
|
|
||||||
}
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let stream = stream.to_input_stream();
|
|
||||||
|
|
||||||
if let Some(table) = table {
|
if let Some(table) = table {
|
||||||
let command_args = create_default_command_args(&context).with_input(stream);
|
let command_args = create_default_command_args(&context).with_input(stream);
|
||||||
|
@ -389,20 +389,26 @@ impl WholeStreamCommand for FnFilterCommand {
|
|||||||
ctrl_c,
|
ctrl_c,
|
||||||
shell_manager,
|
shell_manager,
|
||||||
call_info,
|
call_info,
|
||||||
mut input,
|
input,
|
||||||
..
|
..
|
||||||
}: CommandArgs,
|
}: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let host: Arc<parking_lot::Mutex<dyn Host>> = host.clone();
|
let registry = Arc::new(registry.clone());
|
||||||
let registry: CommandRegistry = registry.clone();
|
|
||||||
let func = self.func;
|
let func = self.func;
|
||||||
|
|
||||||
let stream = async_stream! {
|
Ok(input
|
||||||
while let Some(it) = input.next().await {
|
.then(move |it| {
|
||||||
|
let host = host.clone();
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let call_info = match call_info.clone().evaluate_with_new_it(®istry, &it).await {
|
let ctrl_c = ctrl_c.clone();
|
||||||
Err(err) => { yield Err(err); return; },
|
let shell_manager = shell_manager.clone();
|
||||||
|
let call_info = call_info.clone();
|
||||||
|
async move {
|
||||||
|
let call_info = match call_info.evaluate_with_new_it(&*registry, &it).await {
|
||||||
|
Err(err) => {
|
||||||
|
return OutputStream::one(Err(err));
|
||||||
|
}
|
||||||
Ok(args) => args,
|
Ok(args) => args,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -414,17 +420,13 @@ impl WholeStreamCommand for FnFilterCommand {
|
|||||||
);
|
);
|
||||||
|
|
||||||
match func(args) {
|
match func(args) {
|
||||||
Err(err) => yield Err(err),
|
Err(err) => return OutputStream::one(Err(err)),
|
||||||
Ok(mut stream) => {
|
Ok(stream) => stream,
|
||||||
while let Some(value) = stream.values.next().await {
|
|
||||||
yield value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
.flatten()
|
||||||
};
|
.to_output_stream())
|
||||||
|
|
||||||
Ok(stream.to_output_stream())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ impl WholeStreamCommand for Echo {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
echo(args, registry)
|
echo(args, registry).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -51,67 +51,62 @@ impl WholeStreamCommand for Echo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn echo(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
async fn echo(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let stream = async_stream! {
|
|
||||||
let (args, _): (EchoArgs, _) = args.process(®istry).await?;
|
let (args, _): (EchoArgs, _) = args.process(®istry).await?;
|
||||||
|
|
||||||
for i in args.rest {
|
let stream = args.rest.into_iter().map(|i| {
|
||||||
match i.as_string() {
|
match i.as_string() {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
yield Ok(ReturnSuccess::Value(
|
OutputStream::one(Ok(ReturnSuccess::Value(
|
||||||
UntaggedValue::string(s).into_value(i.tag.clone()),
|
UntaggedValue::string(s).into_value(i.tag.clone()),
|
||||||
));
|
)))
|
||||||
}
|
}
|
||||||
_ => match i {
|
_ => match i {
|
||||||
Value {
|
Value {
|
||||||
value: UntaggedValue::Table(table),
|
value: UntaggedValue::Table(table),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
for value in table {
|
futures::stream::iter(table.into_iter().map(ReturnSuccess::value)).to_output_stream()
|
||||||
yield Ok(ReturnSuccess::Value(value.clone()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Value {
|
Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::Range(range)),
|
value: UntaggedValue::Primitive(Primitive::Range(range)),
|
||||||
tag
|
tag
|
||||||
} => {
|
} => {
|
||||||
|
let mut output_vec = vec![];
|
||||||
|
|
||||||
let mut current = range.from.0.item;
|
let mut current = range.from.0.item;
|
||||||
while current != range.to.0.item {
|
while current != range.to.0.item {
|
||||||
yield Ok(ReturnSuccess::Value(UntaggedValue::Primitive(current.clone()).into_value(&tag)));
|
output_vec.push(Ok(ReturnSuccess::Value(UntaggedValue::Primitive(current.clone()).into_value(&tag))));
|
||||||
current = match crate::data::value::compute_values(Operator::Plus, &UntaggedValue::Primitive(current), &UntaggedValue::int(1)) {
|
current = match crate::data::value::compute_values(Operator::Plus, &UntaggedValue::Primitive(current), &UntaggedValue::int(1)) {
|
||||||
Ok(result) => match result {
|
Ok(result) => match result {
|
||||||
UntaggedValue::Primitive(p) => p,
|
UntaggedValue::Primitive(p) => p,
|
||||||
_ => {
|
_ => {
|
||||||
yield Err(ShellError::unimplemented("Internal error: expected a primitive result from increment"));
|
return OutputStream::one(Err(ShellError::unimplemented("Internal error: expected a primitive result from increment")));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err((left_type, right_type)) => {
|
Err((left_type, right_type)) => {
|
||||||
yield Err(ShellError::coerce_error(
|
return OutputStream::one(Err(ShellError::coerce_error(
|
||||||
left_type.spanned(tag.span),
|
left_type.spanned(tag.span),
|
||||||
right_type.spanned(tag.span),
|
right_type.spanned(tag.span),
|
||||||
));
|
)));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match range.to.1 {
|
if let RangeInclusion::Inclusive = range.to.1 {
|
||||||
RangeInclusion::Inclusive => {
|
output_vec.push(Ok(ReturnSuccess::Value(UntaggedValue::Primitive(current).into_value(&tag))));
|
||||||
yield Ok(ReturnSuccess::Value(UntaggedValue::Primitive(current.clone()).into_value(&tag)));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
futures::stream::iter(output_vec.into_iter()).to_output_stream()
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
yield Ok(ReturnSuccess::Value(i.clone()));
|
OutputStream::one(Ok(ReturnSuccess::Value(i.clone())))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
};
|
|
||||||
|
|
||||||
Ok(stream.to_output_stream())
|
Ok(futures::stream::iter(stream).flatten().to_output_stream())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -36,18 +36,25 @@ impl WholeStreamCommand for FromXLSX {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
from_xlsx(args, registry)
|
from_xlsx(args, registry).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_xlsx(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
async fn from_xlsx(
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
let tag = args.call_info.name_tag.clone();
|
let tag = args.call_info.name_tag.clone();
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let stream = async_stream! {
|
let (
|
||||||
let (FromXLSXArgs { headerless: _headerless }, mut input) = args.process(®istry).await?;
|
FromXLSXArgs {
|
||||||
|
headerless: _headerless,
|
||||||
|
},
|
||||||
|
input,
|
||||||
|
) = args.process(®istry).await?;
|
||||||
let value = input.collect_binary(tag.clone()).await?;
|
let value = input.collect_binary(tag.clone()).await?;
|
||||||
|
|
||||||
let mut buf: Cursor<Vec<u8>> = Cursor::new(value.item);
|
let buf: Cursor<Vec<u8>> = Cursor::new(value.item);
|
||||||
let mut xls = Xlsx::<_>::new(buf).map_err(|_| {
|
let mut xls = Xlsx::<_>::new(buf).map_err(|_| {
|
||||||
ShellError::labeled_error("Could not load xlsx file", "could not load xlsx file", &tag)
|
ShellError::labeled_error("Could not load xlsx file", "could not load xlsx file", &tag)
|
||||||
})?;
|
})?;
|
||||||
@ -80,7 +87,7 @@ fn from_xlsx(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
|||||||
|
|
||||||
dict.insert_untagged(sheet_name, sheet_output.into_untagged_value());
|
dict.insert_untagged(sheet_name, sheet_output.into_untagged_value());
|
||||||
} else {
|
} else {
|
||||||
yield Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Could not load sheet",
|
"Could not load sheet",
|
||||||
"could not load sheet",
|
"could not load sheet",
|
||||||
&tag,
|
&tag,
|
||||||
@ -88,10 +95,7 @@ fn from_xlsx(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
yield ReturnSuccess::value(dict.into_value());
|
Ok(OutputStream::one(ReturnSuccess::value(dict.into_value())))
|
||||||
};
|
|
||||||
|
|
||||||
Ok(stream.to_output_stream())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -43,16 +43,27 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
split_column(args, registry)
|
split_column(args, registry).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_column(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
async fn split_column(
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
let name_span = args.call_info.name_tag.span;
|
let name_span = args.call_info.name_tag.span;
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let stream = async_stream! {
|
let (
|
||||||
let (SplitColumnArgs { separator, rest, collapse_empty }, mut input) = args.process(®istry).await?;
|
SplitColumnArgs {
|
||||||
while let Some(v) = input.next().await {
|
separator,
|
||||||
|
rest,
|
||||||
|
collapse_empty,
|
||||||
|
},
|
||||||
|
input,
|
||||||
|
) = args.process(®istry).await?;
|
||||||
|
|
||||||
|
Ok(input
|
||||||
|
.map(move |v| {
|
||||||
if let Ok(s) = v.as_string() {
|
if let Ok(s) = v.as_string() {
|
||||||
let splitter = separator.replace("\\n", "\n");
|
let splitter = separator.replace("\\n", "\n");
|
||||||
trace!("splitting with {:?}", splitter);
|
trace!("splitting with {:?}", splitter);
|
||||||
@ -79,7 +90,7 @@ fn split_column(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputS
|
|||||||
dict.insert_untagged(v.clone(), Primitive::String(k.into()));
|
dict.insert_untagged(v.clone(), Primitive::String(k.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
yield ReturnSuccess::value(dict.into_value());
|
ReturnSuccess::value(dict.into_value())
|
||||||
} else {
|
} else {
|
||||||
let mut dict = TaggedDictBuilder::new(&v.tag);
|
let mut dict = TaggedDictBuilder::new(&v.tag);
|
||||||
for (&k, v) in split_result.iter().zip(positional.iter()) {
|
for (&k, v) in split_result.iter().zip(positional.iter()) {
|
||||||
@ -88,21 +99,19 @@ fn split_column(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputS
|
|||||||
UntaggedValue::Primitive(Primitive::String(k.into())),
|
UntaggedValue::Primitive(Primitive::String(k.into())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
yield ReturnSuccess::value(dict.into_value());
|
ReturnSuccess::value(dict.into_value())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
yield Err(ShellError::labeled_error_with_secondary(
|
Err(ShellError::labeled_error_with_secondary(
|
||||||
"Expected a string from pipeline",
|
"Expected a string from pipeline",
|
||||||
"requires string input",
|
"requires string input",
|
||||||
name_span,
|
name_span,
|
||||||
"value originates from here",
|
"value originates from here",
|
||||||
v.tag.span,
|
v.tag.span,
|
||||||
));
|
))
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
};
|
.to_output_stream())
|
||||||
|
|
||||||
Ok(stream.to_output_stream())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -47,7 +47,7 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
operate(args, registry)
|
operate(args, registry).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -62,54 +62,53 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct DatetimeFormat(String);
|
struct DatetimeFormat(String);
|
||||||
|
|
||||||
fn operate(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
async fn operate(
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
|
|
||||||
let stream = async_stream! {
|
let (Arguments { format, rest }, input) = args.process(®istry).await?;
|
||||||
let (Arguments { format, rest }, mut input) = args.process(®istry).await?;
|
|
||||||
|
|
||||||
let column_paths: Vec<_> = rest.iter().map(|x| x.clone()).collect();
|
let column_paths: Vec<_> = rest;
|
||||||
|
|
||||||
let options = if let Some(Tagged { item: fmt, tag }) = format {
|
let options = if let Some(Tagged { item: fmt, .. }) = format {
|
||||||
DatetimeFormat(fmt)
|
DatetimeFormat(fmt)
|
||||||
} else {
|
} else {
|
||||||
DatetimeFormat(String::from("%d.%m.%Y %H:%M %P %z"))
|
DatetimeFormat(String::from("%d.%m.%Y %H:%M %P %z"))
|
||||||
};
|
};
|
||||||
|
|
||||||
while let Some(v) = input.next().await {
|
Ok(input
|
||||||
|
.map(move |v| {
|
||||||
if column_paths.is_empty() {
|
if column_paths.is_empty() {
|
||||||
match action(&v, &options, v.tag()) {
|
match action(&v, &options, v.tag()) {
|
||||||
Ok(out) => yield ReturnSuccess::value(out),
|
Ok(out) => ReturnSuccess::value(out),
|
||||||
Err(err) => {
|
Err(err) => Err(err),
|
||||||
yield Err(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let mut ret = v;
|
||||||
let mut ret = v.clone();
|
|
||||||
|
|
||||||
for path in &column_paths {
|
for path in &column_paths {
|
||||||
let options = options.clone();
|
let options = options.clone();
|
||||||
let swapping = ret.swap_data_by_column_path(path, Box::new(move |old| action(old, &options, old.tag())));
|
let swapping = ret.swap_data_by_column_path(
|
||||||
|
path,
|
||||||
|
Box::new(move |old| action(old, &options, old.tag())),
|
||||||
|
);
|
||||||
|
|
||||||
match swapping {
|
match swapping {
|
||||||
Ok(new_value) => {
|
Ok(new_value) => {
|
||||||
ret = new_value;
|
ret = new_value;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
yield Err(err);
|
return Err(err);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
yield ReturnSuccess::value(ret);
|
ReturnSuccess::value(ret)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
};
|
.to_output_stream())
|
||||||
|
|
||||||
Ok(stream.to_output_stream())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action(
|
fn action(
|
||||||
|
@ -57,15 +57,24 @@ impl WholeStreamCommand for TSortBy {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
t_sort_by(args, registry)
|
t_sort_by(args, registry).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn t_sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
async fn t_sort_by(
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let stream = async_stream! {
|
|
||||||
let name = args.call_info.name_tag.clone();
|
let name = args.call_info.name_tag.clone();
|
||||||
let (TSortByArgs { show_columns, group_by, ..}, mut input) = args.process(®istry).await?;
|
let (
|
||||||
|
TSortByArgs {
|
||||||
|
show_columns,
|
||||||
|
group_by,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
mut input,
|
||||||
|
) = args.process(®istry).await?;
|
||||||
let values: Vec<Value> = input.collect().await;
|
let values: Vec<Value> = input.collect().await;
|
||||||
|
|
||||||
let column_grouped_by_name = if let Some(grouped_by) = group_by {
|
let column_grouped_by_name = if let Some(grouped_by) = group_by {
|
||||||
@ -75,18 +84,20 @@ fn t_sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
|||||||
};
|
};
|
||||||
|
|
||||||
if show_columns {
|
if show_columns {
|
||||||
for label in columns_sorted(column_grouped_by_name, &values[0], &name).into_iter() {
|
Ok(futures::stream::iter(
|
||||||
yield ReturnSuccess::value(UntaggedValue::string(label.item).into_value(label.tag));
|
columns_sorted(column_grouped_by_name, &values[0], &name)
|
||||||
}
|
.into_iter()
|
||||||
|
.map(move |label| {
|
||||||
|
ReturnSuccess::value(UntaggedValue::string(label.item).into_value(label.tag))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.to_output_stream())
|
||||||
} else {
|
} else {
|
||||||
match t_sort(column_grouped_by_name, None, &values[0], name) {
|
match t_sort(column_grouped_by_name, None, &values[0], name) {
|
||||||
Ok(sorted) => yield ReturnSuccess::value(sorted),
|
Ok(sorted) => Ok(OutputStream::one(ReturnSuccess::value(sorted))),
|
||||||
Err(err) => yield Err(err)
|
Err(err) => Ok(OutputStream::one(Err(err))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
Ok(stream.to_output_stream())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand;
|
|||||||
use crate::format::TableView;
|
use crate::format::TableView;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
const STREAM_PAGE_SIZE: usize = 1000;
|
const STREAM_PAGE_SIZE: usize = 1000;
|
||||||
@ -34,29 +34,32 @@ impl WholeStreamCommand for Table {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
table(args, registry)
|
table(args, registry).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn table(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
async fn table(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let stream = async_stream! {
|
|
||||||
let mut args = args.evaluate_once(®istry).await?;
|
let mut args = args.evaluate_once(®istry).await?;
|
||||||
let mut finished = false;
|
let mut finished = false;
|
||||||
|
|
||||||
let host = args.host.clone();
|
let host = args.host.clone();
|
||||||
let mut start_number = match args.get("start_number") {
|
let mut start_number = match args.get("start_number") {
|
||||||
Some(Value { value: UntaggedValue::Primitive(Primitive::Int(i)), .. }) => {
|
Some(Value {
|
||||||
|
value: UntaggedValue::Primitive(Primitive::Int(i)),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
if let Some(num) = i.to_usize() {
|
if let Some(num) = i.to_usize() {
|
||||||
num
|
num
|
||||||
} else {
|
} else {
|
||||||
yield Err(ShellError::labeled_error("Expected a row number", "expected a row number", &args.args.call_info.name_tag));
|
return Err(ShellError::labeled_error(
|
||||||
0
|
"Expected a row number",
|
||||||
|
"expected a row number",
|
||||||
|
&args.args.call_info.name_tag,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => 0,
|
||||||
0
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut delay_slot = None;
|
let mut delay_slot = None;
|
||||||
@ -109,7 +112,7 @@ fn table(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
|
|||||||
|
|
||||||
let input: Vec<Value> = new_input.into();
|
let input: Vec<Value> = new_input.into();
|
||||||
|
|
||||||
if input.len() > 0 {
|
if !input.is_empty() {
|
||||||
let mut host = host.lock();
|
let mut host = host.lock();
|
||||||
let view = TableView::from_list(&input, start_number);
|
let view = TableView::from_list(&input, start_number);
|
||||||
|
|
||||||
@ -121,13 +124,7 @@ fn table(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
|
|||||||
start_number += input.len();
|
start_number += input.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needed for async_stream to type check
|
Ok(OutputStream::empty())
|
||||||
if false {
|
|
||||||
yield ReturnSuccess::value(UntaggedValue::nothing().into_value(Tag::unknown()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(OutputStream::new(stream))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -29,7 +29,7 @@ impl WholeStreamCommand for ToBSON {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
to_bson(args, registry)
|
to_bson(args, registry).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_binary(&self) -> bool {
|
fn is_binary(&self) -> bool {
|
||||||
@ -261,34 +261,37 @@ fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result<Vec<u8>, ShellError> {
|
|||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
async fn to_bson(
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let stream = async_stream! {
|
|
||||||
let args = args.evaluate_once(®istry).await?;
|
let args = args.evaluate_once(®istry).await?;
|
||||||
let name_tag = args.name_tag();
|
let name_tag = args.name_tag();
|
||||||
let name_span = name_tag.span;
|
let name_span = name_tag.span;
|
||||||
|
|
||||||
let input: Vec<Value> = args.input.collect().await;
|
let input: Vec<Value> = args.input.collect().await;
|
||||||
|
|
||||||
let to_process_input = if input.len() > 1 {
|
let to_process_input = match input.len() {
|
||||||
|
x if x > 1 => {
|
||||||
let tag = input[0].tag.clone();
|
let tag = input[0].tag.clone();
|
||||||
vec![Value { value: UntaggedValue::Table(input), tag } ]
|
vec![Value {
|
||||||
} else if input.len() == 1 {
|
value: UntaggedValue::Table(input),
|
||||||
input
|
tag,
|
||||||
} else {
|
}]
|
||||||
vec![]
|
}
|
||||||
|
1 => input,
|
||||||
|
_ => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
for value in to_process_input {
|
Ok(futures::stream::iter(to_process_input.into_iter().map(
|
||||||
match value_to_bson_value(&value) {
|
move |value| match value_to_bson_value(&value) {
|
||||||
Ok(bson_value) => {
|
Ok(bson_value) => {
|
||||||
let value_span = value.tag.span;
|
let value_span = value.tag.span;
|
||||||
|
|
||||||
match bson_value_to_bytes(bson_value, name_tag.clone()) {
|
match bson_value_to_bytes(bson_value, name_tag.clone()) {
|
||||||
Ok(x) => yield ReturnSuccess::value(
|
Ok(x) => ReturnSuccess::value(UntaggedValue::binary(x).into_value(&name_tag)),
|
||||||
UntaggedValue::binary(x).into_value(&name_tag),
|
_ => Err(ShellError::labeled_error_with_secondary(
|
||||||
),
|
|
||||||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
|
||||||
"Expected a table with BSON-compatible structure from pipeline",
|
"Expected a table with BSON-compatible structure from pipeline",
|
||||||
"requires BSON-compatible input",
|
"requires BSON-compatible input",
|
||||||
name_span,
|
name_span,
|
||||||
@ -297,15 +300,14 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => yield Err(ShellError::labeled_error(
|
_ => Err(ShellError::labeled_error(
|
||||||
"Expected a table with BSON-compatible structure from pipeline",
|
"Expected a table with BSON-compatible structure from pipeline",
|
||||||
"requires BSON-compatible input",
|
"requires BSON-compatible input",
|
||||||
&name_tag))
|
&name_tag,
|
||||||
}
|
)),
|
||||||
}
|
},
|
||||||
};
|
))
|
||||||
|
.to_output_stream())
|
||||||
Ok(stream.to_output_stream())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -42,15 +42,20 @@ impl WholeStreamCommand for ToCSV {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
to_csv(args, registry)
|
to_csv(args, registry).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_csv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
async fn to_csv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let stream = async_stream! {
|
|
||||||
let name = args.call_info.name_tag.clone();
|
let name = args.call_info.name_tag.clone();
|
||||||
let (ToCSVArgs { separator, headerless }, mut input) = args.process(®istry).await?;
|
let (
|
||||||
|
ToCSVArgs {
|
||||||
|
separator,
|
||||||
|
headerless,
|
||||||
|
},
|
||||||
|
input,
|
||||||
|
) = args.process(®istry).await?;
|
||||||
let sep = match separator {
|
let sep = match separator {
|
||||||
Some(Value {
|
Some(Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::String(s)),
|
value: UntaggedValue::Primitive(Primitive::String(s)),
|
||||||
@ -62,12 +67,11 @@ fn to_csv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
|
|||||||
} else {
|
} else {
|
||||||
let vec_s: Vec<char> = s.chars().collect();
|
let vec_s: Vec<char> = s.chars().collect();
|
||||||
if vec_s.len() != 1 {
|
if vec_s.len() != 1 {
|
||||||
yield Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Expected a single separator char from --separator",
|
"Expected a single separator char from --separator",
|
||||||
"requires a single character string input",
|
"requires a single character string input",
|
||||||
tag,
|
tag,
|
||||||
));
|
));
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
vec_s[0]
|
vec_s[0]
|
||||||
}
|
}
|
||||||
@ -75,14 +79,7 @@ fn to_csv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
|
|||||||
_ => ',',
|
_ => ',',
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut result = to_delimited_data(headerless, sep, "CSV", input, name)?;
|
to_delimited_data(headerless, sep, "CSV", input, name).await
|
||||||
|
|
||||||
while let Some(item) = result.next().await {
|
|
||||||
yield item;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(stream.to_output_stream())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -165,7 +165,7 @@ fn merge_descriptors(values: &[Value]) -> Vec<Spanned<String>> {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_delimited_data(
|
pub async fn to_delimited_data(
|
||||||
headerless: bool,
|
headerless: bool,
|
||||||
sep: char,
|
sep: char,
|
||||||
format_name: &'static str,
|
format_name: &'static str,
|
||||||
@ -175,33 +175,41 @@ pub fn to_delimited_data(
|
|||||||
let name_tag = name;
|
let name_tag = name;
|
||||||
let name_span = name_tag.span;
|
let name_span = name_tag.span;
|
||||||
|
|
||||||
let stream = async_stream! {
|
|
||||||
let input: Vec<Value> = input.collect().await;
|
let input: Vec<Value> = input.collect().await;
|
||||||
|
|
||||||
let to_process_input = if input.len() > 1 {
|
let to_process_input = match input.len() {
|
||||||
|
x if x > 1 => {
|
||||||
let tag = input[0].tag.clone();
|
let tag = input[0].tag.clone();
|
||||||
vec![Value { value: UntaggedValue::Table(input), tag } ]
|
vec![Value {
|
||||||
} else if input.len() == 1 {
|
value: UntaggedValue::Table(input),
|
||||||
input
|
tag,
|
||||||
} else {
|
}]
|
||||||
vec![]
|
}
|
||||||
|
1 => input,
|
||||||
|
_ => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
for value in to_process_input {
|
Ok(
|
||||||
|
futures::stream::iter(to_process_input.into_iter().map(move |value| {
|
||||||
match from_value_to_delimited_string(&clone_tagged_value(&value), sep) {
|
match from_value_to_delimited_string(&clone_tagged_value(&value), sep) {
|
||||||
Ok(mut x) => {
|
Ok(mut x) => {
|
||||||
if headerless {
|
if headerless {
|
||||||
x.find('\n').map(|second_line|{
|
if let Some(second_line) = x.find('\n') {
|
||||||
let start = second_line + 1;
|
let start = second_line + 1;
|
||||||
x.replace_range(0..start, "");
|
x.replace_range(0..start, "");
|
||||||
});
|
|
||||||
}
|
}
|
||||||
yield ReturnSuccess::value(UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag))
|
|
||||||
}
|
}
|
||||||
Err(x) => {
|
ReturnSuccess::value(
|
||||||
let expected = format!("Expected a table with {}-compatible structure from pipeline", format_name);
|
UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
let expected = format!(
|
||||||
|
"Expected a table with {}-compatible structure from pipeline",
|
||||||
|
format_name
|
||||||
|
);
|
||||||
let requires = format!("requires {}-compatible input", format_name);
|
let requires = format!("requires {}-compatible input", format_name);
|
||||||
yield Err(ShellError::labeled_error_with_secondary(
|
Err(ShellError::labeled_error_with_secondary(
|
||||||
expected,
|
expected,
|
||||||
requires,
|
requires,
|
||||||
name_span,
|
name_span,
|
||||||
@ -210,8 +218,7 @@ pub fn to_delimited_data(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}))
|
||||||
};
|
.to_output_stream(),
|
||||||
|
)
|
||||||
Ok(stream.to_output_stream())
|
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ impl WholeStreamCommand for ToJSON {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
to_json(args, registry)
|
to_json(args, registry).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -163,25 +163,30 @@ fn json_list(input: &[Value]) -> Result<Vec<serde_json::Value>, ShellError> {
|
|||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
async fn to_json(
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let stream = async_stream! {
|
|
||||||
let name_tag = args.call_info.name_tag.clone();
|
let name_tag = args.call_info.name_tag.clone();
|
||||||
let (ToJSONArgs { pretty }, mut input) = args.process(®istry).await?;
|
let (ToJSONArgs { pretty }, input) = args.process(®istry).await?;
|
||||||
let name_span = name_tag.span;
|
let name_span = name_tag.span;
|
||||||
let input: Vec<Value> = input.collect().await;
|
let input: Vec<Value> = input.collect().await;
|
||||||
|
|
||||||
let to_process_input = if input.len() > 1 {
|
let to_process_input = match input.len() {
|
||||||
|
x if x > 1 => {
|
||||||
let tag = input[0].tag.clone();
|
let tag = input[0].tag.clone();
|
||||||
vec![Value { value: UntaggedValue::Table(input), tag } ]
|
vec![Value {
|
||||||
} else if input.len() == 1 {
|
value: UntaggedValue::Table(input),
|
||||||
input
|
tag,
|
||||||
} else {
|
}]
|
||||||
vec![]
|
}
|
||||||
|
1 => input,
|
||||||
|
_ => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
for value in to_process_input {
|
Ok(futures::stream::iter(to_process_input.into_iter().map(
|
||||||
match value_to_json_value(&value) {
|
move |value| match value_to_json_value(&value) {
|
||||||
Ok(json_value) => {
|
Ok(json_value) => {
|
||||||
let value_span = value.tag.span;
|
let value_span = value.tag.span;
|
||||||
|
|
||||||
@ -191,15 +196,32 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
|||||||
let mut pretty_format_failed = true;
|
let mut pretty_format_failed = true;
|
||||||
|
|
||||||
if let Ok(pretty_u64) = pretty_value.as_u64() {
|
if let Ok(pretty_u64) = pretty_value.as_u64() {
|
||||||
if let Ok(serde_json_value) = serde_json::from_str::<serde_json::Value>(serde_json_string.as_str()) {
|
if let Ok(serde_json_value) =
|
||||||
let indentation_string = std::iter::repeat(" ").take(pretty_u64 as usize).collect::<String>();
|
serde_json::from_str::<serde_json::Value>(
|
||||||
let serde_formatter = serde_json::ser::PrettyFormatter::with_indent(indentation_string.as_bytes());
|
serde_json_string.as_str(),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
let indentation_string = std::iter::repeat(" ")
|
||||||
|
.take(pretty_u64 as usize)
|
||||||
|
.collect::<String>();
|
||||||
|
let serde_formatter =
|
||||||
|
serde_json::ser::PrettyFormatter::with_indent(
|
||||||
|
indentation_string.as_bytes(),
|
||||||
|
);
|
||||||
let serde_buffer = Vec::new();
|
let serde_buffer = Vec::new();
|
||||||
let mut serde_serializer = serde_json::Serializer::with_formatter(serde_buffer, serde_formatter);
|
let mut serde_serializer =
|
||||||
|
serde_json::Serializer::with_formatter(
|
||||||
|
serde_buffer,
|
||||||
|
serde_formatter,
|
||||||
|
);
|
||||||
let serde_json_object = json!(serde_json_value);
|
let serde_json_object = json!(serde_json_value);
|
||||||
|
|
||||||
if let Ok(()) = serde_json_object.serialize(&mut serde_serializer) {
|
if let Ok(()) =
|
||||||
if let Ok(ser_json_string) = String::from_utf8(serde_serializer.into_inner()) {
|
serde_json_object.serialize(&mut serde_serializer)
|
||||||
|
{
|
||||||
|
if let Ok(ser_json_string) =
|
||||||
|
String::from_utf8(serde_serializer.into_inner())
|
||||||
|
{
|
||||||
pretty_format_failed = false;
|
pretty_format_failed = false;
|
||||||
serde_json_string = ser_json_string
|
serde_json_string = ser_json_string
|
||||||
}
|
}
|
||||||
@ -208,16 +230,20 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
|||||||
}
|
}
|
||||||
|
|
||||||
if pretty_format_failed {
|
if pretty_format_failed {
|
||||||
yield Err(ShellError::labeled_error("Pretty formatting failed", "failed", pretty_value.tag()));
|
return Err(ShellError::labeled_error(
|
||||||
return;
|
"Pretty formatting failed",
|
||||||
|
"failed",
|
||||||
|
pretty_value.tag(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
yield ReturnSuccess::value(
|
ReturnSuccess::value(
|
||||||
UntaggedValue::Primitive(Primitive::String(serde_json_string)).into_value(&name_tag),
|
UntaggedValue::Primitive(Primitive::String(serde_json_string))
|
||||||
|
.into_value(&name_tag),
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
_ => Err(ShellError::labeled_error_with_secondary(
|
||||||
"Expected a table with JSON-compatible structure.tag() from pipeline",
|
"Expected a table with JSON-compatible structure.tag() from pipeline",
|
||||||
"requires JSON-compatible input",
|
"requires JSON-compatible input",
|
||||||
name_span,
|
name_span,
|
||||||
@ -226,15 +252,14 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => yield Err(ShellError::labeled_error(
|
_ => Err(ShellError::labeled_error(
|
||||||
"Expected a table with JSON-compatible structure from pipeline",
|
"Expected a table with JSON-compatible structure from pipeline",
|
||||||
"requires JSON-compatible input",
|
"requires JSON-compatible input",
|
||||||
&name_tag))
|
&name_tag,
|
||||||
}
|
)),
|
||||||
}
|
},
|
||||||
};
|
))
|
||||||
|
.to_output_stream())
|
||||||
Ok(stream.to_output_stream())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -24,7 +24,7 @@ impl WholeStreamCommand for ToTOML {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
to_toml(args, registry)
|
to_toml(args, registry).await
|
||||||
}
|
}
|
||||||
// TODO: add an example here. What commands to run to get a Row(Dictionary)?
|
// TODO: add an example here. What commands to run to get a Row(Dictionary)?
|
||||||
// fn examples(&self) -> Vec<Example> {
|
// fn examples(&self) -> Vec<Example> {
|
||||||
@ -135,49 +135,53 @@ fn collect_values(input: &[Value]) -> Result<Vec<toml::Value>, ShellError> {
|
|||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
async fn to_toml(
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let stream = async_stream! {
|
|
||||||
let args = args.evaluate_once(®istry).await?;
|
let args = args.evaluate_once(®istry).await?;
|
||||||
let name_tag = args.name_tag();
|
let name_tag = args.name_tag();
|
||||||
let name_span = name_tag.span;
|
let name_span = name_tag.span;
|
||||||
let input: Vec<Value> = args.input.collect().await;
|
let input: Vec<Value> = args.input.collect().await;
|
||||||
|
|
||||||
let to_process_input = if input.len() > 1 {
|
let to_process_input = match input.len() {
|
||||||
|
x if x > 1 => {
|
||||||
let tag = input[0].tag.clone();
|
let tag = input[0].tag.clone();
|
||||||
vec![Value { value: UntaggedValue::Table(input), tag } ]
|
vec![Value {
|
||||||
} else if input.len() == 1 {
|
value: UntaggedValue::Table(input),
|
||||||
input
|
tag,
|
||||||
} else {
|
}]
|
||||||
vec![]
|
}
|
||||||
|
1 => input,
|
||||||
|
_ => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
for value in to_process_input {
|
Ok(
|
||||||
|
futures::stream::iter(to_process_input.into_iter().map(move |value| {
|
||||||
let value_span = value.tag.span;
|
let value_span = value.tag.span;
|
||||||
match value_to_toml_value(&value) {
|
match value_to_toml_value(&value) {
|
||||||
Ok(toml_value) => {
|
Ok(toml_value) => match toml::to_string(&toml_value) {
|
||||||
match toml::to_string(&toml_value) {
|
Ok(x) => ReturnSuccess::value(
|
||||||
Ok(x) => yield ReturnSuccess::value(
|
|
||||||
UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag),
|
UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag),
|
||||||
),
|
),
|
||||||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
_ => Err(ShellError::labeled_error_with_secondary(
|
||||||
"Expected a table with TOML-compatible structure.tag() from pipeline",
|
"Expected a table with TOML-compatible structure.tag() from pipeline",
|
||||||
"requires TOML-compatible input",
|
"requires TOML-compatible input",
|
||||||
name_span,
|
name_span,
|
||||||
"originates from here".to_string(),
|
"originates from here".to_string(),
|
||||||
value_span,
|
value_span,
|
||||||
)),
|
)),
|
||||||
}
|
},
|
||||||
}
|
_ => Err(ShellError::labeled_error(
|
||||||
_ => yield Err(ShellError::labeled_error(
|
|
||||||
"Expected a table with TOML-compatible structure from pipeline",
|
"Expected a table with TOML-compatible structure from pipeline",
|
||||||
"requires TOML-compatible input",
|
"requires TOML-compatible input",
|
||||||
&name_tag))
|
&name_tag,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}))
|
||||||
};
|
.to_output_stream(),
|
||||||
|
)
|
||||||
Ok(stream.to_output_stream())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -43,7 +43,7 @@ async fn to_tsv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputS
|
|||||||
let name = args.call_info.name_tag.clone();
|
let name = args.call_info.name_tag.clone();
|
||||||
let (ToTSVArgs { headerless }, input) = args.process(®istry).await?;
|
let (ToTSVArgs { headerless }, input) = args.process(®istry).await?;
|
||||||
|
|
||||||
to_delimited_data(headerless, '\t', "TSV", input, name)
|
to_delimited_data(headerless, '\t', "TSV", input, name).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -35,7 +35,7 @@ impl WholeStreamCommand for Where {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
where_command(args, registry)
|
where_command(args, registry).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -63,65 +63,71 @@ impl WholeStreamCommand for Where {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn where_command(
|
async fn where_command(
|
||||||
raw_args: CommandArgs,
|
raw_args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = Arc::new(registry.clone());
|
||||||
let scope = raw_args.call_info.scope.clone();
|
let scope = Arc::new(raw_args.call_info.scope.clone());
|
||||||
let tag = raw_args.call_info.name_tag.clone();
|
let tag = raw_args.call_info.name_tag.clone();
|
||||||
let stream = async_stream! {
|
let (WhereArgs { block }, input) = raw_args.process(®istry).await?;
|
||||||
let (WhereArgs { block }, mut input) = raw_args.process(®istry).await?;
|
|
||||||
let condition = {
|
let condition = {
|
||||||
if block.block.len() != 1 {
|
if block.block.len() != 1 {
|
||||||
yield Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Expected a condition",
|
"Expected a condition",
|
||||||
"expected a condition",
|
"expected a condition",
|
||||||
tag,
|
tag,
|
||||||
));
|
));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
match block.block[0].list.get(0) {
|
match block.block[0].list.get(0) {
|
||||||
Some(item) => match item {
|
Some(item) => match item {
|
||||||
ClassifiedCommand::Expr(expr) => expr.clone(),
|
ClassifiedCommand::Expr(expr) => expr.clone(),
|
||||||
_ => {
|
_ => {
|
||||||
yield Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Expected a condition",
|
"Expected a condition",
|
||||||
"expected a condition",
|
"expected a condition",
|
||||||
tag,
|
tag,
|
||||||
));
|
));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
yield Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Expected a condition",
|
"Expected a condition",
|
||||||
"expected a condition",
|
"expected a condition",
|
||||||
tag,
|
tag,
|
||||||
));
|
));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut input = input;
|
Ok(input
|
||||||
while let Some(input) = input.next().await {
|
.filter_map(move |input| {
|
||||||
|
let condition = condition.clone();
|
||||||
|
let registry = registry.clone();
|
||||||
|
let scope = scope.clone();
|
||||||
|
|
||||||
|
async move {
|
||||||
//FIXME: should we use the scope that's brought in as well?
|
//FIXME: should we use the scope that's brought in as well?
|
||||||
let condition = evaluate_baseline_expr(&condition, ®istry, &input, &scope.vars, &scope.env).await?;
|
let condition =
|
||||||
|
evaluate_baseline_expr(&condition, &*registry, &input, &scope.vars, &scope.env)
|
||||||
|
.await;
|
||||||
|
|
||||||
match condition.as_bool() {
|
match condition {
|
||||||
|
Ok(condition) => match condition.as_bool() {
|
||||||
Ok(b) => {
|
Ok(b) => {
|
||||||
if b {
|
if b {
|
||||||
yield Ok(ReturnSuccess::Value(input));
|
Some(Ok(ReturnSuccess::Value(input)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => yield Err(e),
|
Err(e) => Some(Err(e)),
|
||||||
};
|
},
|
||||||
|
Err(e) => Some(Err(e)),
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
})
|
||||||
Ok(stream.to_output_stream())
|
.to_output_stream())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
Loading…
Reference in New Issue
Block a user