It expansion (#1668)

* First step in it-expansion

* Fix tests

* fix clippy warnings
This commit is contained in:
Jonathan Turner 2020-04-27 14:04:54 +12:00 committed by GitHub
parent 6f2ef05195
commit 8bd3cedce1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1040 additions and 1002 deletions

View File

@ -238,28 +238,28 @@ pub fn create_default_context(
context.add_commands(vec![ context.add_commands(vec![
// System/file operations // System/file operations
whole_stream_command(Pwd), whole_stream_command(Pwd),
per_item_command(Ls), whole_stream_command(Ls),
per_item_command(Du), whole_stream_command(Du),
whole_stream_command(Cd), whole_stream_command(Cd),
per_item_command(Remove), whole_stream_command(Remove),
per_item_command(Open), whole_stream_command(Open),
whole_stream_command(Config), whole_stream_command(Config),
per_item_command(Help), whole_stream_command(Help),
per_item_command(History), whole_stream_command(History),
whole_stream_command(Save), whole_stream_command(Save),
per_item_command(Touch), whole_stream_command(Touch),
per_item_command(Cpy), whole_stream_command(Cpy),
whole_stream_command(Date), whole_stream_command(Date),
per_item_command(Calc), whole_stream_command(Calc),
per_item_command(Mkdir), whole_stream_command(Mkdir),
per_item_command(Move), whole_stream_command(Move),
per_item_command(Kill), whole_stream_command(Kill),
whole_stream_command(Version), whole_stream_command(Version),
whole_stream_command(Clear), whole_stream_command(Clear),
whole_stream_command(What), whole_stream_command(What),
whole_stream_command(Which), whole_stream_command(Which),
whole_stream_command(Debug), whole_stream_command(Debug),
per_item_command(Alias), whole_stream_command(Alias),
// Statistics // Statistics
whole_stream_command(Size), whole_stream_command(Size),
whole_stream_command(Count), whole_stream_command(Count),
@ -269,7 +269,7 @@ pub fn create_default_context(
whole_stream_command(Next), whole_stream_command(Next),
whole_stream_command(Previous), whole_stream_command(Previous),
whole_stream_command(Shells), whole_stream_command(Shells),
per_item_command(Enter), whole_stream_command(Enter),
whole_stream_command(Exit), whole_stream_command(Exit),
// Viewers // Viewers
whole_stream_command(Autoview), whole_stream_command(Autoview),
@ -279,14 +279,14 @@ pub fn create_default_context(
whole_stream_command(SplitRow), whole_stream_command(SplitRow),
whole_stream_command(Lines), whole_stream_command(Lines),
whole_stream_command(Trim), whole_stream_command(Trim),
per_item_command(Echo), whole_stream_command(Echo),
per_item_command(Parse), whole_stream_command(Parse),
// Column manipulation // Column manipulation
whole_stream_command(Reject), whole_stream_command(Reject),
whole_stream_command(Pick), whole_stream_command(Pick),
whole_stream_command(Get), whole_stream_command(Get),
per_item_command(Edit), whole_stream_command(Edit),
per_item_command(Insert), whole_stream_command(Insert),
whole_stream_command(SplitBy), whole_stream_command(SplitBy),
// Row manipulation // Row manipulation
whole_stream_command(Reverse), whole_stream_command(Reverse),
@ -299,16 +299,16 @@ pub fn create_default_context(
whole_stream_command(Skip), whole_stream_command(Skip),
whole_stream_command(Nth), whole_stream_command(Nth),
whole_stream_command(Drop), whole_stream_command(Drop),
per_item_command(Format), whole_stream_command(Format),
per_item_command(Where), whole_stream_command(Where),
whole_stream_command(Compact), whole_stream_command(Compact),
whole_stream_command(Default), whole_stream_command(Default),
whole_stream_command(SkipWhile), whole_stream_command(SkipWhile),
whole_stream_command(Range), whole_stream_command(Range),
whole_stream_command(Rename), whole_stream_command(Rename),
whole_stream_command(Uniq), whole_stream_command(Uniq),
per_item_command(Each), whole_stream_command(Each),
per_item_command(IsEmpty), whole_stream_command(IsEmpty),
// Table manipulation // Table manipulation
whole_stream_command(Shuffle), whole_stream_command(Shuffle),
whole_stream_command(Wrap), whole_stream_command(Wrap),
@ -726,7 +726,7 @@ async fn process_line(
debug!("=== Parsed ==="); debug!("=== Parsed ===");
debug!("{:#?}", result); debug!("{:#?}", result);
let classified_block = nu_parser::classify_block(&result, ctx.registry()); let mut classified_block = nu_parser::classify_block(&result, ctx.registry());
debug!("{:#?}", classified_block); debug!("{:#?}", classified_block);
//println!("{:#?}", pipeline); //println!("{:#?}", pipeline);
@ -845,6 +845,8 @@ async fn process_line(
InputStream::empty() InputStream::empty()
}; };
classified_block.block.expand_it_usage();
match run_block(&classified_block.block, ctx, input_stream, &Scope::empty()).await { match run_block(&classified_block.block, ctx, input_stream, &Scope::empty()).await {
Ok(input) => { Ok(input) => {
// Running a pipeline gives us back a stream that we can then // Running a pipeline gives us back a stream that we can then

View File

@ -116,10 +116,7 @@ pub(crate) mod wrap;
pub(crate) use autoview::Autoview; pub(crate) use autoview::Autoview;
pub(crate) use cd::Cd; pub(crate) use cd::Cd;
pub(crate) use command::{ pub(crate) use command::{whole_stream_command, Command, UnevaluatedCallInfo, WholeStreamCommand};
per_item_command, whole_stream_command, Command, PerItemCommand, UnevaluatedCallInfo,
WholeStreamCommand,
};
pub(crate) use alias::Alias; pub(crate) use alias::Alias;
pub(crate) use append::Append; pub(crate) use append::Append;

View File

@ -1,14 +1,20 @@
use crate::commands::PerItemCommand; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{hir::Block, CommandAction, ReturnSuccess, Signature, SyntaxShape, Value};
CallInfo, CommandAction, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, use nu_source::Tagged;
};
pub struct Alias; pub struct Alias;
impl PerItemCommand for Alias { #[derive(Deserialize)]
pub struct AliasArgs {
pub name: Tagged<String>,
pub args: Vec<Value>,
pub block: Block,
}
impl WholeStreamCommand for Alias {
fn name(&self) -> &str { fn name(&self) -> &str {
"alias" "alias"
} }
@ -26,40 +32,32 @@ impl PerItemCommand for Alias {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let call_info = call_info.clone(); args.process(registry, alias)?.run()
let stream = async_stream! {
match (call_info.args.expect_nth(0)?, call_info.args.expect_nth(1)?, call_info.args.expect_nth(2)?) {
(Value {value: UntaggedValue::Primitive(Primitive::String(name)), .. },
Value { value: UntaggedValue::Table(list), .. },
Value {
value: UntaggedValue::Block(block),
tag
}) => {
let mut args: Vec<String> = vec![];
for item in list.iter() {
if let Ok(string) = item.as_string() {
args.push(format!("${}", string));
} else {
yield Err(ShellError::labeled_error("Expected a string", "expected a string", item.tag()));
}
}
yield ReturnSuccess::action(CommandAction::AddAlias(name.to_string(), args, block.clone()))
}
_ => {
yield Err(ShellError::labeled_error(
"Expected `name [args] {block}",
"needs a name, args, and a block",
call_info.name_tag,
))
}
};
};
Ok(stream.to_output_stream())
} }
} }
pub fn alias(
AliasArgs {
name,
args: list,
block,
}: AliasArgs,
_: RunnableContext,
) -> Result<OutputStream, ShellError> {
let stream = async_stream! {
let mut args: Vec<String> = vec![];
for item in list.iter() {
if let Ok(string) = item.as_string() {
args.push(format!("${}", string));
} else {
yield Err(ShellError::labeled_error("Expected a string", "expected a string", item.tag()));
}
}
yield ReturnSuccess::action(CommandAction::AddAlias(name.to_string(), args, block.clone()))
};
Ok(stream.to_output_stream())
}

View File

@ -1,11 +1,14 @@
use crate::commands::PerItemCommand; use crate::commands::WholeStreamCommand;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, Primitive, ReturnSuccess, UntaggedValue, Value}; use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value};
pub struct Calc; pub struct Calc;
impl PerItemCommand for Calc { #[derive(Deserialize)]
pub struct CalcArgs {}
impl WholeStreamCommand for Calc {
fn name(&self) -> &str { fn name(&self) -> &str {
"calc" "calc"
} }
@ -16,36 +19,37 @@ impl PerItemCommand for Calc {
fn run( fn run(
&self, &self,
_call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
calc(input, raw_args) args.process(registry, calc)?.run()
} }
} }
fn calc(input: Value, args: &RawCommandArgs) -> Result<OutputStream, ShellError> { pub fn calc(
let name_span = &args.call_info.name_tag.span; _: CalcArgs,
RunnableContext { input, name, .. }: RunnableContext,
let output = if let Ok(string) = input.as_string() { ) -> Result<OutputStream, ShellError> {
match parse(&string, &input.tag) { Ok(input
Ok(value) => ReturnSuccess::value(value), .map(move |input| {
Err(err) => Err(ShellError::labeled_error( if let Ok(string) = input.as_string() {
"Calculation error", match parse(&string, &input.tag) {
err, Ok(value) => ReturnSuccess::value(value),
&input.tag.span, Err(err) => Err(ShellError::labeled_error(
)), "Calculation error",
} err,
} else { &input.tag.span,
Err(ShellError::labeled_error( )),
"Expected a string from pipeline", }
"requires string input", } else {
name_span, Err(ShellError::labeled_error(
)) "Expected a string from pipeline",
}; "requires string input",
name.clone(),
Ok(vec![output].into()) ))
}
})
.to_output_stream())
} }
pub fn parse(math_expression: &str, tag: impl Into<Tag>) -> Result<Value, String> { pub fn parse(math_expression: &str, tag: impl Into<Tag>) -> Result<Value, String> {

View File

@ -1,5 +1,4 @@
use crate::commands::classified::expr::run_expression_block; use crate::commands::classified::expr::run_expression_block;
//use crate::commands::classified::external::run_external_command;
use crate::commands::classified::internal::run_internal_command; use crate::commands::classified::internal::run_internal_command;
use crate::context::Context; use crate::context::Context;
use crate::prelude::*; use crate::prelude::*;

View File

@ -1,4 +1,4 @@
use crate::commands::command::per_item_command; use crate::commands::command::whole_stream_command;
use crate::commands::run_alias::AliasCommand; use crate::commands::run_alias::AliasCommand;
use crate::commands::UnevaluatedCallInfo; use crate::commands::UnevaluatedCallInfo;
use crate::prelude::*; use crate::prelude::*;
@ -122,7 +122,7 @@ pub(crate) fn run_internal_command(
} }
CommandAction::AddAlias(name, args, block) => { CommandAction::AddAlias(name, args, block) => {
context.add_commands(vec![ context.add_commands(vec![
per_item_command(AliasCommand::new( whole_stream_command(AliasCommand::new(
name, name,
args, args,
block, block,

View File

@ -49,36 +49,6 @@ impl UnevaluatedCallInfo {
} }
} }
pub trait CallInfoExt {
fn process<'de, T: Deserialize<'de>>(
&self,
shell_manager: &ShellManager,
ctrl_c: Arc<AtomicBool>,
callback: fn(T, &RunnablePerItemContext) -> Result<OutputStream, ShellError>,
) -> Result<RunnablePerItemArgs<T>, ShellError>;
}
impl CallInfoExt for CallInfo {
fn process<'de, T: Deserialize<'de>>(
&self,
shell_manager: &ShellManager,
ctrl_c: Arc<AtomicBool>,
callback: fn(T, &RunnablePerItemContext) -> Result<OutputStream, ShellError>,
) -> Result<RunnablePerItemArgs<T>, ShellError> {
let mut deserializer = ConfigDeserializer::from_call_info(self.clone());
Ok(RunnablePerItemArgs {
args: T::deserialize(&mut deserializer)?,
context: RunnablePerItemContext {
shell_manager: shell_manager.clone(),
name: self.name_tag.clone(),
ctrl_c,
},
callback,
})
}
}
#[derive(Getters)] #[derive(Getters)]
#[get = "pub(crate)"] #[get = "pub(crate)"]
pub struct CommandArgs { pub struct CommandArgs {
@ -227,12 +197,6 @@ impl CommandArgs {
} }
} }
pub struct RunnablePerItemContext {
pub shell_manager: ShellManager,
pub name: Tag,
pub ctrl_c: Arc<AtomicBool>,
}
pub struct RunnableContext { pub struct RunnableContext {
pub input: InputStream, pub input: InputStream,
pub shell_manager: ShellManager, pub shell_manager: ShellManager,
@ -248,18 +212,6 @@ impl RunnableContext {
} }
} }
pub struct RunnablePerItemArgs<T> {
args: T,
context: RunnablePerItemContext,
callback: fn(T, &RunnablePerItemContext) -> Result<OutputStream, ShellError>,
}
impl<T> RunnablePerItemArgs<T> {
pub fn run(self) -> Result<OutputStream, ShellError> {
(self.callback)(self.args, &self.context)
}
}
pub struct RunnableArgs<T, O: ToOutputStream> { pub struct RunnableArgs<T, O: ToOutputStream> {
args: T, args: T,
context: RunnableContext, context: RunnableContext,
@ -626,7 +578,3 @@ impl WholeStreamCommand for FnFilterCommand {
pub fn whole_stream_command(command: impl WholeStreamCommand + 'static) -> Arc<Command> { pub fn whole_stream_command(command: impl WholeStreamCommand + 'static) -> Arc<Command> {
Arc::new(Command::WholeStream(Arc::new(command))) Arc::new(Command::WholeStream(Arc::new(command)))
} }
pub fn per_item_command(command: impl PerItemCommand + 'static) -> Arc<Command> {
Arc::new(Command::PerItem(Arc::new(command)))
}

View File

@ -1,8 +1,8 @@
use crate::commands::command::RunnablePerItemContext; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged; use nu_source::Tagged;
use std::path::PathBuf; use std::path::PathBuf;
@ -15,7 +15,7 @@ pub struct CopyArgs {
pub recursive: Tagged<bool>, pub recursive: Tagged<bool>,
} }
impl PerItemCommand for Cpy { impl WholeStreamCommand for Cpy {
fn name(&self) -> &str { fn name(&self) -> &str {
"cp" "cp"
} }
@ -37,18 +37,14 @@ impl PerItemCommand for Cpy {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info args.process(registry, cp)?.run()
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), cp)?
.run()
} }
} }
fn cp(args: CopyArgs, context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> { pub fn cp(args: CopyArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
let shell_manager = context.shell_manager.clone(); let shell_manager = context.shell_manager.clone();
shell_manager.cp(args, context) shell_manager.cp(args, &context)
} }

View File

@ -1,12 +1,10 @@
extern crate filesize; use crate::commands::WholeStreamCommand;
use crate::commands::command::RunnablePerItemContext;
use crate::prelude::*; use crate::prelude::*;
use filesize::file_real_size_fast; use filesize::file_real_size_fast;
use glob::*; use glob::*;
use indexmap::map::IndexMap; use indexmap::map::IndexMap;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged; use nu_source::Tagged;
use std::path::PathBuf; use std::path::PathBuf;
@ -31,7 +29,7 @@ pub struct DuArgs {
min_size: Option<Tagged<u64>>, min_size: Option<Tagged<u64>>,
} }
impl PerItemCommand for Du { impl WholeStreamCommand for Du {
fn name(&self) -> &str { fn name(&self) -> &str {
NAME NAME
} }
@ -75,18 +73,14 @@ impl PerItemCommand for Du {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info args.process(registry, du)?.run()
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), du)?
.run()
} }
} }
fn du(args: DuArgs, ctx: &RunnablePerItemContext) -> Result<OutputStream, ShellError> { fn du(args: DuArgs, ctx: RunnableContext) -> Result<OutputStream, ShellError> {
let tag = ctx.name.clone(); let tag = ctx.name.clone();
let exclude = args.exclude.map_or(Ok(None), move |x| { let exclude = args.exclude.map_or(Ok(None), move |x| {
@ -117,7 +111,7 @@ fn du(args: DuArgs, ctx: &RunnablePerItemContext) -> Result<OutputStream, ShellE
}) })
.map(|v| v.map_err(glob_err_into)); .map(|v| v.map_err(glob_err_into));
let ctrl_c = ctx.ctrl_c.clone(); let ctrl_c = ctx.ctrl_c;
let all = args.all; let all = args.all;
let deref = args.deref; let deref = args.deref;
let max_depth = args.max_depth.map(|f| f.item); let max_depth = args.max_depth.map(|f| f.item);

View File

@ -1,16 +1,21 @@
use crate::commands::classified::block::run_block; use crate::commands::classified::block::run_block;
use crate::commands::PerItemCommand; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use futures::stream::once; use futures::stream::once;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{hir::Block, ReturnSuccess, Scope, Signature, SyntaxShape};
pub struct Each; pub struct Each;
impl PerItemCommand for Each { #[derive(Deserialize)]
pub struct EachArgs {
block: Block,
}
impl WholeStreamCommand for Each {
fn name(&self) -> &str { fn name(&self) -> &str {
"each" "each"
} }
@ -29,58 +34,51 @@ impl PerItemCommand for Each {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let call_info = call_info.clone(); Ok(args.process_raw(registry, each)?.run())
let registry = registry.clone();
let raw_args = raw_args.clone();
let stream = async_stream! {
match call_info.args.expect_nth(0)? {
Value {
value: UntaggedValue::Block(block),
tag
} => {
let mut context = Context::from_raw(&raw_args, &registry);
let input_clone = input.clone();
let input_stream = once(async { Ok(input) }).to_input_stream();
let result = run_block(
block,
&mut context,
input_stream,
&Scope::new(input_clone),
).await;
match result {
Ok(mut stream) => {
let errors = context.get_errors();
if let Some(error) = errors.first() {
yield Err(error.clone());
return;
}
while let Some(result) = stream.next().await {
yield Ok(ReturnSuccess::Value(result));
}
}
Err(e) => {
yield Err(e);
}
}
}
Value { tag, .. } => {
yield Err(ShellError::labeled_error(
"Expected a block",
"each needs a block",
tag,
))
}
};
};
Ok(stream.to_output_stream())
} }
} }
fn each(
each_args: EachArgs,
context: RunnableContext,
raw_args: RawCommandArgs,
) -> Result<OutputStream, ShellError> {
let block = each_args.block;
let registry = context.registry.clone();
let mut input_stream = context.input;
let stream = async_stream! {
while let Some(input) = input_stream.next().await {
let mut context = Context::from_raw(&raw_args, &registry);
let input_clone = input.clone();
let input_stream = once(async { Ok(input) }).to_input_stream();
let result = run_block(
&block,
&mut context,
input_stream,
&Scope::new(input_clone),
).await;
match result {
Ok(mut stream) => {
let errors = context.get_errors();
if let Some(error) = errors.first() {
yield Err(error.clone());
}
while let Some(result) = stream.next().await {
yield Ok(ReturnSuccess::Value(result));
}
}
Err(e) => {
yield Err(e);
}
}
}
};
Ok(stream.to_output_stream())
}

View File

@ -1,10 +1,16 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
pub struct Echo; pub struct Echo;
impl PerItemCommand for Echo { #[derive(Deserialize)]
pub struct EchoArgs {
pub rest: Vec<Value>,
}
impl WholeStreamCommand for Echo {
fn name(&self) -> &str { fn name(&self) -> &str {
"echo" "echo"
} }
@ -19,44 +25,36 @@ impl PerItemCommand for Echo {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
run(call_info, registry, raw_args) args.process(registry, echo)?.run()
} }
} }
fn run( fn echo(args: EchoArgs, _: RunnableContext) -> Result<OutputStream, ShellError> {
call_info: &CallInfo,
_registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
) -> Result<OutputStream, ShellError> {
let mut output = vec![]; let mut output = vec![];
if let Some(ref positional) = call_info.args.positional { for i in args.rest {
for i in positional { match i.as_string() {
match i.as_string() { Ok(s) => {
Ok(s) => { output.push(Ok(ReturnSuccess::Value(
output.push(Ok(ReturnSuccess::Value( UntaggedValue::string(s).into_value(i.tag.clone()),
UntaggedValue::string(s).into_value(i.tag.clone()), )));
)));
}
_ => match i {
Value {
value: UntaggedValue::Table(table),
..
} => {
for value in table {
output.push(Ok(ReturnSuccess::Value(value.clone())));
}
}
_ => {
output.push(Ok(ReturnSuccess::Value(i.clone())));
}
},
} }
_ => match i {
Value {
value: UntaggedValue::Table(table),
..
} => {
for value in table {
output.push(Ok(ReturnSuccess::Value(value.clone())));
}
}
_ => {
output.push(Ok(ReturnSuccess::Value(i.clone())));
}
},
} }
} }

View File

@ -1,13 +1,19 @@
use crate::commands::PerItemCommand; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_value_ext::ValueExt; use nu_value_ext::ValueExt;
pub struct Edit; pub struct Edit;
impl PerItemCommand for Edit { #[derive(Deserialize)]
pub struct EditArgs {
field: ColumnPath,
replacement: Value,
}
impl WholeStreamCommand for Edit {
fn name(&self) -> &str { fn name(&self) -> &str {
"edit" "edit"
} }
@ -15,13 +21,13 @@ impl PerItemCommand for Edit {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("edit") Signature::build("edit")
.required( .required(
"Field", "field",
SyntaxShape::ColumnPath, SyntaxShape::ColumnPath,
"the name of the column to edit", "the name of the column to edit",
) )
.required( .required(
"Value", "replacement value",
SyntaxShape::String, SyntaxShape::Any,
"the new value to give the cell(s)", "the new value to give the cell(s)",
) )
} }
@ -32,41 +38,45 @@ impl PerItemCommand for Edit {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
value: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let value_tag = value.tag(); args.process(registry, edit)?.run()
let field = call_info.args.expect_nth(0)?.as_column_path()?; }
let replacement = call_info.args.expect_nth(1)?.tagged_unknown(); }
let stream = match value { fn edit(
obj EditArgs { field, replacement }: EditArgs,
@ RunnableContext { input, .. }: RunnableContext,
Value { ) -> Result<OutputStream, ShellError> {
let mut input = input;
let stream = async_stream! {
match input.next().await {
Some(obj @ Value {
value: UntaggedValue::Row(_), value: UntaggedValue::Row(_),
.. ..
} => match obj.replace_data_at_column_path(&field, replacement.item.clone()) { }) => match obj.replace_data_at_column_path(&field, replacement.clone()) {
Some(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]), Some(v) => yield Ok(ReturnSuccess::Value(v)),
None => { None => {
return Err(ShellError::labeled_error( yield Err(ShellError::labeled_error(
"edit could not find place to insert column", "edit could not find place to insert column",
"column name", "column name",
&field.tag, obj.tag,
)) ))
} }
}, },
_ => { Some(Value { tag, ..}) => {
return Err(ShellError::labeled_error( yield Err(ShellError::labeled_error(
"Unrecognized type in stream", "Unrecognized type in stream",
"original value", "original value",
value_tag, tag,
)) ))
} }
}; _ => {}
}
};
Ok(stream.to_output_stream()) Ok(stream.to_output_stream())
}
} }

View File

@ -1,15 +1,22 @@
use crate::commands::PerItemCommand;
use crate::commands::UnevaluatedCallInfo; use crate::commands::UnevaluatedCallInfo;
use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{
CallInfo, CommandAction, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, CommandAction, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
}; };
use nu_source::Tagged;
use std::path::PathBuf;
pub struct Enter; pub struct Enter;
impl PerItemCommand for Enter { #[derive(Deserialize)]
pub struct EnterArgs {
location: Tagged<PathBuf>,
}
impl WholeStreamCommand for Enter {
fn name(&self) -> &str { fn name(&self) -> &str {
"enter" "enter"
} }
@ -28,130 +35,121 @@ impl PerItemCommand for Enter {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); Ok(args.process_raw(registry, enter)?.run())
let raw_args = raw_args.clone(); }
match call_info.args.expect_nth(0)? { }
Value {
value: UntaggedValue::Primitive(Primitive::Path(location)), fn enter(
tag, EnterArgs { location }: EnterArgs,
.. RunnableContext {
} => { registry,
let location_string = location.display().to_string(); name: tag,
let location_clone = location_string.clone(); ..
let tag_clone = tag.clone(); }: RunnableContext,
raw_args: RawCommandArgs,
if location_string.starts_with("help") { ) -> Result<OutputStream, ShellError> {
let spec = location_string.split(':').collect::<Vec<&str>>(); let location_string = location.display().to_string();
let location_clone = location_string.clone();
if spec.len() == 2 {
let (_, command) = (spec[0], spec[1]); if location_string.starts_with("help") {
let spec = location_string.split(':').collect::<Vec<&str>>();
if registry.has(command) {
return Ok(vec![Ok(ReturnSuccess::Action( if spec.len() == 2 {
CommandAction::EnterHelpShell( let (_, command) = (spec[0], spec[1]);
UntaggedValue::string(command).into_value(Tag::unknown()),
), if registry.has(command) {
))] return Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell(
.into()); UntaggedValue::string(command).into_value(Tag::unknown()),
} )))]
} .into());
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( }
UntaggedValue::nothing().into_value(Tag::unknown()), }
)))] Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell(
.into()) UntaggedValue::nothing().into_value(Tag::unknown()),
} else if location.is_dir() { )))]
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell( .into())
location_clone, } else if location.is_dir() {
)))] Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell(
.into()) location_clone,
} else { )))]
let stream = async_stream! { .into())
// If it's a file, attempt to open the file as a value and enter it } else {
let cwd = raw_args.shell_manager.path(); let stream = async_stream! {
// If it's a file, attempt to open the file as a value and enter it
let full_path = std::path::PathBuf::from(cwd); let cwd = raw_args.shell_manager.path();
let (file_extension, contents, contents_tag) = let full_path = std::path::PathBuf::from(cwd);
crate::commands::open::fetch(
&full_path, let (file_extension, contents, contents_tag) =
&location_clone, crate::commands::open::fetch(
tag_clone.span, &full_path,
).await?; &PathBuf::from(location_clone),
tag.span,
match contents { ).await?;
UntaggedValue::Primitive(Primitive::String(_)) => {
let tagged_contents = contents.into_value(&contents_tag); match contents {
UntaggedValue::Primitive(Primitive::String(_)) => {
if let Some(extension) = file_extension { let tagged_contents = contents.into_value(&contents_tag);
let command_name = format!("from-{}", extension);
if let Some(converter) = if let Some(extension) = file_extension {
registry.get_command(&command_name) let command_name = format!("from-{}", extension);
{ if let Some(converter) =
let new_args = RawCommandArgs { registry.get_command(&command_name)
host: raw_args.host, {
ctrl_c: raw_args.ctrl_c, let new_args = RawCommandArgs {
shell_manager: raw_args.shell_manager, host: raw_args.host,
call_info: UnevaluatedCallInfo { ctrl_c: raw_args.ctrl_c,
args: nu_protocol::hir::Call { shell_manager: raw_args.shell_manager,
head: raw_args.call_info.args.head, call_info: UnevaluatedCallInfo {
positional: None, args: nu_protocol::hir::Call {
named: None, head: raw_args.call_info.args.head,
span: Span::unknown(), positional: None,
is_last: false, named: None,
}, span: Span::unknown(),
name_tag: raw_args.call_info.name_tag, is_last: false,
scope: raw_args.call_info.scope.clone() },
}, name_tag: raw_args.call_info.name_tag,
}; scope: raw_args.call_info.scope.clone()
let mut result = converter.run( },
new_args.with_input(vec![tagged_contents]), };
&registry, let mut result = converter.run(
); new_args.with_input(vec![tagged_contents]),
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = &registry,
result.drain_vec().await; );
for res in result_vec { let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
match res { result.drain_vec().await;
Ok(ReturnSuccess::Value(Value { for res in result_vec {
value, match res {
.. Ok(ReturnSuccess::Value(Value {
})) => { value,
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell( ..
Value { })) => {
value, yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
tag: contents_tag.clone(), Value {
}))); value,
} tag: contents_tag.clone(),
x => yield x, })));
} }
} x => yield x,
} else { }
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents))); }
} } else {
} else { yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents))); }
} } else {
} yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
_ => { }
let tagged_contents = contents.into_value(contents_tag); }
_ => {
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents))); let tagged_contents = contents.into_value(contents_tag);
}
} yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
}; }
Ok(stream.to_output_stream()) }
} };
} Ok(stream.to_output_stream())
x => Ok(
vec![Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
x.clone(),
)))]
.into(),
),
}
} }
} }

View File

@ -1,17 +1,20 @@
use crate::commands::PerItemCommand; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
CallInfo, ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged; use nu_source::Tagged;
use nu_value_ext::{as_column_path, get_data_by_column_path}; use nu_value_ext::{as_column_path, get_data_by_column_path};
use std::borrow::Borrow; use std::borrow::Borrow;
pub struct Format; pub struct Format;
impl PerItemCommand for Format { #[derive(Deserialize)]
pub struct FormatArgs {
pattern: Tagged<String>,
}
impl WholeStreamCommand for Format {
fn name(&self) -> &str { fn name(&self) -> &str {
"format" "format"
} }
@ -19,7 +22,7 @@ impl PerItemCommand for Format {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("format").required( Signature::build("format").required(
"pattern", "pattern",
SyntaxShape::Any, SyntaxShape::String,
"the pattern to output. Eg) \"{foo}: {bar}\"", "the pattern to output. Eg) \"{foo}: {bar}\"",
) )
} }
@ -30,61 +33,67 @@ impl PerItemCommand for Format {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
value: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
//let value_tag = value.tag(); args.process(registry, format_command)?.run()
let pattern = call_info.args.expect_nth(0)?; }
let pattern_tag = pattern.tag.clone(); }
let pattern = pattern.as_string()?;
let format_pattern = format(&pattern); fn format_command(
let commands = format_pattern; FormatArgs { pattern }: FormatArgs,
RunnableContext { input, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let pattern_tag = pattern.tag.clone();
let output = match value { let format_pattern = format(&pattern);
value let commands = format_pattern;
@ let mut input = input;
Value {
value: UntaggedValue::Row(_),
..
} => {
let mut output = String::new();
for command in &commands { let stream = async_stream! {
match command { while let Some(value) = input.next().await {
FormatCommand::Text(s) => { match value {
output.push_str(&s); value
} @
FormatCommand::Column(c) => { Value {
let key = to_column_path(&c, &pattern_tag)?; value: UntaggedValue::Row(_),
..
} => {
let mut output = String::new();
let fetcher = get_data_by_column_path( for command in &commands {
&value, match command {
&key, FormatCommand::Text(s) => {
Box::new(move |(_, _, error)| error), output.push_str(&s);
); }
FormatCommand::Column(c) => {
if let Ok(c) = fetcher { let key = to_column_path(&c, &pattern_tag)?;
output
.push_str(&value::format_leaf(c.borrow()).plain_string(100_000)) let fetcher = get_data_by_column_path(
&value,
&key,
Box::new(move |(_, _, error)| error),
);
if let Ok(c) = fetcher {
output
.push_str(&value::format_leaf(c.borrow()).plain_string(100_000))
}
// That column doesn't match, so don't emit anything
} }
// That column doesn't match, so don't emit anything
} }
} }
yield ReturnSuccess::value(
UntaggedValue::string(output).into_untagged_value())
} }
_ => yield ReturnSuccess::value(
UntaggedValue::string(String::new()).into_untagged_value()),
};
}
};
output Ok(stream.to_output_stream())
}
_ => String::new(),
};
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::string(output).into_untagged_value(),
)])
.to_output_stream())
}
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -1,24 +1,33 @@
use crate::commands::PerItemCommand; use crate::commands::WholeStreamCommand;
use crate::data::command_dict; use crate::data::command_dict;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{
CallInfo, NamedType, PositionalType, Primitive, ReturnSuccess, Signature, SyntaxShape, NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder,
TaggedDictBuilder, UntaggedValue, Value, UntaggedValue,
}; };
use nu_source::SpannedItem; use nu_source::{SpannedItem, Tagged};
use nu_value_ext::get_data_by_key; use nu_value_ext::get_data_by_key;
pub struct Help; pub struct Help;
impl PerItemCommand for Help { #[derive(Deserialize)]
pub struct HelpArgs {
command: Option<Tagged<String>>,
}
impl WholeStreamCommand for Help {
fn name(&self) -> &str { fn name(&self) -> &str {
"help" "help"
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("help").rest(SyntaxShape::Any, "the name of command(s) to get help on") Signature::build("help").optional(
"command",
SyntaxShape::String,
"the name of command(s) to get help on",
)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -27,67 +36,65 @@ impl PerItemCommand for Help {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let tag = &call_info.name_tag; args.process(registry, help)?.run()
}
}
match call_info.args.nth(0) { fn help(
Some(Value { HelpArgs { command }: HelpArgs,
value: UntaggedValue::Primitive(Primitive::String(document)), RunnableContext { registry, name, .. }: RunnableContext,
tag, ) -> Result<OutputStream, ShellError> {
}) => { if let Some(document) = command {
let mut help = VecDeque::new(); let mut help = VecDeque::new();
if document == "commands" { if document.item == "commands" {
let mut sorted_names = registry.names(); let mut sorted_names = registry.names();
sorted_names.sort(); sorted_names.sort();
for cmd in sorted_names { for cmd in sorted_names {
let mut short_desc = TaggedDictBuilder::new(tag.clone()); let mut short_desc = TaggedDictBuilder::new(name.clone());
let value = command_dict( let document_tag = document.tag.clone();
registry.get_command(&cmd).ok_or_else(|| { let value = command_dict(
ShellError::labeled_error( registry.get_command(&cmd).ok_or_else(|| {
format!("Could not load {}", cmd), ShellError::labeled_error(
"could not load command", format!("Could not load {}", cmd),
tag, "could not load command",
) document_tag,
})?, )
tag.clone(), })?,
); name.clone(),
);
short_desc.insert_untagged("name", cmd); short_desc.insert_untagged("name", cmd);
short_desc.insert_untagged( short_desc.insert_untagged(
"description", "description",
get_data_by_key(&value, "usage".spanned_unknown()) get_data_by_key(&value, "usage".spanned_unknown())
.ok_or_else(|| { .ok_or_else(|| {
ShellError::labeled_error( ShellError::labeled_error(
"Expected a usage key", "Expected a usage key",
"expected a 'usage' key", "expected a 'usage' key",
&value.tag, &value.tag,
) )
})? })?
.as_string()?, .as_string()?,
); );
help.push_back(ReturnSuccess::value(short_desc.into_value())); help.push_back(ReturnSuccess::value(short_desc.into_value()));
}
} else if let Some(command) = registry.get_command(document) {
return Ok(
get_help(&command.name(), &command.usage(), command.signature()).into(),
);
} else {
return Err(ShellError::labeled_error(
"Can't find command (use 'help commands' for full list)",
"can't find command",
tag,
));
}
let help = futures::stream::iter(help);
Ok(help.to_output_stream())
} }
_ => { } else if let Some(command) = registry.get_command(&document.item) {
let msg = r#"Welcome to Nushell. return Ok(get_help(&command.name(), &command.usage(), command.signature()).into());
} else {
return Err(ShellError::labeled_error(
"Can't find command (use 'help commands' for full list)",
"can't find command",
document.tag,
));
}
let help = futures::stream::iter(help);
Ok(help.to_output_stream())
} else {
let msg = r#"Welcome to Nushell.
Here are some tips to help you get started. Here are some tips to help you get started.
* help commands - list all available commands * help commands - list all available commands
@ -109,13 +116,11 @@ Get the processes on your system actively using CPU:
You can also learn more at https://www.nushell.sh/book/"#; You can also learn more at https://www.nushell.sh/book/"#;
let output_stream = futures::stream::iter(vec![ReturnSuccess::value( let output_stream = futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::string(msg).into_value(tag), UntaggedValue::string(msg).into_value(name),
)]); )]);
Ok(output_stream.to_output_stream()) Ok(output_stream.to_output_stream())
}
}
} }
} }

View File

@ -1,14 +1,17 @@
use crate::cli::History as HistoryFile; use crate::cli::History as HistoryFile;
use crate::commands::PerItemCommand; use crate::commands::WholeStreamCommand;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, ReturnSuccess, Signature, UntaggedValue, Value}; use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
pub struct History; pub struct History;
impl PerItemCommand for History { #[derive(Deserialize)]
pub struct HistoryArgs;
impl WholeStreamCommand for History {
fn name(&self) -> &str { fn name(&self) -> &str {
"history" "history"
} }
@ -23,27 +26,30 @@ impl PerItemCommand for History {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let tag = call_info.name_tag.clone(); args.process(registry, history)?.run()
let stream = async_stream! {
let history_path = HistoryFile::path();
let file = File::open(history_path);
if let Ok(file) = file {
let reader = BufReader::new(file);
for line in reader.lines() {
if let Ok(line) = line {
yield ReturnSuccess::value(UntaggedValue::string(line).into_value(tag.clone()));
}
}
} else {
yield Err(ShellError::labeled_error("Could not open history", "history file could not be opened", tag.clone()));
}
};
Ok(stream.to_output_stream())
} }
} }
fn history(
_: HistoryArgs,
RunnableContext { name: tag, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let stream = async_stream! {
let history_path = HistoryFile::path();
let file = File::open(history_path);
if let Ok(file) = file {
let reader = BufReader::new(file);
for line in reader.lines() {
if let Ok(line) = line {
yield ReturnSuccess::value(UntaggedValue::string(line).into_value(tag.clone()));
}
}
} else {
yield Err(ShellError::labeled_error("Could not open history", "history file could not be opened", tag.clone()));
}
};
Ok(stream.to_output_stream())
}

View File

@ -1,13 +1,19 @@
use crate::commands::PerItemCommand; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_value_ext::ValueExt; use nu_value_ext::ValueExt;
pub struct Insert; pub struct Insert;
impl PerItemCommand for Insert { #[derive(Deserialize)]
pub struct InsertArgs {
column: ColumnPath,
value: Value,
}
impl WholeStreamCommand for Insert {
fn name(&self) -> &str { fn name(&self) -> &str {
"insert" "insert"
} }
@ -27,40 +33,45 @@ impl PerItemCommand for Insert {
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Edit an existing column to have a new value." "Insert a new column with a given value."
} }
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
value: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let value_tag = value.tag(); args.process(registry, insert)?.run()
let field = call_info.args.expect_nth(0)?.as_column_path()?;
let replacement = call_info.args.expect_nth(1)?.tagged_unknown();
let stream = match value {
obj
@
Value {
value: UntaggedValue::Row(_),
..
} => match obj.insert_data_at_column_path(&field, replacement.item.clone()) {
Ok(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]),
Err(err) => return Err(err),
},
_ => {
return Err(ShellError::labeled_error(
"Unrecognized type in stream",
"original value",
value_tag,
))
}
};
Ok(stream.to_output_stream())
} }
} }
fn insert(
InsertArgs { column, value }: InsertArgs,
RunnableContext { input, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let mut input = input;
let stream = async_stream! {
match input.next().await {
Some(obj @ Value {
value: UntaggedValue::Row(_),
..
}) => match obj.insert_data_at_column_path(&column, value.clone()) {
Ok(v) => yield Ok(ReturnSuccess::Value(v)),
Err(err) => yield Err(err),
},
Some(Value { tag, ..}) => {
yield Err(ShellError::labeled_error(
"Unrecognized type in stream",
"original value",
tag,
));
}
None => {}
};
};
Ok(stream.to_output_stream())
}

View File

@ -1,10 +1,8 @@
use crate::commands::PerItemCommand; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
CallInfo, ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged; use nu_source::Tagged;
use nu_value_ext::ValueExt; use nu_value_ext::ValueExt;
@ -17,7 +15,12 @@ enum IsEmptyFor {
pub struct IsEmpty; pub struct IsEmpty;
impl PerItemCommand for IsEmpty { #[derive(Deserialize)]
pub struct IsEmptyArgs {
rest: Vec<Value>,
}
impl WholeStreamCommand for IsEmpty {
fn name(&self) -> &str { fn name(&self) -> &str {
"empty?" "empty?"
} }
@ -35,61 +38,100 @@ impl PerItemCommand for IsEmpty {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
value: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let value_tag = value.tag(); args.process(registry, is_empty)?.run()
}
}
let action = if call_info.args.len() <= 2 { fn is_empty(
let field = call_info.args.expect_nth(0); IsEmptyArgs { rest }: IsEmptyArgs,
let replacement_if_true = call_info.args.expect_nth(1); RunnableContext { input, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
Ok(input
.map(move |value| {
let value_tag = value.tag();
match (field, replacement_if_true) { let action = if rest.len() <= 2 {
(Ok(field), Ok(replacement_if_true)) => IsEmptyFor::RowWithFieldAndFallback( let field = rest.get(0);
Box::new(field.as_column_path()?), let replacement_if_true = rest.get(1);
replacement_if_true.clone(),
), match (field, replacement_if_true) {
(Ok(field), Err(_)) => IsEmptyFor::RowWithField(field.as_column_path()?), (Some(field), Some(replacement_if_true)) => {
(_, _) => IsEmptyFor::Value, IsEmptyFor::RowWithFieldAndFallback(
} Box::new(field.as_column_path()?),
} else { replacement_if_true.clone(),
let no_args = vec![]; )
let mut arguments = call_info }
.args (Some(field), None) => IsEmptyFor::RowWithField(field.as_column_path()?),
.positional (_, _) => IsEmptyFor::Value,
.as_ref() }
.unwrap_or_else(|| &no_args) } else {
.iter() // let no_args = vec![];
.rev(); let mut arguments = rest.iter().rev();
let replacement_if_true = match arguments.next() { let replacement_if_true = match arguments.next() {
Some(arg) => arg.clone(), Some(arg) => arg.clone(),
None => UntaggedValue::boolean(value.is_empty()).into_value(&value_tag), None => UntaggedValue::boolean(value.is_empty()).into_value(&value_tag),
};
IsEmptyFor::RowWithFieldsAndFallback(
arguments
.map(|a| a.as_column_path())
.filter_map(Result::ok)
.collect(),
replacement_if_true,
)
}; };
IsEmptyFor::RowWithFieldsAndFallback( match action {
arguments IsEmptyFor::Value => Ok(ReturnSuccess::Value(
.map(|a| a.as_column_path()) UntaggedValue::boolean(value.is_empty()).into_value(value_tag),
.filter_map(Result::ok) )),
.collect(), IsEmptyFor::RowWithFieldsAndFallback(fields, default) => {
replacement_if_true, let mut out = value;
)
};
match action { for field in fields.iter() {
IsEmptyFor::Value => Ok(futures::stream::iter(vec![Ok(ReturnSuccess::Value( let val =
UntaggedValue::boolean(value.is_empty()).into_value(value_tag), out.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?;
))])
.to_output_stream()),
IsEmptyFor::RowWithFieldsAndFallback(fields, default) => {
let mut out = value;
for field in fields.iter() { let emptiness_value = match out {
obj
@
Value {
value: UntaggedValue::Row(_),
..
} => {
if val.is_empty() {
match obj.replace_data_at_column_path(&field, default.clone()) {
Some(v) => Ok(v),
None => Err(ShellError::labeled_error(
"empty? could not find place to check emptiness",
"column name",
&field.tag,
)),
}
} else {
Ok(obj)
}
}
_ => Err(ShellError::labeled_error(
"Unrecognized type in stream",
"original value",
&value_tag,
)),
};
out = emptiness_value?;
}
Ok(ReturnSuccess::Value(out))
}
IsEmptyFor::RowWithField(field) => {
let val = let val =
out.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?; value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?;
let emptiness_value = match out { match &value {
obj obj
@ @
Value { Value {
@ -97,8 +139,11 @@ impl PerItemCommand for IsEmpty {
.. ..
} => { } => {
if val.is_empty() { if val.is_empty() {
match obj.replace_data_at_column_path(&field, default.clone()) { match obj.replace_data_at_column_path(
Some(v) => Ok(v), &field,
UntaggedValue::boolean(true).into_value(&value_tag),
) {
Some(v) => Ok(ReturnSuccess::Value(v)),
None => Err(ShellError::labeled_error( None => Err(ShellError::labeled_error(
"empty? could not find place to check emptiness", "empty? could not find place to check emptiness",
"column name", "column name",
@ -106,7 +151,7 @@ impl PerItemCommand for IsEmpty {
)), )),
} }
} else { } else {
Ok(obj) Ok(ReturnSuccess::Value(value))
} }
} }
_ => Err(ShellError::labeled_error( _ => Err(ShellError::labeled_error(
@ -114,90 +159,40 @@ impl PerItemCommand for IsEmpty {
"original value", "original value",
&value_tag, &value_tag,
)), )),
}; }
out = emptiness_value?;
} }
IsEmptyFor::RowWithFieldAndFallback(field, default) => {
let val =
value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?;
Ok(futures::stream::iter(vec![Ok(ReturnSuccess::Value(out))]).to_output_stream()) match &value {
} obj
IsEmptyFor::RowWithField(field) => { @
let val = Value {
value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?; value: UntaggedValue::Row(_),
..
let stream = match &value { } => {
obj if val.is_empty() {
@ match obj.replace_data_at_column_path(&field, default) {
Value { Some(v) => Ok(ReturnSuccess::Value(v)),
value: UntaggedValue::Row(_), None => Err(ShellError::labeled_error(
..
} => {
if val.is_empty() {
match obj.replace_data_at_column_path(
&field,
UntaggedValue::boolean(true).into_value(&value_tag),
) {
Some(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]),
None => {
return Err(ShellError::labeled_error(
"empty? could not find place to check emptiness", "empty? could not find place to check emptiness",
"column name", "column name",
&field.tag, &field.tag,
)) )),
} }
} else {
Ok(ReturnSuccess::Value(value))
} }
} else {
futures::stream::iter(vec![Ok(ReturnSuccess::Value(value))])
} }
} _ => Err(ShellError::labeled_error(
_ => {
return Err(ShellError::labeled_error(
"Unrecognized type in stream", "Unrecognized type in stream",
"original value", "original value",
&value_tag, &value_tag,
)) )),
} }
}; }
Ok(stream.to_output_stream())
} }
IsEmptyFor::RowWithFieldAndFallback(field, default) => { })
let val = .to_output_stream())
value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?;
let stream = match &value {
obj
@
Value {
value: UntaggedValue::Row(_),
..
} => {
if val.is_empty() {
match obj.replace_data_at_column_path(&field, default) {
Some(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]),
None => {
return Err(ShellError::labeled_error(
"empty? could not find place to check emptiness",
"column name",
&field.tag,
))
}
}
} else {
futures::stream::iter(vec![Ok(ReturnSuccess::Value(value))])
}
}
_ => {
return Err(ShellError::labeled_error(
"Unrecognized type in stream",
"original value",
&value_tag,
))
}
};
Ok(stream.to_output_stream())
}
}
}
} }

View File

@ -1,8 +1,8 @@
use crate::commands::command::RunnablePerItemContext; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged; use nu_source::Tagged;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
@ -16,7 +16,7 @@ pub struct KillArgs {
pub quiet: Tagged<bool>, pub quiet: Tagged<bool>,
} }
impl PerItemCommand for Kill { impl WholeStreamCommand for Kill {
fn name(&self) -> &str { fn name(&self) -> &str {
"kill" "kill"
} }
@ -39,14 +39,10 @@ impl PerItemCommand for Kill {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info args.process(registry, kill)?.run()
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), kill)?
.run()
} }
} }
@ -57,7 +53,7 @@ fn kill(
force, force,
quiet, quiet,
}: KillArgs, }: KillArgs,
_context: &RunnablePerItemContext, _context: RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let mut cmd = if cfg!(windows) { let mut cmd = if cfg!(windows) {
let mut cmd = Command::new("taskkill"); let mut cmd = Command::new("taskkill");

View File

@ -1,7 +1,7 @@
use crate::commands::command::RunnablePerItemContext; use crate::commands::WholeStreamCommand;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged; use nu_source::Tagged;
use std::path::PathBuf; use std::path::PathBuf;
@ -18,7 +18,7 @@ pub struct LsArgs {
pub with_symlink_targets: bool, pub with_symlink_targets: bool,
} }
impl PerItemCommand for Ls { impl WholeStreamCommand for Ls {
fn name(&self) -> &str { fn name(&self) -> &str {
"ls" "ls"
} }
@ -54,17 +54,13 @@ impl PerItemCommand for Ls {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info args.process(registry, ls)?.run()
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), ls)?
.run()
} }
} }
fn ls(args: LsArgs, context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> { fn ls(args: LsArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
context.shell_manager.ls(args, context) context.shell_manager.ls(args, &context)
} }

View File

@ -1,8 +1,8 @@
use crate::commands::command::RunnablePerItemContext; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged; use nu_source::Tagged;
use std::path::PathBuf; use std::path::PathBuf;
@ -13,7 +13,7 @@ pub struct MkdirArgs {
pub rest: Vec<Tagged<PathBuf>>, pub rest: Vec<Tagged<PathBuf>>,
} }
impl PerItemCommand for Mkdir { impl WholeStreamCommand for Mkdir {
fn name(&self) -> &str { fn name(&self) -> &str {
"mkdir" "mkdir"
} }
@ -28,18 +28,14 @@ impl PerItemCommand for Mkdir {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info args.process(registry, mkdir)?.run()
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), mkdir)?
.run()
} }
} }
fn mkdir(args: MkdirArgs, context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> { fn mkdir(args: MkdirArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
let shell_manager = context.shell_manager.clone(); let shell_manager = context.shell_manager.clone();
shell_manager.mkdir(args, context) shell_manager.mkdir(args, &context)
} }

View File

@ -1,8 +1,8 @@
use crate::commands::command::RunnablePerItemContext; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged; use nu_source::Tagged;
use std::path::PathBuf; use std::path::PathBuf;
@ -14,7 +14,7 @@ pub struct MoveArgs {
pub dst: Tagged<PathBuf>, pub dst: Tagged<PathBuf>,
} }
impl PerItemCommand for Move { impl WholeStreamCommand for Move {
fn name(&self) -> &str { fn name(&self) -> &str {
"mv" "mv"
} }
@ -39,18 +39,14 @@ impl PerItemCommand for Move {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info args.process(registry, mv)?.run()
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), mv)?
.run()
} }
} }
fn mv(args: MoveArgs, context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> { fn mv(args: MoveArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
let shell_manager = context.shell_manager.clone(); let shell_manager = context.shell_manager.clone();
shell_manager.mv(args, context) shell_manager.mv(args, &context)
} }

View File

@ -1,14 +1,19 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
CallInfo, CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, use nu_source::{AnchorLocation, Span, Tagged};
};
use nu_source::{AnchorLocation, Span};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
pub struct Open; pub struct Open;
impl PerItemCommand for Open { #[derive(Deserialize)]
pub struct OpenArgs {
path: Tagged<PathBuf>,
raw: Tagged<bool>,
}
impl WholeStreamCommand for Open {
fn name(&self) -> &str { fn name(&self) -> &str {
"open" "open"
} }
@ -33,36 +38,23 @@ impl PerItemCommand for Open {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
run(call_info, raw_args) args.process(registry, open)?.run()
} }
} }
fn run(call_info: &CallInfo, raw_args: &RawCommandArgs) -> Result<OutputStream, ShellError> { fn open(
let shell_manager = &raw_args.shell_manager; OpenArgs { path, raw }: OpenArgs,
RunnableContext { shell_manager, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let cwd = PathBuf::from(shell_manager.path()); let cwd = PathBuf::from(shell_manager.path());
let full_path = cwd; let full_path = cwd;
let path = call_info.args.nth(0).ok_or_else(|| {
ShellError::labeled_error(
"No file or directory specified",
"for command",
&call_info.name_tag,
)
})?;
let path_buf = path.as_path()?;
let path_str = path_buf.display().to_string();
let path_span = path.tag.span;
let has_raw = call_info.args.has("raw");
let stream = async_stream! { let stream = async_stream! {
let result = fetch(&full_path, &path_str, path_span).await; let result = fetch(&full_path, &path.item, path.tag.span).await;
if let Err(e) = result { if let Err(e) = result {
yield Err(e); yield Err(e);
@ -70,12 +62,12 @@ fn run(call_info: &CallInfo, raw_args: &RawCommandArgs) -> Result<OutputStream,
} }
let (file_extension, contents, contents_tag) = result?; let (file_extension, contents, contents_tag) = result?;
let file_extension = if has_raw { let file_extension = if raw.item {
None None
} else { } else {
// If the extension could not be determined via mimetype, try to use the path // If the extension could not be determined via mimetype, try to use the path
// extension. Some file types do not declare their mimetypes (such as bson files). // extension. Some file types do not declare their mimetypes (such as bson files).
file_extension.or(path_str.split('.').last().map(String::from)) file_extension.or(path.extension().map(|x| x.to_string_lossy().to_string()))
}; };
let tagged_contents = contents.into_value(&contents_tag); let tagged_contents = contents.into_value(&contents_tag);
@ -92,7 +84,7 @@ fn run(call_info: &CallInfo, raw_args: &RawCommandArgs) -> Result<OutputStream,
pub async fn fetch( pub async fn fetch(
cwd: &PathBuf, cwd: &PathBuf,
location: &str, location: &PathBuf,
span: Span, span: Span,
) -> Result<(Option<String>, UntaggedValue, Tag), ShellError> { ) -> Result<(Option<String>, UntaggedValue, Tag), ShellError> {
let mut cwd = cwd.clone(); let mut cwd = cwd.clone();

View File

@ -1,11 +1,9 @@
use crate::commands::PerItemCommand; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
CallInfo, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value, use nu_source::Tagged;
};
use regex::Regex; use regex::Regex;
#[derive(Debug)] #[derive(Debug)]
@ -84,7 +82,12 @@ fn build_regex(commands: &[ParseCommand]) -> String {
} }
pub struct Parse; pub struct Parse;
impl PerItemCommand for Parse { #[derive(Deserialize)]
pub struct ParseArgs {
pattern: Tagged<String>,
}
impl WholeStreamCommand for Parse {
fn name(&self) -> &str { fn name(&self) -> &str {
"parse" "parse"
} }
@ -92,7 +95,7 @@ impl PerItemCommand for Parse {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("parse").required( Signature::build("parse").required(
"pattern", "pattern",
SyntaxShape::Any, SyntaxShape::String,
"the pattern to match. Eg) \"{foo}: {bar}\"", "the pattern to match. Eg) \"{foo}: {bar}\"",
) )
} }
@ -103,41 +106,55 @@ impl PerItemCommand for Parse {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
value: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
//let value_tag = value.tag(); args.process(registry, parse_command)?.run()
let pattern = call_info.args.expect_nth(0)?.as_string()?;
let parse_pattern = parse(&pattern);
let parse_regex = build_regex(&parse_pattern);
let column_names = column_names(&parse_pattern);
let regex = Regex::new(&parse_regex).map_err(|_| {
ShellError::labeled_error("Could not parse regex", "could not parse regex", &value.tag)
})?;
let output = if let Ok(s) = value.as_string() {
let mut results = vec![];
for cap in regex.captures_iter(&s) {
let mut dict = TaggedDictBuilder::new(value.tag());
for (idx, column_name) in column_names.iter().enumerate() {
dict.insert_untagged(
column_name,
UntaggedValue::string(&cap[idx + 1].to_string()),
);
}
results.push(ReturnSuccess::value(dict.into_value()));
}
VecDeque::from(results)
} else {
VecDeque::new()
};
Ok(output.into())
} }
} }
fn parse_command(
ParseArgs { pattern }: ParseArgs,
RunnableContext { name, input, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let parse_pattern = parse(&pattern.item);
let parse_regex = build_regex(&parse_pattern);
let column_names = column_names(&parse_pattern);
let name = name.span;
let regex = Regex::new(&parse_regex).map_err(|_| {
ShellError::labeled_error(
"Could not parse regex",
"could not parse regex",
&pattern.tag,
)
})?;
Ok(input
.map(move |value| {
if let Ok(s) = value.as_string() {
let mut output = vec![];
for cap in regex.captures_iter(&s) {
let mut dict = TaggedDictBuilder::new(value.tag());
for (idx, column_name) in column_names.iter().enumerate() {
dict.insert_untagged(
column_name,
UntaggedValue::string(cap[idx + 1].to_string()),
);
}
output.push(Ok(ReturnSuccess::Value(dict.into_value())));
}
output
} else {
vec![Err(ShellError::labeled_error_with_secondary(
"Expected string input",
"expected string input",
name,
"value originated here",
value.tag,
))]
}
})
.map(futures::stream::iter)
.flatten()
.to_output_stream())
}

View File

@ -1,8 +1,8 @@
use crate::commands::command::RunnablePerItemContext; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged; use nu_source::Tagged;
use std::path::PathBuf; use std::path::PathBuf;
@ -16,7 +16,7 @@ pub struct RemoveArgs {
pub trash: Tagged<bool>, pub trash: Tagged<bool>,
} }
impl PerItemCommand for Remove { impl WholeStreamCommand for Remove {
fn name(&self) -> &str { fn name(&self) -> &str {
"rm" "rm"
} }
@ -38,18 +38,14 @@ impl PerItemCommand for Remove {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info args.process(registry, rm)?.run()
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), rm)?
.run()
} }
} }
fn rm(args: RemoveArgs, context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> { fn rm(args: RemoveArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
let shell_manager = context.shell_manager.clone(); let shell_manager = context.shell_manager.clone();
shell_manager.rm(args, context) shell_manager.rm(args, &context)
} }

View File

@ -1,18 +1,19 @@
use crate::commands::classified::block::run_block; use crate::commands::classified::block::run_block;
use crate::commands::WholeStreamCommand;
use crate::prelude::*; use crate::prelude::*;
use derive_new::new; use derive_new::new;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{hir::Block, CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, Value}; use nu_protocol::{hir::Block, ReturnSuccess, Scope, Signature, SyntaxShape};
#[derive(new)] #[derive(new, Clone)]
pub struct AliasCommand { pub struct AliasCommand {
name: String, name: String,
args: Vec<String>, args: Vec<String>,
block: Block, block: Block,
} }
impl PerItemCommand for AliasCommand { impl WholeStreamCommand for AliasCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
&self.name &self.name
} }
@ -33,73 +34,76 @@ impl PerItemCommand for AliasCommand {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let tag = call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let call_info = call_info.clone(); let call_info = args.call_info.clone();
let registry = registry.clone(); let registry = registry.clone();
let raw_args = raw_args.clone();
let block = self.block.clone(); let block = self.block.clone();
let alias_command = self.clone();
let mut scope = Scope::it_value(input.clone()); let mut context = Context::from_args(&args, &registry);
if let Some(positional) = &call_info.args.positional { let mut input = args.input;
for (pos, arg) in positional.iter().enumerate() {
scope = scope.set_var(self.args[pos].to_string(), arg.clone());
}
}
let stream = async_stream! { let stream = async_stream! {
let mut context = Context::from_raw(&raw_args, &registry); while let Some(input) = input.next().await {
let call_info = call_info.clone();
let input_clone = Ok(input.clone()); let tag = tag.clone();
let input_stream = futures::stream::once(async { input_clone }).boxed().to_input_stream(); let evaluated = call_info.evaluate_with_new_it(&registry, &input)?;
let mut scope = Scope::it_value(input.clone());
let result = run_block( if let Some(positional) = &evaluated.args.positional {
&block, for (pos, arg) in positional.iter().enumerate() {
&mut context, scope = scope.set_var(alias_command.args[pos].to_string(), arg.clone());
input_stream,
&scope
).await;
match result {
Ok(stream) if stream.is_empty() => {
yield Err(ShellError::labeled_error(
"Expected a block",
"alias needs a block",
tag,
));
}
Ok(mut stream) => {
// We collect first to ensure errors are put into the context
while let Some(result) = stream.next().await {
yield Ok(ReturnSuccess::Value(result));
} }
}
let errors = context.get_errors(); let input_clone = Ok(input.clone());
if let Some(x) = errors.first() { let input_stream = futures::stream::once(async { input_clone }).boxed().to_input_stream();
//yield Err(error.clone());
let result = run_block(
&block,
&mut context,
input_stream,
&scope
).await;
match result {
Ok(stream) if stream.is_empty() => {
yield Err(ShellError::labeled_error(
"Expected a block",
"alias needs a block",
tag,
));
}
Ok(mut stream) => {
// We collect first to ensure errors are put into the context
while let Some(result) = stream.next().await {
yield Ok(ReturnSuccess::Value(result));
}
let errors = context.get_errors();
if let Some(x) = errors.first() {
//yield Err(error.clone());
yield Err(ShellError::labeled_error_with_secondary(
"Alias failed to run",
"alias failed to run",
tag.clone(),
x.to_string(),
tag
));
}
}
Err(e) => {
//yield Err(e);
yield Err(ShellError::labeled_error_with_secondary( yield Err(ShellError::labeled_error_with_secondary(
"Alias failed to run", "Alias failed to run",
"alias failed to run", "alias failed to run",
tag.clone(), tag.clone(),
x.to_string(), e.to_string(),
tag tag
)); ));
} }
} }
Err(e) => {
//yield Err(e);
yield Err(ShellError::labeled_error_with_secondary(
"Alias failed to run",
"alias failed to run",
tag.clone(),
e.to_string(),
tag
));
}
} }
}; };

View File

@ -1,6 +1,7 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged; use nu_source::Tagged;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::path::PathBuf; use std::path::PathBuf;
@ -12,7 +13,7 @@ pub struct TouchArgs {
pub target: Tagged<PathBuf>, pub target: Tagged<PathBuf>,
} }
impl PerItemCommand for Touch { impl WholeStreamCommand for Touch {
fn name(&self) -> &str { fn name(&self) -> &str {
"touch" "touch"
} }
@ -28,27 +29,19 @@ impl PerItemCommand for Touch {
} }
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
_registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info args.process(registry, touch)?.run()
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), touch)?
.run()
} }
} }
fn touch(args: TouchArgs, _context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> { fn touch(TouchArgs { target }: TouchArgs, _: RunnableContext) -> Result<OutputStream, ShellError> {
match OpenOptions::new() match OpenOptions::new().write(true).create(true).open(&target) {
.write(true)
.create(true)
.open(&args.target)
{
Ok(_) => Ok(OutputStream::empty()), Ok(_) => Ok(OutputStream::empty()),
Err(err) => Err(ShellError::labeled_error( Err(err) => Err(ShellError::labeled_error(
"File Error", "File Error",
err.to_string(), err.to_string(),
&args.target.tag, &target.tag,
)), )),
} }
} }

View File

@ -1,16 +1,20 @@
use crate::commands::PerItemCommand; use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::evaluate::evaluate_baseline_expr; use crate::evaluate::evaluate_baseline_expr;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{
hir::ClassifiedCommand, CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, hir::Block, hir::ClassifiedCommand, ReturnSuccess, Scope, Signature, SyntaxShape,
Value,
}; };
pub struct Where; pub struct Where;
impl PerItemCommand for Where { #[derive(Deserialize)]
pub struct WhereArgs {
block: Block,
}
impl WholeStreamCommand for Where {
fn name(&self) -> &str { fn name(&self) -> &str {
"where" "where"
} }
@ -29,68 +33,68 @@ impl PerItemCommand for Where {
fn run( fn run(
&self, &self,
call_info: &CallInfo, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
input: Value,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let block = call_info.args.expect_nth(0)?.clone(); args.process(registry, where_command)?.run()
}
let condition = match block { }
Value { fn where_command(
value: UntaggedValue::Block(block), WhereArgs { block }: WhereArgs,
RunnableContext {
name: tag,
registry,
input,
..
}: RunnableContext,
) -> Result<OutputStream, ShellError> {
let condition = {
if block.block.len() != 1 {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag, tag,
} => { ));
if block.block.len() != 1 { }
match block.block[0].list.get(0) {
Some(item) => match item {
ClassifiedCommand::Expr(expr) => expr.clone(),
_ => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a condition", "Expected a condition",
"expected a condition", "expected a condition",
tag, tag,
)); ))
} }
match block.block[0].list.get(0) { },
Some(item) => match item { None => {
ClassifiedCommand::Expr(expr) => expr.clone(),
_ => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
))
}
},
None => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
}
}
Value { tag, .. } => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a condition", "Expected a condition",
"expected a condition", "expected a condition",
tag, tag,
)); ));
} }
}; }
};
//FIXME: should we use the scope that's brought in as well? let mut input = input;
let condition = evaluate_baseline_expr(&condition, registry, &Scope::new(input.clone()))?;
let stream = match condition.as_bool() { let stream = async_stream! {
Ok(b) => { while let Some(input) = input.next().await {
if b {
VecDeque::from(vec![Ok(ReturnSuccess::Value(input))]) //FIXME: should we use the scope that's brought in as well?
} else { let condition = evaluate_baseline_expr(&condition, &registry, &Scope::new(input.clone()))?;
VecDeque::new()
match condition.as_bool() {
Ok(b) => {
if b {
yield Ok(ReturnSuccess::Value(input));
}
} }
} Err(e) => yield Err(e),
Err(e) => return Err(e), };
}; }
};
Ok(stream.into()) Ok(stream.to_output_stream())
}
} }

View File

@ -112,6 +112,30 @@ impl Context {
} }
} }
pub(crate) fn from_args(args: &CommandArgs, registry: &CommandRegistry) -> Context {
#[cfg(windows)]
{
Context {
registry: registry.clone(),
host: args.host.clone(),
current_errors: Arc::new(Mutex::new(vec![])),
ctrl_c: args.ctrl_c.clone(),
shell_manager: args.shell_manager.clone(),
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
}
}
#[cfg(not(windows))]
{
Context {
registry: registry.clone(),
host: args.host.clone(),
current_errors: Arc::new(Mutex::new(vec![])),
ctrl_c: args.ctrl_c.clone(),
shell_manager: args.shell_manager.clone(),
}
}
}
pub(crate) fn basic() -> Result<Context, Box<dyn Error>> { pub(crate) fn basic() -> Result<Context, Box<dyn Error>> {
let registry = CommandRegistry::new(); let registry = CommandRegistry::new();

View File

@ -356,7 +356,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> {
return visit::<Value, _>(value.val, name, fields, visitor); return visit::<Value, _>(value.val, name, fields, visitor);
} }
if name == "Evaluate" { if name == "Block" {
let block = match value.val { let block = match value.val {
Value { Value {
value: UntaggedValue::Block(block), value: UntaggedValue::Block(block),

View File

@ -71,10 +71,7 @@ macro_rules! trace_out_stream {
pub(crate) use nu_protocol::{errln, out, outln}; pub(crate) use nu_protocol::{errln, out, outln};
use nu_source::HasFallibleSpan; use nu_source::HasFallibleSpan;
pub(crate) use crate::commands::command::{ pub(crate) use crate::commands::command::{CommandArgs, RawCommandArgs, RunnableContext};
CallInfoExt, CommandArgs, PerItemCommand, RawCommandArgs, RunnableContext,
RunnablePerItemContext,
};
pub(crate) use crate::context::CommandRegistry; pub(crate) use crate::context::CommandRegistry;
pub(crate) use crate::context::Context; pub(crate) use crate::context::Context;
pub(crate) use crate::data::config; pub(crate) use crate::data::config;

View File

@ -102,7 +102,7 @@ impl Shell for FilesystemShell {
short_names, short_names,
with_symlink_targets, with_symlink_targets,
}: LsArgs, }: LsArgs,
context: &RunnablePerItemContext, context: &RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let ctrl_c = context.ctrl_c.clone(); let ctrl_c = context.ctrl_c.clone();
let name_tag = context.name.clone(); let name_tag = context.name.clone();

View File

@ -140,11 +140,7 @@ impl Shell for HelpShell {
self.path = path; self.path = path;
} }
fn ls( fn ls(&self, _args: LsArgs, _context: &RunnableContext) -> Result<OutputStream, ShellError> {
&self,
_args: LsArgs,
_context: &RunnablePerItemContext,
) -> Result<OutputStream, ShellError> {
let output = self let output = self
.commands() .commands()
.into_iter() .into_iter()

View File

@ -14,11 +14,7 @@ pub trait Shell: std::fmt::Debug {
fn name(&self) -> String; fn name(&self) -> String;
fn homedir(&self) -> Option<PathBuf>; fn homedir(&self) -> Option<PathBuf>;
fn ls( fn ls(&self, args: LsArgs, context: &RunnableContext) -> Result<OutputStream, ShellError>;
&self,
args: LsArgs,
context: &RunnablePerItemContext,
) -> Result<OutputStream, ShellError>;
fn cd(&self, args: CdArgs, name: Tag) -> Result<OutputStream, ShellError>; fn cd(&self, args: CdArgs, name: Tag) -> Result<OutputStream, ShellError>;
fn cp(&self, args: CopyArgs, name: Tag, path: &str) -> Result<OutputStream, ShellError>; fn cp(&self, args: CopyArgs, name: Tag, path: &str) -> Result<OutputStream, ShellError>;
fn mkdir(&self, args: MkdirArgs, name: Tag, path: &str) -> Result<OutputStream, ShellError>; fn mkdir(&self, args: MkdirArgs, name: Tag, path: &str) -> Result<OutputStream, ShellError>;

View File

@ -1,5 +1,5 @@
use crate::commands::cd::CdArgs; use crate::commands::cd::CdArgs;
use crate::commands::command::{EvaluatedWholeStreamCommandArgs, RunnablePerItemContext}; use crate::commands::command::EvaluatedWholeStreamCommandArgs;
use crate::commands::cp::CopyArgs; use crate::commands::cp::CopyArgs;
use crate::commands::ls::LsArgs; use crate::commands::ls::LsArgs;
use crate::commands::mkdir::MkdirArgs; use crate::commands::mkdir::MkdirArgs;
@ -132,11 +132,7 @@ impl ShellManager {
env[self.current_shell()].homedir() env[self.current_shell()].homedir()
} }
pub fn ls( pub fn ls(&self, args: LsArgs, context: &RunnableContext) -> Result<OutputStream, ShellError> {
&self,
args: LsArgs,
context: &RunnablePerItemContext,
) -> Result<OutputStream, ShellError> {
let env = self.shells.lock(); let env = self.shells.lock();
env[self.current_shell()].ls(args, context) env[self.current_shell()].ls(args, context)
@ -151,7 +147,7 @@ impl ShellManager {
pub fn cp( pub fn cp(
&self, &self,
args: CopyArgs, args: CopyArgs,
context: &RunnablePerItemContext, context: &RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let shells = self.shells.lock(); let shells = self.shells.lock();
@ -162,7 +158,7 @@ impl ShellManager {
pub fn rm( pub fn rm(
&self, &self,
args: RemoveArgs, args: RemoveArgs,
context: &RunnablePerItemContext, context: &RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let shells = self.shells.lock(); let shells = self.shells.lock();
@ -173,7 +169,7 @@ impl ShellManager {
pub fn mkdir( pub fn mkdir(
&self, &self,
args: MkdirArgs, args: MkdirArgs,
context: &RunnablePerItemContext, context: &RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let shells = self.shells.lock(); let shells = self.shells.lock();
@ -184,7 +180,7 @@ impl ShellManager {
pub fn mv( pub fn mv(
&self, &self,
args: MoveArgs, args: MoveArgs,
context: &RunnablePerItemContext, context: &RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let shells = self.shells.lock(); let shells = self.shells.lock();

View File

@ -95,7 +95,7 @@ impl Shell for ValueShell {
fn ls( fn ls(
&self, &self,
LsArgs { path, .. }: LsArgs, LsArgs { path, .. }: LsArgs,
context: &RunnablePerItemContext, context: &RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let mut full_path = PathBuf::from(self.path()); let mut full_path = PathBuf::from(self.path());
let name_tag = context.name.clone(); let name_tag = context.name.clone();

View File

@ -77,6 +77,36 @@ pub enum ClassifiedCommand {
Error(ParseError), Error(ParseError),
} }
impl ClassifiedCommand {
pub fn has_it_iteration(&self) -> bool {
match self {
ClassifiedCommand::Internal(command) => {
if let SpannedExpression {
expr: Expression::Literal(Literal::String(s)),
..
} = &*command.args.head
{
if s == "run_external" {
// For now, don't it-expand externals
return false;
}
}
let mut result = command.args.head.has_shallow_it_usage();
if let Some(positionals) = &command.args.positional {
for arg in positionals {
result = result || arg.has_shallow_it_usage();
}
}
result
}
ClassifiedCommand::Expr(expr) => expr.has_shallow_it_usage(),
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
pub struct Commands { pub struct Commands {
pub list: Vec<ClassifiedCommand>, pub list: Vec<ClassifiedCommand>,
@ -91,6 +121,37 @@ impl Commands {
pub fn push(&mut self, command: ClassifiedCommand) { pub fn push(&mut self, command: ClassifiedCommand) {
self.list.push(command); self.list.push(command);
} }
/// Convert all shallow uses of $it to `each { use of $it }`, converting each to a per-row command
pub fn expand_it_usage(&mut self) {
for idx in 0..self.list.len() {
if self.list[idx].has_it_iteration() {
self.list[idx] = ClassifiedCommand::Internal(InternalCommand {
name: "each".to_string(),
name_span: self.span,
args: hir::Call {
head: Box::new(SpannedExpression {
expr: Expression::Synthetic(Synthetic::String("each".to_string())),
span: self.span,
}),
named: None,
span: self.span,
positional: Some(vec![SpannedExpression {
expr: Expression::Block(Block {
block: vec![Commands {
list: vec![self.list[idx].clone()],
span: self.span,
}],
span: self.span,
}),
span: self.span,
}]),
is_last: false, // FIXME
},
})
}
}
}
} }
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
@ -110,6 +171,13 @@ impl Block {
pub fn push(&mut self, commands: Commands) { pub fn push(&mut self, commands: Commands) {
self.block.push(commands); self.block.push(commands);
} }
/// Convert all shallow uses of $it to `each { use of $it }`, converting each to a per-row command
pub fn expand_it_usage(&mut self) {
for commands in &mut self.block {
commands.expand_it_usage();
}
}
} }
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)] #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
@ -201,14 +269,6 @@ pub enum Member {
} }
impl Member { impl Member {
// pub fn int(span: Span, source: &Text) -> Member {
// if let Ok(big_int) = BigInt::from_str(span.slice(source)) {
// Member::Int(big_int, span)
// } else {
// unreachable!("Internal error: could not convert text to BigInt as expected")
// }
// }
pub fn to_path_member(&self) -> PathMember { pub fn to_path_member(&self) -> PathMember {
match self { match self {
//Member::String(outer, inner) => PathMember::string(inner.slice(source), *outer), //Member::String(outer, inner) => PathMember::string(inner.slice(source), *outer),
@ -459,6 +519,17 @@ impl SpannedExpression {
_ => 0, _ => 0,
} }
} }
pub fn has_shallow_it_usage(&self) -> bool {
match &self.expr {
Expression::Binary(binary) => {
binary.left.has_shallow_it_usage() || binary.right.has_shallow_it_usage()
}
Expression::Variable(Variable::It(_)) => true,
Expression::Path(path) => path.head.has_shallow_it_usage(),
_ => false,
}
}
} }
impl std::ops::Deref for SpannedExpression { impl std::ops::Deref for SpannedExpression {