mirror of
https://github.com/nushell/nushell.git
synced 2025-06-30 22:50:14 +02:00
Merge branch 'master' of github.com:nushell/nushell
This commit is contained in:
@ -291,6 +291,7 @@ pub fn create_default_context(
|
||||
whole_stream_command(Lines),
|
||||
whole_stream_command(Trim),
|
||||
whole_stream_command(Echo),
|
||||
whole_stream_command(Parse),
|
||||
whole_stream_command(Str),
|
||||
whole_stream_command(StrToDecimal),
|
||||
whole_stream_command(StrToInteger),
|
||||
|
@ -74,6 +74,7 @@ pub(crate) mod mv;
|
||||
pub(crate) mod next;
|
||||
pub(crate) mod nth;
|
||||
pub(crate) mod open;
|
||||
pub(crate) mod parse;
|
||||
pub(crate) mod pivot;
|
||||
pub(crate) mod plugin;
|
||||
pub(crate) mod prepend;
|
||||
@ -203,6 +204,7 @@ pub(crate) use mv::Move;
|
||||
pub(crate) use next::Next;
|
||||
pub(crate) use nth::Nth;
|
||||
pub(crate) use open::Open;
|
||||
pub(crate) use parse::Parse;
|
||||
pub(crate) use pivot::Pivot;
|
||||
pub(crate) use prepend::Prepend;
|
||||
pub(crate) use prev::Previous;
|
||||
|
@ -182,8 +182,8 @@ pub fn get_help(cmd: &dyn WholeStreamCommand, registry: &CommandRegistry) -> Str
|
||||
|
||||
if !signature.positional.is_empty() || signature.rest_positional.is_some() {
|
||||
long_desc.push_str("\nParameters:\n");
|
||||
for positional in signature.positional {
|
||||
match positional.0 {
|
||||
for positional in &signature.positional {
|
||||
match &positional.0 {
|
||||
PositionalType::Mandatory(name, _m) => {
|
||||
long_desc.push_str(&format!(" <{}> {}\n", name, positional.1));
|
||||
}
|
||||
@ -193,75 +193,12 @@ pub fn get_help(cmd: &dyn WholeStreamCommand, registry: &CommandRegistry) -> Str
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(rest_positional) = signature.rest_positional {
|
||||
if let Some(rest_positional) = &signature.rest_positional {
|
||||
long_desc.push_str(&format!(" ...args: {}\n", rest_positional.1));
|
||||
}
|
||||
}
|
||||
if !signature.named.is_empty() {
|
||||
long_desc.push_str("\nFlags:\n");
|
||||
for (flag, ty) in signature.named {
|
||||
let msg = match ty.0 {
|
||||
NamedType::Switch(s) => {
|
||||
if let Some(c) = s {
|
||||
format!(
|
||||
" -{}, --{}{} {}\n",
|
||||
c,
|
||||
flag,
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
" --{}{} {}\n",
|
||||
flag,
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
}
|
||||
}
|
||||
NamedType::Mandatory(s, m) => {
|
||||
if let Some(c) = s {
|
||||
format!(
|
||||
" -{}, --{} <{}> (required parameter){} {}\n",
|
||||
c,
|
||||
flag,
|
||||
m.display(),
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
" --{} <{}> (required parameter){} {}\n",
|
||||
flag,
|
||||
m.display(),
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
}
|
||||
}
|
||||
NamedType::Optional(s, o) => {
|
||||
if let Some(c) = s {
|
||||
format!(
|
||||
" -{}, --{} <{}>{} {}\n",
|
||||
c,
|
||||
flag,
|
||||
o.display(),
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
" --{} <{}>{} {}\n",
|
||||
flag,
|
||||
o.display(),
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
long_desc.push_str(&msg);
|
||||
}
|
||||
long_desc.push_str(&get_flags_section(&signature))
|
||||
}
|
||||
|
||||
let palette = crate::shell::palette::DefaultPalette {};
|
||||
@ -283,6 +220,75 @@ pub fn get_help(cmd: &dyn WholeStreamCommand, registry: &CommandRegistry) -> Str
|
||||
long_desc
|
||||
}
|
||||
|
||||
fn get_flags_section(signature: &Signature) -> String {
|
||||
let mut long_desc = String::new();
|
||||
long_desc.push_str("\nFlags:\n");
|
||||
for (flag, ty) in &signature.named {
|
||||
let msg = match ty.0 {
|
||||
NamedType::Switch(s) => {
|
||||
if let Some(c) = s {
|
||||
format!(
|
||||
" -{}, --{}{} {}\n",
|
||||
c,
|
||||
flag,
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
" --{}{} {}\n",
|
||||
flag,
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
}
|
||||
}
|
||||
NamedType::Mandatory(s, m) => {
|
||||
if let Some(c) = s {
|
||||
format!(
|
||||
" -{}, --{} <{}> (required parameter){} {}\n",
|
||||
c,
|
||||
flag,
|
||||
m.display(),
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
" --{} <{}> (required parameter){} {}\n",
|
||||
flag,
|
||||
m.display(),
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
}
|
||||
}
|
||||
NamedType::Optional(s, o) => {
|
||||
if let Some(c) = s {
|
||||
format!(
|
||||
" -{}, --{} <{}>{} {}\n",
|
||||
c,
|
||||
flag,
|
||||
o.display(),
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
" --{} <{}>{} {}\n",
|
||||
flag,
|
||||
o.display(),
|
||||
if !ty.1.is_empty() { ":" } else { "" },
|
||||
ty.1
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
long_desc.push_str(&msg);
|
||||
}
|
||||
long_desc
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Help;
|
||||
|
@ -32,7 +32,7 @@ impl WholeStreamCommand for Mkdir {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
mkdir(args, registry)
|
||||
mkdir(args, registry).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -44,20 +44,13 @@ impl WholeStreamCommand for Mkdir {
|
||||
}
|
||||
}
|
||||
|
||||
fn mkdir(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
async fn mkdir(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let stream = async_stream! {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let (args, _) = args.process(®istry).await?;
|
||||
let mut result = shell_manager.mkdir(args, name)?;
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let (args, _) = args.process(®istry).await?;
|
||||
|
||||
while let Some(item) = result.next().await {
|
||||
yield item;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
shell_manager.mkdir(args, name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
175
crates/nu-cli/src/commands/parse/command.rs
Normal file
175
crates/nu-cli/src/commands/parse/command.rs
Normal file
@ -0,0 +1,175 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Arguments {
|
||||
pattern: Tagged<String>,
|
||||
regex: Tagged<bool>,
|
||||
}
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"parse"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("parse")
|
||||
.required(
|
||||
"pattern",
|
||||
SyntaxShape::String,
|
||||
"the pattern to match. Eg) \"{foo}: {bar}\"",
|
||||
)
|
||||
.switch("regex", "use full regex syntax for patterns", Some('r'))
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Parse columns from string data using a simple pattern."
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
operate(args, registry).await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn operate(
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
let (Arguments { regex, pattern }, mut input) = args.process(®istry).await?;
|
||||
|
||||
let regex_pattern = if let Tagged { item: true, tag } = regex {
|
||||
Regex::new(&pattern.item)
|
||||
.map_err(|_| ShellError::labeled_error("Invalid regex", "invalid regex", tag.span))?
|
||||
} else {
|
||||
let parse_regex = build_regex(&pattern.item, name_tag.clone())?;
|
||||
|
||||
Regex::new(&parse_regex).map_err(|_| {
|
||||
ShellError::labeled_error("Invalid pattern", "invalid pattern", name_tag.span)
|
||||
})?
|
||||
};
|
||||
|
||||
let columns = column_names(®ex_pattern);
|
||||
let mut parsed: VecDeque<Value> = VecDeque::new();
|
||||
|
||||
while let Some(v) = input.next().await {
|
||||
match v.as_string() {
|
||||
Ok(s) => {
|
||||
let results = regex_pattern.captures_iter(&s);
|
||||
|
||||
for c in results {
|
||||
let mut dict = TaggedDictBuilder::new(&v.tag);
|
||||
|
||||
for (column_name, cap) in columns.iter().zip(c.iter().skip(1)) {
|
||||
let cap_string = cap.map(|v| v.as_str()).unwrap_or("").to_string();
|
||||
dict.insert_untagged(column_name, UntaggedValue::string(cap_string));
|
||||
}
|
||||
|
||||
parsed.push_back(dict.into_value());
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected string input",
|
||||
"expected string input",
|
||||
&name_tag,
|
||||
"value originated here",
|
||||
v.tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(futures::stream::iter(parsed).to_output_stream())
|
||||
}
|
||||
|
||||
fn build_regex(input: &str, tag: Tag) -> Result<String, ShellError> {
|
||||
let mut output = "(?s)\\A".to_string();
|
||||
|
||||
//let mut loop_input = input;
|
||||
let mut loop_input = input.chars().peekable();
|
||||
loop {
|
||||
let mut before = String::new();
|
||||
while let Some(c) = loop_input.next() {
|
||||
if c == '{' {
|
||||
// If '{{', still creating a plaintext parse command, but just for a single '{' char
|
||||
if loop_input.peek() == Some(&'{') {
|
||||
let _ = loop_input.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
before.push(c);
|
||||
}
|
||||
|
||||
if !before.is_empty() {
|
||||
output.push_str(®ex::escape(&before));
|
||||
}
|
||||
|
||||
// Look for column as we're now at one
|
||||
let mut column = String::new();
|
||||
while let Some(c) = loop_input.next() {
|
||||
if c == '}' {
|
||||
break;
|
||||
}
|
||||
column.push(c);
|
||||
|
||||
if loop_input.peek().is_none() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Found opening `{` without an associated closing `}`",
|
||||
"invalid parse pattern",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if !column.is_empty() {
|
||||
output.push_str("(?P<");
|
||||
output.push_str(&column);
|
||||
output.push_str(">.*?)");
|
||||
}
|
||||
|
||||
if before.is_empty() && column.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output.push_str("\\z");
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn column_names(regex: &Regex) -> Vec<String> {
|
||||
regex
|
||||
.capture_names()
|
||||
.enumerate()
|
||||
.skip(1)
|
||||
.map(|(i, name)| {
|
||||
name.map(String::from)
|
||||
.unwrap_or_else(|| format!("Capture{}", i))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Command;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Command {})
|
||||
}
|
||||
}
|
3
crates/nu-cli/src/commands/parse/mod.rs
Normal file
3
crates/nu-cli/src/commands/parse/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod command;
|
||||
|
||||
pub use command::Command as Parse;
|
@ -49,7 +49,7 @@ impl WholeStreamCommand for Remove {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
rm(args, registry)
|
||||
rm(args, registry).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -73,27 +73,21 @@ impl WholeStreamCommand for Remove {
|
||||
}
|
||||
}
|
||||
|
||||
fn rm(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
async fn rm(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let stream = async_stream! {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let (args, _): (RemoveArgs, _) = args.process(®istry).await?;
|
||||
let mut result = if args.trash.item && args.permanent.item {
|
||||
OutputStream::one(Err(ShellError::labeled_error(
|
||||
"only one of --permanent and --trash can be used",
|
||||
"conflicting flags",
|
||||
name
|
||||
)))
|
||||
} else {
|
||||
shell_manager.rm(args, name)?
|
||||
};
|
||||
while let Some(item) = result.next().await {
|
||||
yield item;
|
||||
}
|
||||
};
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let (args, _): (RemoveArgs, _) = args.process(®istry).await?;
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
if args.trash.item && args.permanent.item {
|
||||
return Ok(OutputStream::one(Err(ShellError::labeled_error(
|
||||
"only one of --permanent and --trash can be used",
|
||||
"conflicting flags",
|
||||
name,
|
||||
))));
|
||||
}
|
||||
|
||||
shell_manager.rm(args, name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -31,7 +31,7 @@ impl WholeStreamCommand for SortBy {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
sort_by(args, registry)
|
||||
sort_by(args, registry).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -60,51 +60,59 @@ impl WholeStreamCommand for SortBy {
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
async fn sort_by(
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let stream = async_stream! {
|
||||
let (SortByArgs { rest }, mut input) = args.process(®istry).await?;
|
||||
let mut vec = input.drain_vec().await;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
if vec.is_empty() {
|
||||
return;
|
||||
let (SortByArgs { rest }, mut input) = args.process(®istry).await?;
|
||||
let mut vec = input.drain_vec().await;
|
||||
|
||||
if vec.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Error performing sort-by command",
|
||||
"sort-by error",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
|
||||
for sort_arg in rest.iter() {
|
||||
let match_test = get_data_by_key(&vec[0], sort_arg.borrow_spanned());
|
||||
if match_test == None {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Can not find column to sort by",
|
||||
"invalid column",
|
||||
sort_arg.borrow_spanned().span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for sort_arg in rest.iter() {
|
||||
let match_test = get_data_by_key(&vec[0], sort_arg.borrow_spanned());
|
||||
if match_test == None {
|
||||
yield Err(ShellError::labeled_error(
|
||||
"Can not find column to sort by",
|
||||
"invalid column",
|
||||
sort_arg.borrow_spanned().span,
|
||||
));
|
||||
return;
|
||||
}
|
||||
match &vec[0] {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(_),
|
||||
..
|
||||
} => {
|
||||
vec.sort();
|
||||
}
|
||||
|
||||
match &vec[0] {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(_),
|
||||
..
|
||||
} => {
|
||||
vec.sort();
|
||||
},
|
||||
_ => {
|
||||
let calc_key = |item: &Value| {
|
||||
rest.iter()
|
||||
.map(|f| get_data_by_key(item, f.borrow_spanned()))
|
||||
.collect::<Vec<Option<Value>>>()
|
||||
};
|
||||
vec.sort_by_cached_key(calc_key);
|
||||
},
|
||||
};
|
||||
|
||||
for item in vec {
|
||||
yield item.into();
|
||||
_ => {
|
||||
let calc_key = |item: &Value| {
|
||||
rest.iter()
|
||||
.map(|f| get_data_by_key(item, f.borrow_spanned()))
|
||||
.collect::<Vec<Option<Value>>>()
|
||||
};
|
||||
vec.sort_by_cached_key(calc_key);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
let mut values_vec_deque: VecDeque<Value> = VecDeque::new();
|
||||
|
||||
for item in vec {
|
||||
values_vec_deque.push_back(item);
|
||||
}
|
||||
|
||||
Ok(futures::stream::iter(values_vec_deque).to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
ReturnSuccess, Signature, SpannedTypeName, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
Signature, SpannedTypeName, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
|
||||
@ -36,32 +36,31 @@ impl WholeStreamCommand for SplitBy {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
split_by(args, registry)
|
||||
split_by(args, registry).await
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split_by(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
pub async fn split_by(
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let stream = async_stream! {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (SplitByArgs { column_name }, mut input) = args.process(®istry).await?;
|
||||
let values: Vec<Value> = input.collect().await;
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (SplitByArgs { column_name }, input) = args.process(®istry).await?;
|
||||
let values: Vec<Value> = input.collect().await;
|
||||
|
||||
if values.len() > 1 || values.is_empty() {
|
||||
yield Err(ShellError::labeled_error(
|
||||
"Expected table from pipeline",
|
||||
"requires a table input",
|
||||
column_name.span()
|
||||
))
|
||||
} else {
|
||||
match split(&column_name, &values[0], name) {
|
||||
Ok(split) => yield ReturnSuccess::value(split),
|
||||
Err(err) => yield Err(err),
|
||||
}
|
||||
}
|
||||
};
|
||||
if values.len() > 1 || values.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected table from pipeline",
|
||||
"requires a table input",
|
||||
column_name.span(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
match split(&column_name, &values[0], name) {
|
||||
Ok(split) => Ok(OutputStream::one(split)),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split(
|
||||
|
@ -26,21 +26,24 @@ impl WholeStreamCommand for Uniq {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
uniq(args, registry)
|
||||
uniq(args, registry).await
|
||||
}
|
||||
}
|
||||
|
||||
fn uniq(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let stream = async_stream! {
|
||||
let mut input = args.input;
|
||||
let uniq_values: IndexSet<_> = input.collect().await;
|
||||
async fn uniq(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let input = args.input;
|
||||
let uniq_values: IndexSet<_> = input.collect().await;
|
||||
|
||||
for item in uniq_values.iter().map(|row| ReturnSuccess::value(row.clone())) {
|
||||
yield item;
|
||||
}
|
||||
};
|
||||
let mut values_vec_deque = VecDeque::new();
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
for item in uniq_values
|
||||
.iter()
|
||||
.map(|row| ReturnSuccess::value(row.clone()))
|
||||
{
|
||||
values_vec_deque.push_back(item);
|
||||
}
|
||||
|
||||
Ok(futures::stream::iter(values_vec_deque).to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Reference in New Issue
Block a user