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![
// System/file operations
whole_stream_command(Pwd),
per_item_command(Ls),
per_item_command(Du),
whole_stream_command(Ls),
whole_stream_command(Du),
whole_stream_command(Cd),
per_item_command(Remove),
per_item_command(Open),
whole_stream_command(Remove),
whole_stream_command(Open),
whole_stream_command(Config),
per_item_command(Help),
per_item_command(History),
whole_stream_command(Help),
whole_stream_command(History),
whole_stream_command(Save),
per_item_command(Touch),
per_item_command(Cpy),
whole_stream_command(Touch),
whole_stream_command(Cpy),
whole_stream_command(Date),
per_item_command(Calc),
per_item_command(Mkdir),
per_item_command(Move),
per_item_command(Kill),
whole_stream_command(Calc),
whole_stream_command(Mkdir),
whole_stream_command(Move),
whole_stream_command(Kill),
whole_stream_command(Version),
whole_stream_command(Clear),
whole_stream_command(What),
whole_stream_command(Which),
whole_stream_command(Debug),
per_item_command(Alias),
whole_stream_command(Alias),
// Statistics
whole_stream_command(Size),
whole_stream_command(Count),
@ -269,7 +269,7 @@ pub fn create_default_context(
whole_stream_command(Next),
whole_stream_command(Previous),
whole_stream_command(Shells),
per_item_command(Enter),
whole_stream_command(Enter),
whole_stream_command(Exit),
// Viewers
whole_stream_command(Autoview),
@ -279,14 +279,14 @@ pub fn create_default_context(
whole_stream_command(SplitRow),
whole_stream_command(Lines),
whole_stream_command(Trim),
per_item_command(Echo),
per_item_command(Parse),
whole_stream_command(Echo),
whole_stream_command(Parse),
// Column manipulation
whole_stream_command(Reject),
whole_stream_command(Pick),
whole_stream_command(Get),
per_item_command(Edit),
per_item_command(Insert),
whole_stream_command(Edit),
whole_stream_command(Insert),
whole_stream_command(SplitBy),
// Row manipulation
whole_stream_command(Reverse),
@ -299,16 +299,16 @@ pub fn create_default_context(
whole_stream_command(Skip),
whole_stream_command(Nth),
whole_stream_command(Drop),
per_item_command(Format),
per_item_command(Where),
whole_stream_command(Format),
whole_stream_command(Where),
whole_stream_command(Compact),
whole_stream_command(Default),
whole_stream_command(SkipWhile),
whole_stream_command(Range),
whole_stream_command(Rename),
whole_stream_command(Uniq),
per_item_command(Each),
per_item_command(IsEmpty),
whole_stream_command(Each),
whole_stream_command(IsEmpty),
// Table manipulation
whole_stream_command(Shuffle),
whole_stream_command(Wrap),
@ -726,7 +726,7 @@ async fn process_line(
debug!("=== Parsed ===");
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);
//println!("{:#?}", pipeline);
@ -845,6 +845,8 @@ async fn process_line(
InputStream::empty()
};
classified_block.block.expand_it_usage();
match run_block(&classified_block.block, ctx, input_stream, &Scope::empty()).await {
Ok(input) => {
// 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 cd::Cd;
pub(crate) use command::{
per_item_command, whole_stream_command, Command, PerItemCommand, UnevaluatedCallInfo,
WholeStreamCommand,
};
pub(crate) use command::{whole_stream_command, Command, UnevaluatedCallInfo, WholeStreamCommand};
pub(crate) use alias::Alias;
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::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{
CallInfo, CommandAction, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_protocol::{hir::Block, CommandAction, ReturnSuccess, Signature, SyntaxShape, Value};
use nu_source::Tagged;
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 {
"alias"
}
@ -26,40 +32,32 @@ impl PerItemCommand for Alias {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
_input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let call_info = call_info.clone();
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())
args.process(registry, alias)?.run()
}
}
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 nu_errors::ShellError;
use nu_protocol::{CallInfo, Primitive, ReturnSuccess, UntaggedValue, Value};
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value};
pub struct Calc;
impl PerItemCommand for Calc {
#[derive(Deserialize)]
pub struct CalcArgs {}
impl WholeStreamCommand for Calc {
fn name(&self) -> &str {
"calc"
}
@ -16,36 +19,37 @@ impl PerItemCommand for Calc {
fn run(
&self,
_call_info: &CallInfo,
_registry: &CommandRegistry,
raw_args: &RawCommandArgs,
input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
calc(input, raw_args)
args.process(registry, calc)?.run()
}
}
fn calc(input: Value, args: &RawCommandArgs) -> Result<OutputStream, ShellError> {
let name_span = &args.call_info.name_tag.span;
let output = if let Ok(string) = input.as_string() {
match parse(&string, &input.tag) {
Ok(value) => ReturnSuccess::value(value),
Err(err) => Err(ShellError::labeled_error(
"Calculation error",
err,
&input.tag.span,
)),
}
} else {
Err(ShellError::labeled_error(
"Expected a string from pipeline",
"requires string input",
name_span,
))
};
Ok(vec![output].into())
pub fn calc(
_: CalcArgs,
RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
Ok(input
.map(move |input| {
if let Ok(string) = input.as_string() {
match parse(&string, &input.tag) {
Ok(value) => ReturnSuccess::value(value),
Err(err) => Err(ShellError::labeled_error(
"Calculation error",
err,
&input.tag.span,
)),
}
} else {
Err(ShellError::labeled_error(
"Expected a string from pipeline",
"requires string input",
name.clone(),
))
}
})
.to_output_stream())
}
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::external::run_external_command;
use crate::commands::classified::internal::run_internal_command;
use crate::context::Context;
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::UnevaluatedCallInfo;
use crate::prelude::*;
@ -122,7 +122,7 @@ pub(crate) fn run_internal_command(
}
CommandAction::AddAlias(name, args, block) => {
context.add_commands(vec![
per_item_command(AliasCommand::new(
whole_stream_command(AliasCommand::new(
name,
args,
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)]
#[get = "pub(crate)"]
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 input: InputStream,
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> {
args: T,
context: RunnableContext,
@ -626,7 +578,3 @@ impl WholeStreamCommand for FnFilterCommand {
pub fn whole_stream_command(command: impl WholeStreamCommand + 'static) -> Arc<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::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
use std::path::PathBuf;
@ -15,7 +15,7 @@ pub struct CopyArgs {
pub recursive: Tagged<bool>,
}
impl PerItemCommand for Cpy {
impl WholeStreamCommand for Cpy {
fn name(&self) -> &str {
"cp"
}
@ -37,18 +37,14 @@ impl PerItemCommand for Cpy {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
call_info
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), cp)?
.run()
args.process(registry, 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();
shell_manager.cp(args, context)
shell_manager.cp(args, &context)
}

View File

@ -1,12 +1,10 @@
extern crate filesize;
use crate::commands::command::RunnablePerItemContext;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use filesize::file_real_size_fast;
use glob::*;
use indexmap::map::IndexMap;
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 std::path::PathBuf;
@ -31,7 +29,7 @@ pub struct DuArgs {
min_size: Option<Tagged<u64>>,
}
impl PerItemCommand for Du {
impl WholeStreamCommand for Du {
fn name(&self) -> &str {
NAME
}
@ -75,18 +73,14 @@ impl PerItemCommand for Du {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
call_info
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), du)?
.run()
args.process(registry, 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 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));
let ctrl_c = ctx.ctrl_c.clone();
let ctrl_c = ctx.ctrl_c;
let all = args.all;
let deref = args.deref;
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::PerItemCommand;
use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry;
use crate::prelude::*;
use futures::stream::once;
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;
impl PerItemCommand for Each {
#[derive(Deserialize)]
pub struct EachArgs {
block: Block,
}
impl WholeStreamCommand for Each {
fn name(&self) -> &str {
"each"
}
@ -29,58 +34,51 @@ impl PerItemCommand for Each {
fn run(
&self,
call_info: &CallInfo,
args: CommandArgs,
registry: &CommandRegistry,
raw_args: &RawCommandArgs,
input: Value,
) -> Result<OutputStream, ShellError> {
let call_info = call_info.clone();
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())
Ok(args.process_raw(registry, each)?.run())
}
}
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 nu_errors::ShellError;
use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
pub struct Echo;
impl PerItemCommand for Echo {
#[derive(Deserialize)]
pub struct EchoArgs {
pub rest: Vec<Value>,
}
impl WholeStreamCommand for Echo {
fn name(&self) -> &str {
"echo"
}
@ -19,44 +25,36 @@ impl PerItemCommand for Echo {
fn run(
&self,
call_info: &CallInfo,
args: CommandArgs,
registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> {
run(call_info, registry, raw_args)
args.process(registry, echo)?.run()
}
}
fn run(
call_info: &CallInfo,
_registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
) -> Result<OutputStream, ShellError> {
fn echo(args: EchoArgs, _: RunnableContext) -> Result<OutputStream, ShellError> {
let mut output = vec![];
if let Some(ref positional) = call_info.args.positional {
for i in positional {
match i.as_string() {
Ok(s) => {
output.push(Ok(ReturnSuccess::Value(
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())));
}
},
for i in args.rest {
match i.as_string() {
Ok(s) => {
output.push(Ok(ReturnSuccess::Value(
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())));
}
},
}
}

View File

@ -1,13 +1,19 @@
use crate::commands::PerItemCommand;
use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry;
use crate::prelude::*;
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;
pub struct Edit;
impl PerItemCommand for Edit {
#[derive(Deserialize)]
pub struct EditArgs {
field: ColumnPath,
replacement: Value,
}
impl WholeStreamCommand for Edit {
fn name(&self) -> &str {
"edit"
}
@ -15,13 +21,13 @@ impl PerItemCommand for Edit {
fn signature(&self) -> Signature {
Signature::build("edit")
.required(
"Field",
"field",
SyntaxShape::ColumnPath,
"the name of the column to edit",
)
.required(
"Value",
SyntaxShape::String,
"replacement value",
SyntaxShape::Any,
"the new value to give the cell(s)",
)
}
@ -32,41 +38,45 @@ impl PerItemCommand for Edit {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
value: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let value_tag = value.tag();
let field = call_info.args.expect_nth(0)?.as_column_path()?;
let replacement = call_info.args.expect_nth(1)?.tagged_unknown();
args.process(registry, edit)?.run()
}
}
let stream = match value {
obj
@
Value {
fn edit(
EditArgs { field, replacement }: EditArgs,
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.replace_data_at_column_path(&field, replacement.item.clone()) {
Some(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]),
}) => match obj.replace_data_at_column_path(&field, replacement.clone()) {
Some(v) => yield Ok(ReturnSuccess::Value(v)),
None => {
return Err(ShellError::labeled_error(
yield Err(ShellError::labeled_error(
"edit could not find place to insert column",
"column name",
&field.tag,
obj.tag,
))
}
},
_ => {
return Err(ShellError::labeled_error(
Some(Value { tag, ..}) => {
yield Err(ShellError::labeled_error(
"Unrecognized type in stream",
"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::WholeStreamCommand;
use crate::context::CommandRegistry;
use crate::prelude::*;
use nu_errors::ShellError;
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;
impl PerItemCommand for Enter {
#[derive(Deserialize)]
pub struct EnterArgs {
location: Tagged<PathBuf>,
}
impl WholeStreamCommand for Enter {
fn name(&self) -> &str {
"enter"
}
@ -28,130 +35,121 @@ impl PerItemCommand for Enter {
fn run(
&self,
call_info: &CallInfo,
args: CommandArgs,
registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let raw_args = raw_args.clone();
match call_info.args.expect_nth(0)? {
Value {
value: UntaggedValue::Primitive(Primitive::Path(location)),
tag,
..
} => {
let location_string = location.display().to_string();
let location_clone = location_string.clone();
let tag_clone = tag.clone();
if location_string.starts_with("help") {
let spec = location_string.split(':').collect::<Vec<&str>>();
if spec.len() == 2 {
let (_, command) = (spec[0], spec[1]);
if registry.has(command) {
return Ok(vec![Ok(ReturnSuccess::Action(
CommandAction::EnterHelpShell(
UntaggedValue::string(command).into_value(Tag::unknown()),
),
))]
.into());
}
}
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell(
UntaggedValue::nothing().into_value(Tag::unknown()),
)))]
.into())
} else if location.is_dir() {
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell(
location_clone,
)))]
.into())
} else {
let stream = async_stream! {
// If it's a file, attempt to open the file as a value and enter it
let cwd = raw_args.shell_manager.path();
let full_path = std::path::PathBuf::from(cwd);
let (file_extension, contents, contents_tag) =
crate::commands::open::fetch(
&full_path,
&location_clone,
tag_clone.span,
).await?;
match contents {
UntaggedValue::Primitive(Primitive::String(_)) => {
let tagged_contents = contents.into_value(&contents_tag);
if let Some(extension) = file_extension {
let command_name = format!("from-{}", extension);
if let Some(converter) =
registry.get_command(&command_name)
{
let new_args = RawCommandArgs {
host: raw_args.host,
ctrl_c: raw_args.ctrl_c,
shell_manager: raw_args.shell_manager,
call_info: UnevaluatedCallInfo {
args: nu_protocol::hir::Call {
head: raw_args.call_info.args.head,
positional: None,
named: None,
span: Span::unknown(),
is_last: false,
},
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 result_vec: Vec<Result<ReturnSuccess, ShellError>> =
result.drain_vec().await;
for res in result_vec {
match res {
Ok(ReturnSuccess::Value(Value {
value,
..
})) => {
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
Value {
value,
tag: contents_tag.clone(),
})));
}
x => yield x,
}
}
} else {
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)));
}
}
};
Ok(stream.to_output_stream())
}
}
x => Ok(
vec![Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
x.clone(),
)))]
.into(),
),
}
Ok(args.process_raw(registry, enter)?.run())
}
}
fn enter(
EnterArgs { location }: EnterArgs,
RunnableContext {
registry,
name: tag,
..
}: RunnableContext,
raw_args: RawCommandArgs,
) -> Result<OutputStream, ShellError> {
let location_string = location.display().to_string();
let location_clone = location_string.clone();
if location_string.starts_with("help") {
let spec = location_string.split(':').collect::<Vec<&str>>();
if spec.len() == 2 {
let (_, command) = (spec[0], spec[1]);
if registry.has(command) {
return Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell(
UntaggedValue::string(command).into_value(Tag::unknown()),
)))]
.into());
}
}
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell(
UntaggedValue::nothing().into_value(Tag::unknown()),
)))]
.into())
} else if location.is_dir() {
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell(
location_clone,
)))]
.into())
} else {
let stream = async_stream! {
// If it's a file, attempt to open the file as a value and enter it
let cwd = raw_args.shell_manager.path();
let full_path = std::path::PathBuf::from(cwd);
let (file_extension, contents, contents_tag) =
crate::commands::open::fetch(
&full_path,
&PathBuf::from(location_clone),
tag.span,
).await?;
match contents {
UntaggedValue::Primitive(Primitive::String(_)) => {
let tagged_contents = contents.into_value(&contents_tag);
if let Some(extension) = file_extension {
let command_name = format!("from-{}", extension);
if let Some(converter) =
registry.get_command(&command_name)
{
let new_args = RawCommandArgs {
host: raw_args.host,
ctrl_c: raw_args.ctrl_c,
shell_manager: raw_args.shell_manager,
call_info: UnevaluatedCallInfo {
args: nu_protocol::hir::Call {
head: raw_args.call_info.args.head,
positional: None,
named: None,
span: Span::unknown(),
is_last: false,
},
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 result_vec: Vec<Result<ReturnSuccess, ShellError>> =
result.drain_vec().await;
for res in result_vec {
match res {
Ok(ReturnSuccess::Value(Value {
value,
..
})) => {
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
Value {
value,
tag: contents_tag.clone(),
})));
}
x => yield x,
}
}
} else {
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)));
}
}
};
Ok(stream.to_output_stream())
}
}

View File

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

View File

@ -1,24 +1,33 @@
use crate::commands::PerItemCommand;
use crate::commands::WholeStreamCommand;
use crate::data::command_dict;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{
CallInfo, NamedType, PositionalType, Primitive, ReturnSuccess, Signature, SyntaxShape,
TaggedDictBuilder, UntaggedValue, Value,
NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder,
UntaggedValue,
};
use nu_source::SpannedItem;
use nu_source::{SpannedItem, Tagged};
use nu_value_ext::get_data_by_key;
pub struct Help;
impl PerItemCommand for Help {
#[derive(Deserialize)]
pub struct HelpArgs {
command: Option<Tagged<String>>,
}
impl WholeStreamCommand for Help {
fn name(&self) -> &str {
"help"
}
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 {
@ -27,67 +36,65 @@ impl PerItemCommand for Help {
fn run(
&self,
call_info: &CallInfo,
args: CommandArgs,
registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
_input: Value,
) -> Result<OutputStream, ShellError> {
let tag = &call_info.name_tag;
args.process(registry, help)?.run()
}
}
match call_info.args.nth(0) {
Some(Value {
value: UntaggedValue::Primitive(Primitive::String(document)),
tag,
}) => {
let mut help = VecDeque::new();
if document == "commands" {
let mut sorted_names = registry.names();
sorted_names.sort();
for cmd in sorted_names {
let mut short_desc = TaggedDictBuilder::new(tag.clone());
let value = command_dict(
registry.get_command(&cmd).ok_or_else(|| {
ShellError::labeled_error(
format!("Could not load {}", cmd),
"could not load command",
tag,
)
})?,
tag.clone(),
);
fn help(
HelpArgs { command }: HelpArgs,
RunnableContext { registry, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
if let Some(document) = command {
let mut help = VecDeque::new();
if document.item == "commands" {
let mut sorted_names = registry.names();
sorted_names.sort();
for cmd in sorted_names {
let mut short_desc = TaggedDictBuilder::new(name.clone());
let document_tag = document.tag.clone();
let value = command_dict(
registry.get_command(&cmd).ok_or_else(|| {
ShellError::labeled_error(
format!("Could not load {}", cmd),
"could not load command",
document_tag,
)
})?,
name.clone(),
);
short_desc.insert_untagged("name", cmd);
short_desc.insert_untagged(
"description",
get_data_by_key(&value, "usage".spanned_unknown())
.ok_or_else(|| {
ShellError::labeled_error(
"Expected a usage key",
"expected a 'usage' key",
&value.tag,
)
})?
.as_string()?,
);
short_desc.insert_untagged("name", cmd);
short_desc.insert_untagged(
"description",
get_data_by_key(&value, "usage".spanned_unknown())
.ok_or_else(|| {
ShellError::labeled_error(
"Expected a usage key",
"expected a 'usage' key",
&value.tag,
)
})?
.as_string()?,
);
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())
help.push_back(ReturnSuccess::value(short_desc.into_value()));
}
_ => {
let msg = r#"Welcome to Nushell.
} else if let Some(command) = registry.get_command(&document.item) {
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.
* 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/"#;
let output_stream = futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::string(msg).into_value(tag),
)]);
let output_stream = futures::stream::iter(vec![ReturnSuccess::value(
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::commands::PerItemCommand;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{CallInfo, ReturnSuccess, Signature, UntaggedValue, Value};
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use std::fs::File;
use std::io::{BufRead, BufReader};
pub struct History;
impl PerItemCommand for History {
#[derive(Deserialize)]
pub struct HistoryArgs;
impl WholeStreamCommand for History {
fn name(&self) -> &str {
"history"
}
@ -23,27 +26,30 @@ impl PerItemCommand for History {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
_input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let tag = call_info.name_tag.clone();
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())
args.process(registry, history)?.run()
}
}
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::prelude::*;
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;
pub struct Insert;
impl PerItemCommand for Insert {
#[derive(Deserialize)]
pub struct InsertArgs {
column: ColumnPath,
value: Value,
}
impl WholeStreamCommand for Insert {
fn name(&self) -> &str {
"insert"
}
@ -27,40 +33,45 @@ impl PerItemCommand for Insert {
}
fn usage(&self) -> &str {
"Edit an existing column to have a new value."
"Insert a new column with a given value."
}
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
value: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let value_tag = value.tag();
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())
args.process(registry, insert)?.run()
}
}
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::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{
CallInfo, ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
use nu_value_ext::ValueExt;
@ -17,7 +15,12 @@ enum IsEmptyFor {
pub struct IsEmpty;
impl PerItemCommand for IsEmpty {
#[derive(Deserialize)]
pub struct IsEmptyArgs {
rest: Vec<Value>,
}
impl WholeStreamCommand for IsEmpty {
fn name(&self) -> &str {
"empty?"
}
@ -35,61 +38,100 @@ impl PerItemCommand for IsEmpty {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
value: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let value_tag = value.tag();
args.process(registry, is_empty)?.run()
}
}
let action = if call_info.args.len() <= 2 {
let field = call_info.args.expect_nth(0);
let replacement_if_true = call_info.args.expect_nth(1);
fn is_empty(
IsEmptyArgs { rest }: IsEmptyArgs,
RunnableContext { input, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
Ok(input
.map(move |value| {
let value_tag = value.tag();
match (field, replacement_if_true) {
(Ok(field), Ok(replacement_if_true)) => IsEmptyFor::RowWithFieldAndFallback(
Box::new(field.as_column_path()?),
replacement_if_true.clone(),
),
(Ok(field), Err(_)) => IsEmptyFor::RowWithField(field.as_column_path()?),
(_, _) => IsEmptyFor::Value,
}
} else {
let no_args = vec![];
let mut arguments = call_info
.args
.positional
.as_ref()
.unwrap_or_else(|| &no_args)
.iter()
.rev();
let replacement_if_true = match arguments.next() {
Some(arg) => arg.clone(),
None => UntaggedValue::boolean(value.is_empty()).into_value(&value_tag),
let action = if rest.len() <= 2 {
let field = rest.get(0);
let replacement_if_true = rest.get(1);
match (field, replacement_if_true) {
(Some(field), Some(replacement_if_true)) => {
IsEmptyFor::RowWithFieldAndFallback(
Box::new(field.as_column_path()?),
replacement_if_true.clone(),
)
}
(Some(field), None) => IsEmptyFor::RowWithField(field.as_column_path()?),
(_, _) => IsEmptyFor::Value,
}
} else {
// let no_args = vec![];
let mut arguments = rest.iter().rev();
let replacement_if_true = match arguments.next() {
Some(arg) => arg.clone(),
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(
arguments
.map(|a| a.as_column_path())
.filter_map(Result::ok)
.collect(),
replacement_if_true,
)
};
match action {
IsEmptyFor::Value => Ok(ReturnSuccess::Value(
UntaggedValue::boolean(value.is_empty()).into_value(value_tag),
)),
IsEmptyFor::RowWithFieldsAndFallback(fields, default) => {
let mut out = value;
match action {
IsEmptyFor::Value => Ok(futures::stream::iter(vec![Ok(ReturnSuccess::Value(
UntaggedValue::boolean(value.is_empty()).into_value(value_tag),
))])
.to_output_stream()),
IsEmptyFor::RowWithFieldsAndFallback(fields, default) => {
let mut out = value;
for field in fields.iter() {
let val =
out.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?;
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 =
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
@
Value {
@ -97,8 +139,11 @@ impl PerItemCommand for IsEmpty {
..
} => {
if val.is_empty() {
match obj.replace_data_at_column_path(&field, default.clone()) {
Some(v) => Ok(v),
match obj.replace_data_at_column_path(
&field,
UntaggedValue::boolean(true).into_value(&value_tag),
) {
Some(v) => Ok(ReturnSuccess::Value(v)),
None => Err(ShellError::labeled_error(
"empty? could not find place to check emptiness",
"column name",
@ -106,7 +151,7 @@ impl PerItemCommand for IsEmpty {
)),
}
} else {
Ok(obj)
Ok(ReturnSuccess::Value(value))
}
}
_ => Err(ShellError::labeled_error(
@ -114,90 +159,40 @@ impl PerItemCommand for IsEmpty {
"original value",
&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())
}
IsEmptyFor::RowWithField(field) => {
let val =
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,
UntaggedValue::boolean(true).into_value(&value_tag),
) {
Some(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]),
None => {
return Err(ShellError::labeled_error(
match &value {
obj
@
Value {
value: UntaggedValue::Row(_),
..
} => {
if val.is_empty() {
match obj.replace_data_at_column_path(&field, default) {
Some(v) => Ok(ReturnSuccess::Value(v)),
None => Err(ShellError::labeled_error(
"empty? could not find place to check emptiness",
"column name",
&field.tag,
))
)),
}
} else {
Ok(ReturnSuccess::Value(value))
}
} else {
futures::stream::iter(vec![Ok(ReturnSuccess::Value(value))])
}
}
_ => {
return Err(ShellError::labeled_error(
_ => Err(ShellError::labeled_error(
"Unrecognized type in stream",
"original value",
&value_tag,
))
)),
}
};
Ok(stream.to_output_stream())
}
}
IsEmptyFor::RowWithFieldAndFallback(field, default) => {
let val =
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())
}
}
}
})
.to_output_stream())
}

View File

@ -1,8 +1,8 @@
use crate::commands::command::RunnablePerItemContext;
use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
use std::process::{Command, Stdio};
@ -16,7 +16,7 @@ pub struct KillArgs {
pub quiet: Tagged<bool>,
}
impl PerItemCommand for Kill {
impl WholeStreamCommand for Kill {
fn name(&self) -> &str {
"kill"
}
@ -39,14 +39,10 @@ impl PerItemCommand for Kill {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
call_info
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), kill)?
.run()
args.process(registry, kill)?.run()
}
}
@ -57,7 +53,7 @@ fn kill(
force,
quiet,
}: KillArgs,
_context: &RunnablePerItemContext,
_context: RunnableContext,
) -> Result<OutputStream, ShellError> {
let mut cmd = if cfg!(windows) {
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 nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
use std::path::PathBuf;
@ -18,7 +18,7 @@ pub struct LsArgs {
pub with_symlink_targets: bool,
}
impl PerItemCommand for Ls {
impl WholeStreamCommand for Ls {
fn name(&self) -> &str {
"ls"
}
@ -54,17 +54,13 @@ impl PerItemCommand for Ls {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
call_info
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), ls)?
.run()
args.process(registry, ls)?.run()
}
}
fn ls(args: LsArgs, context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> {
context.shell_manager.ls(args, context)
fn ls(args: LsArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
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::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
use std::path::PathBuf;
@ -13,7 +13,7 @@ pub struct MkdirArgs {
pub rest: Vec<Tagged<PathBuf>>,
}
impl PerItemCommand for Mkdir {
impl WholeStreamCommand for Mkdir {
fn name(&self) -> &str {
"mkdir"
}
@ -28,18 +28,14 @@ impl PerItemCommand for Mkdir {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
call_info
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), mkdir)?
.run()
args.process(registry, 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();
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::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
use std::path::PathBuf;
@ -14,7 +14,7 @@ pub struct MoveArgs {
pub dst: Tagged<PathBuf>,
}
impl PerItemCommand for Move {
impl WholeStreamCommand for Move {
fn name(&self) -> &str {
"mv"
}
@ -39,18 +39,14 @@ impl PerItemCommand for Move {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
call_info
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), mv)?
.run()
args.process(registry, 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();
shell_manager.mv(args, context)
shell_manager.mv(args, &context)
}

View File

@ -1,14 +1,19 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{
CallInfo, CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::{AnchorLocation, Span};
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_source::{AnchorLocation, Span, Tagged};
use std::path::{Path, PathBuf};
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 {
"open"
}
@ -33,36 +38,23 @@ impl PerItemCommand for Open {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
run(call_info, raw_args)
args.process(registry, open)?.run()
}
}
fn run(call_info: &CallInfo, raw_args: &RawCommandArgs) -> Result<OutputStream, ShellError> {
let shell_manager = &raw_args.shell_manager;
fn open(
OpenArgs { path, raw }: OpenArgs,
RunnableContext { shell_manager, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let cwd = PathBuf::from(shell_manager.path());
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 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 {
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 = if has_raw {
let file_extension = if raw.item {
None
} else {
// 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).
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);
@ -92,7 +84,7 @@ fn run(call_info: &CallInfo, raw_args: &RawCommandArgs) -> Result<OutputStream,
pub async fn fetch(
cwd: &PathBuf,
location: &str,
location: &PathBuf,
span: Span,
) -> Result<(Option<String>, UntaggedValue, Tag), ShellError> {
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::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{
CallInfo, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
};
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
use nu_source::Tagged;
use regex::Regex;
#[derive(Debug)]
@ -84,7 +82,12 @@ fn build_regex(commands: &[ParseCommand]) -> String {
}
pub struct Parse;
impl PerItemCommand for Parse {
#[derive(Deserialize)]
pub struct ParseArgs {
pattern: Tagged<String>,
}
impl WholeStreamCommand for Parse {
fn name(&self) -> &str {
"parse"
}
@ -92,7 +95,7 @@ impl PerItemCommand for Parse {
fn signature(&self) -> Signature {
Signature::build("parse").required(
"pattern",
SyntaxShape::Any,
SyntaxShape::String,
"the pattern to match. Eg) \"{foo}: {bar}\"",
)
}
@ -103,41 +106,55 @@ impl PerItemCommand for Parse {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
value: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
//let value_tag = value.tag();
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())
args.process(registry, parse_command)?.run()
}
}
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::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
use std::path::PathBuf;
@ -16,7 +16,7 @@ pub struct RemoveArgs {
pub trash: Tagged<bool>,
}
impl PerItemCommand for Remove {
impl WholeStreamCommand for Remove {
fn name(&self) -> &str {
"rm"
}
@ -38,18 +38,14 @@ impl PerItemCommand for Remove {
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
call_info
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), rm)?
.run()
args.process(registry, 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();
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::WholeStreamCommand;
use crate::prelude::*;
use derive_new::new;
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 {
name: String,
args: Vec<String>,
block: Block,
}
impl PerItemCommand for AliasCommand {
impl WholeStreamCommand for AliasCommand {
fn name(&self) -> &str {
&self.name
}
@ -33,73 +34,76 @@ impl PerItemCommand for AliasCommand {
fn run(
&self,
call_info: &CallInfo,
args: CommandArgs,
registry: &CommandRegistry,
raw_args: &RawCommandArgs,
input: Value,
) -> Result<OutputStream, ShellError> {
let tag = call_info.name_tag.clone();
let call_info = call_info.clone();
let tag = args.call_info.name_tag.clone();
let call_info = args.call_info.clone();
let registry = registry.clone();
let raw_args = raw_args.clone();
let block = self.block.clone();
let mut scope = Scope::it_value(input.clone());
if let Some(positional) = &call_info.args.positional {
for (pos, arg) in positional.iter().enumerate() {
scope = scope.set_var(self.args[pos].to_string(), arg.clone());
}
}
let alias_command = self.clone();
let mut context = Context::from_args(&args, &registry);
let mut input = args.input;
let stream = async_stream! {
let mut context = Context::from_raw(&raw_args, &registry);
let input_clone = Ok(input.clone());
let input_stream = futures::stream::once(async { input_clone }).boxed().to_input_stream();
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));
while let Some(input) = input.next().await {
let call_info = call_info.clone();
let tag = tag.clone();
let evaluated = call_info.evaluate_with_new_it(&registry, &input)?;
let mut scope = Scope::it_value(input.clone());
if let Some(positional) = &evaluated.args.positional {
for (pos, arg) in positional.iter().enumerate() {
scope = scope.set_var(alias_command.args[pos].to_string(), arg.clone());
}
}
let errors = context.get_errors();
if let Some(x) = errors.first() {
//yield Err(error.clone());
let input_clone = Ok(input.clone());
let input_stream = futures::stream::once(async { input_clone }).boxed().to_input_stream();
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(
"Alias failed to run",
"alias failed to run",
tag.clone(),
x.to_string(),
e.to_string(),
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 nu_errors::ShellError;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
use std::fs::OpenOptions;
use std::path::PathBuf;
@ -12,7 +13,7 @@ pub struct TouchArgs {
pub target: Tagged<PathBuf>,
}
impl PerItemCommand for Touch {
impl WholeStreamCommand for Touch {
fn name(&self) -> &str {
"touch"
}
@ -28,27 +29,19 @@ impl PerItemCommand for Touch {
}
fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
raw_args: &RawCommandArgs,
_input: Value,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
call_info
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), touch)?
.run()
args.process(registry, touch)?.run()
}
}
fn touch(args: TouchArgs, _context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> {
match OpenOptions::new()
.write(true)
.create(true)
.open(&args.target)
{
fn touch(TouchArgs { target }: TouchArgs, _: RunnableContext) -> Result<OutputStream, ShellError> {
match OpenOptions::new().write(true).create(true).open(&target) {
Ok(_) => Ok(OutputStream::empty()),
Err(err) => Err(ShellError::labeled_error(
"File Error",
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::evaluate::evaluate_baseline_expr;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{
hir::ClassifiedCommand, CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue,
Value,
hir::Block, hir::ClassifiedCommand, ReturnSuccess, Scope, Signature, SyntaxShape,
};
pub struct Where;
impl PerItemCommand for Where {
#[derive(Deserialize)]
pub struct WhereArgs {
block: Block,
}
impl WholeStreamCommand for Where {
fn name(&self) -> &str {
"where"
}
@ -29,68 +33,68 @@ impl PerItemCommand for Where {
fn run(
&self,
call_info: &CallInfo,
args: CommandArgs,
registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
input: Value,
) -> Result<OutputStream, ShellError> {
let block = call_info.args.expect_nth(0)?.clone();
let condition = match block {
Value {
value: UntaggedValue::Block(block),
args.process(registry, where_command)?.run()
}
}
fn where_command(
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,
} => {
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(
"Expected a condition",
"expected a condition",
tag,
));
))
}
match block.block[0].list.get(0) {
Some(item) => match item {
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, .. } => {
},
None => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
};
}
};
//FIXME: should we use the scope that's brought in as well?
let condition = evaluate_baseline_expr(&condition, registry, &Scope::new(input.clone()))?;
let mut input = input;
let stream = match condition.as_bool() {
Ok(b) => {
if b {
VecDeque::from(vec![Ok(ReturnSuccess::Value(input))])
} else {
VecDeque::new()
let stream = async_stream! {
while let Some(input) = input.next().await {
//FIXME: should we use the scope that's brought in as well?
let condition = evaluate_baseline_expr(&condition, &registry, &Scope::new(input.clone()))?;
match condition.as_bool() {
Ok(b) => {
if b {
yield Ok(ReturnSuccess::Value(input));
}
}
}
Err(e) => return Err(e),
};
Err(e) => yield 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>> {
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);
}
if name == "Evaluate" {
if name == "Block" {
let block = match value.val {
Value {
value: UntaggedValue::Block(block),

View File

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

View File

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

View File

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

View File

@ -14,11 +14,7 @@ pub trait Shell: std::fmt::Debug {
fn name(&self) -> String;
fn homedir(&self) -> Option<PathBuf>;
fn ls(
&self,
args: LsArgs,
context: &RunnablePerItemContext,
) -> Result<OutputStream, ShellError>;
fn ls(&self, args: LsArgs, context: &RunnableContext) -> 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 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::command::{EvaluatedWholeStreamCommandArgs, RunnablePerItemContext};
use crate::commands::command::EvaluatedWholeStreamCommandArgs;
use crate::commands::cp::CopyArgs;
use crate::commands::ls::LsArgs;
use crate::commands::mkdir::MkdirArgs;
@ -132,11 +132,7 @@ impl ShellManager {
env[self.current_shell()].homedir()
}
pub fn ls(
&self,
args: LsArgs,
context: &RunnablePerItemContext,
) -> Result<OutputStream, ShellError> {
pub fn ls(&self, args: LsArgs, context: &RunnableContext) -> Result<OutputStream, ShellError> {
let env = self.shells.lock();
env[self.current_shell()].ls(args, context)
@ -151,7 +147,7 @@ impl ShellManager {
pub fn cp(
&self,
args: CopyArgs,
context: &RunnablePerItemContext,
context: &RunnableContext,
) -> Result<OutputStream, ShellError> {
let shells = self.shells.lock();
@ -162,7 +158,7 @@ impl ShellManager {
pub fn rm(
&self,
args: RemoveArgs,
context: &RunnablePerItemContext,
context: &RunnableContext,
) -> Result<OutputStream, ShellError> {
let shells = self.shells.lock();
@ -173,7 +169,7 @@ impl ShellManager {
pub fn mkdir(
&self,
args: MkdirArgs,
context: &RunnablePerItemContext,
context: &RunnableContext,
) -> Result<OutputStream, ShellError> {
let shells = self.shells.lock();
@ -184,7 +180,7 @@ impl ShellManager {
pub fn mv(
&self,
args: MoveArgs,
context: &RunnablePerItemContext,
context: &RunnableContext,
) -> Result<OutputStream, ShellError> {
let shells = self.shells.lock();

View File

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

View File

@ -77,6 +77,36 @@ pub enum ClassifiedCommand {
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)]
pub struct Commands {
pub list: Vec<ClassifiedCommand>,
@ -91,6 +121,37 @@ impl Commands {
pub fn push(&mut self, command: ClassifiedCommand) {
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)]
@ -110,6 +171,13 @@ impl Block {
pub fn push(&mut self, commands: 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)]
@ -201,14 +269,6 @@ pub enum 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 {
match self {
//Member::String(outer, inner) => PathMember::string(inner.slice(source), *outer),
@ -459,6 +519,17 @@ impl SpannedExpression {
_ => 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 {