forked from extern/nushell
moved commands
This commit is contained in:
parent
f0d5e2dcf1
commit
8250b44ce5
34
crates/nu-command/src/core_commands/alias.rs
Normal file
34
crates/nu-command/src/core_commands/alias.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Alias;
|
||||||
|
|
||||||
|
impl Command for Alias {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"alias"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Alias a command (with optional flags) to a new name"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("alias")
|
||||||
|
.required("name", SyntaxShape::String, "name of the alias")
|
||||||
|
.required(
|
||||||
|
"initial_value",
|
||||||
|
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
|
||||||
|
"equals sign followed by value",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
35
crates/nu-command/src/core_commands/def.rs
Normal file
35
crates/nu-command/src/core_commands/def.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Def;
|
||||||
|
|
||||||
|
impl Command for Def {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"def"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Define a custom command"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("def")
|
||||||
|
.required("def_name", SyntaxShape::String, "definition name")
|
||||||
|
.required("params", SyntaxShape::Signature, "parameters")
|
||||||
|
.required(
|
||||||
|
"block",
|
||||||
|
SyntaxShape::Block(Some(vec![])),
|
||||||
|
"body of the definition",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
44
crates/nu-command/src/core_commands/do_.rs
Normal file
44
crates/nu-command/src/core_commands/do_.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use nu_engine::{eval_block, eval_expression};
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Do;
|
||||||
|
|
||||||
|
impl Command for Do {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"do"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Run a block"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("do").required(
|
||||||
|
"block",
|
||||||
|
SyntaxShape::Block(Some(vec![])),
|
||||||
|
"the block to run",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
let block = &call.positional[0];
|
||||||
|
|
||||||
|
let out = eval_expression(context, block)?;
|
||||||
|
|
||||||
|
match out {
|
||||||
|
Value::Block { val: block_id, .. } => {
|
||||||
|
let engine_state = context.engine_state.borrow();
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
eval_block(context, block, input)
|
||||||
|
}
|
||||||
|
_ => Ok(Value::nothing()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
crates/nu-command/src/core_commands/if_.rs
Normal file
67
crates/nu-command/src/core_commands/if_.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use nu_engine::{eval_block, eval_expression};
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct If;
|
||||||
|
|
||||||
|
impl Command for If {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"if"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Conditionally run a block."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("if")
|
||||||
|
.required("cond", SyntaxShape::Expression, "condition")
|
||||||
|
.required("then_block", SyntaxShape::Block(Some(vec![])), "then block")
|
||||||
|
.optional(
|
||||||
|
"else",
|
||||||
|
SyntaxShape::Keyword(b"else".to_vec(), Box::new(SyntaxShape::Expression)),
|
||||||
|
"optional else followed by else block",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
let cond = &call.positional[0];
|
||||||
|
let then_block = call.positional[1]
|
||||||
|
.as_block()
|
||||||
|
.expect("internal error: expected block");
|
||||||
|
let else_case = call.positional.get(2);
|
||||||
|
|
||||||
|
let result = eval_expression(context, cond)?;
|
||||||
|
match result {
|
||||||
|
Value::Bool { val, span } => {
|
||||||
|
let engine_state = context.engine_state.borrow();
|
||||||
|
if val {
|
||||||
|
let block = engine_state.get_block(then_block);
|
||||||
|
let state = context.enter_scope();
|
||||||
|
eval_block(&state, block, input)
|
||||||
|
} else if let Some(else_case) = else_case {
|
||||||
|
if let Some(else_expr) = else_case.as_keyword() {
|
||||||
|
if let Some(block_id) = else_expr.as_block() {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
let state = context.enter_scope();
|
||||||
|
eval_block(&state, block, input)
|
||||||
|
} else {
|
||||||
|
eval_expression(context, else_expr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eval_expression(context, else_case)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Value::Nothing { span })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(ShellError::CantConvert("bool".into(), result.span())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
crates/nu-command/src/core_commands/let_.rs
Normal file
50
crates/nu-command/src/core_commands/let_.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use nu_engine::eval_expression;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Let;
|
||||||
|
|
||||||
|
impl Command for Let {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"let"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Create a variable and give it a value."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("let")
|
||||||
|
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
|
||||||
|
.required(
|
||||||
|
"initial_value",
|
||||||
|
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
|
||||||
|
"equals sign followed by value",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
let var_id = call.positional[0]
|
||||||
|
.as_var()
|
||||||
|
.expect("internal error: missing variable");
|
||||||
|
|
||||||
|
let keyword_expr = call.positional[1]
|
||||||
|
.as_keyword()
|
||||||
|
.expect("internal error: missing keyword");
|
||||||
|
|
||||||
|
let rhs = eval_expression(context, keyword_expr)?;
|
||||||
|
|
||||||
|
//println!("Adding: {:?} to {}", rhs, var_id);
|
||||||
|
|
||||||
|
context.add_var(var_id, rhs);
|
||||||
|
Ok(Value::Nothing {
|
||||||
|
span: call.positional[0].span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
15
crates/nu-command/src/core_commands/mod.rs
Normal file
15
crates/nu-command/src/core_commands/mod.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
mod alias;
|
||||||
|
mod def;
|
||||||
|
mod do_;
|
||||||
|
mod if_;
|
||||||
|
mod let_;
|
||||||
|
mod module;
|
||||||
|
mod use_;
|
||||||
|
|
||||||
|
pub use alias::Alias;
|
||||||
|
pub use def::Def;
|
||||||
|
pub use do_::Do;
|
||||||
|
pub use if_::If;
|
||||||
|
pub use let_::Let;
|
||||||
|
pub use module::Module;
|
||||||
|
pub use use_::Use;
|
34
crates/nu-command/src/core_commands/module.rs
Normal file
34
crates/nu-command/src/core_commands/module.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Module;
|
||||||
|
|
||||||
|
impl Command for Module {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"module"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Define a custom module"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("module")
|
||||||
|
.required("module_name", SyntaxShape::String, "module name")
|
||||||
|
.required(
|
||||||
|
"block",
|
||||||
|
SyntaxShape::Block(Some(vec![])),
|
||||||
|
"body of the module",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
28
crates/nu-command/src/core_commands/use_.rs
Normal file
28
crates/nu-command/src/core_commands/use_.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Use;
|
||||||
|
|
||||||
|
impl Command for Use {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"use"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Use definitions from a module"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("use").required("module_name", SyntaxShape::String, "module name")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
51
crates/nu-command/src/env/let_env.rs
vendored
Normal file
51
crates/nu-command/src/env/let_env.rs
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use nu_engine::eval_expression;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct LetEnv;
|
||||||
|
|
||||||
|
impl Command for LetEnv {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"let-env"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Create an environment variable and give it a value."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("let-env")
|
||||||
|
.required("var_name", SyntaxShape::String, "variable name")
|
||||||
|
.required(
|
||||||
|
"initial_value",
|
||||||
|
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::String)),
|
||||||
|
"equals sign followed by value",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
let env_var = call.positional[0]
|
||||||
|
.as_string()
|
||||||
|
.expect("internal error: missing variable");
|
||||||
|
|
||||||
|
let keyword_expr = call.positional[1]
|
||||||
|
.as_keyword()
|
||||||
|
.expect("internal error: missing keyword");
|
||||||
|
|
||||||
|
let rhs = eval_expression(context, keyword_expr)?;
|
||||||
|
let rhs = rhs.as_string()?;
|
||||||
|
|
||||||
|
//println!("Adding: {:?} to {}", rhs, var_id);
|
||||||
|
|
||||||
|
context.add_env_var(env_var, rhs);
|
||||||
|
Ok(Value::Nothing {
|
||||||
|
span: call.positional[0].span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
3
crates/nu-command/src/env/mod.rs
vendored
Normal file
3
crates/nu-command/src/env/mod.rs
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod let_env;
|
||||||
|
|
||||||
|
pub use let_env::LetEnv;
|
51
crates/nu-command/src/experimental/git.rs
Normal file
51
crates/nu-command/src/experimental/git.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, Value};
|
||||||
|
|
||||||
|
pub struct Git;
|
||||||
|
|
||||||
|
impl Command for Git {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"git"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Run a block"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("git")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
use std::process::Command as ProcessCommand;
|
||||||
|
use std::process::Stdio;
|
||||||
|
|
||||||
|
let proc = ProcessCommand::new("git").stdout(Stdio::piped()).spawn();
|
||||||
|
|
||||||
|
match proc {
|
||||||
|
Ok(child) => {
|
||||||
|
match child.wait_with_output() {
|
||||||
|
Ok(val) => {
|
||||||
|
let result = val.stdout;
|
||||||
|
|
||||||
|
Ok(Value::string(&String::from_utf8_lossy(&result), call.head))
|
||||||
|
}
|
||||||
|
Err(_err) => {
|
||||||
|
// FIXME
|
||||||
|
Ok(Value::nothing())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_err) => {
|
||||||
|
// FIXME
|
||||||
|
Ok(Value::nothing())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
crates/nu-command/src/experimental/git_checkout.rs
Normal file
66
crates/nu-command/src/experimental/git_checkout.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use nu_engine::eval_expression;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct GitCheckout;
|
||||||
|
|
||||||
|
impl Command for GitCheckout {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"git checkout"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Checkout a git revision"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("git checkout").required(
|
||||||
|
"branch",
|
||||||
|
SyntaxShape::Custom(Box::new(SyntaxShape::String), "list-git-branches".into()),
|
||||||
|
"the branch to checkout",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
use std::process::Command as ProcessCommand;
|
||||||
|
use std::process::Stdio;
|
||||||
|
|
||||||
|
let block = &call.positional[0];
|
||||||
|
|
||||||
|
let out = eval_expression(context, block)?;
|
||||||
|
|
||||||
|
let out = out.as_string()?;
|
||||||
|
|
||||||
|
let proc = ProcessCommand::new("git")
|
||||||
|
.arg("checkout")
|
||||||
|
.arg(out)
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn();
|
||||||
|
|
||||||
|
match proc {
|
||||||
|
Ok(child) => {
|
||||||
|
match child.wait_with_output() {
|
||||||
|
Ok(val) => {
|
||||||
|
let result = val.stdout;
|
||||||
|
|
||||||
|
Ok(Value::string(&String::from_utf8_lossy(&result), call.head))
|
||||||
|
}
|
||||||
|
Err(_err) => {
|
||||||
|
// FIXME
|
||||||
|
Ok(Value::nothing())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_err) => {
|
||||||
|
// FIXME
|
||||||
|
Ok(Value::nothing())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
crates/nu-command/src/experimental/list_git_branches.rs
Normal file
69
crates/nu-command/src/experimental/list_git_branches.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Note: this is a temporary command that later will be converted into a pipeline
|
||||||
|
|
||||||
|
use std::process::Command as ProcessCommand;
|
||||||
|
use std::process::Stdio;
|
||||||
|
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, Value};
|
||||||
|
|
||||||
|
pub struct ListGitBranches;
|
||||||
|
|
||||||
|
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
|
||||||
|
impl Command for ListGitBranches {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"list-git-branches"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"List the git branches of the current directory."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("list-git-branches")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
let list_branches = ProcessCommand::new("git")
|
||||||
|
.arg("branch")
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn();
|
||||||
|
|
||||||
|
if let Ok(child) = list_branches {
|
||||||
|
if let Ok(output) = child.wait_with_output() {
|
||||||
|
let val = output.stdout;
|
||||||
|
|
||||||
|
let s = String::from_utf8_lossy(&val).to_string();
|
||||||
|
|
||||||
|
let lines: Vec<_> = s
|
||||||
|
.lines()
|
||||||
|
.filter_map(|x| {
|
||||||
|
if x.starts_with("* ") {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(x.trim())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|x| Value::String {
|
||||||
|
val: x.into(),
|
||||||
|
span: call.head,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(Value::List {
|
||||||
|
vals: lines,
|
||||||
|
span: call.head,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
crates/nu-command/src/experimental/mod.rs
Normal file
7
crates/nu-command/src/experimental/mod.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
mod git;
|
||||||
|
mod git_checkout;
|
||||||
|
mod list_git_branches;
|
||||||
|
|
||||||
|
pub use git::Git;
|
||||||
|
pub use git_checkout::GitCheckout;
|
||||||
|
pub use list_git_branches::ListGitBranches;
|
93
crates/nu-command/src/filesystem/ls.rs
Normal file
93
crates/nu-command/src/filesystem/ls.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
use nu_engine::eval_expression;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{IntoValueStream, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Ls;
|
||||||
|
|
||||||
|
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
|
||||||
|
impl Command for Ls {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"ls"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"List the files in a directory."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("ls").optional(
|
||||||
|
"pattern",
|
||||||
|
SyntaxShape::GlobPattern,
|
||||||
|
"the glob pattern to use",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
let pattern = if let Some(expr) = call.positional.get(0) {
|
||||||
|
let result = eval_expression(context, expr)?;
|
||||||
|
result.as_string()?
|
||||||
|
} else {
|
||||||
|
"*".into()
|
||||||
|
};
|
||||||
|
|
||||||
|
let call_span = call.head;
|
||||||
|
let glob = glob::glob(&pattern).unwrap();
|
||||||
|
|
||||||
|
Ok(Value::Stream {
|
||||||
|
stream: glob
|
||||||
|
.into_iter()
|
||||||
|
.map(move |x| match x {
|
||||||
|
Ok(path) => match std::fs::symlink_metadata(&path) {
|
||||||
|
Ok(metadata) => {
|
||||||
|
let is_file = metadata.is_file();
|
||||||
|
let is_dir = metadata.is_dir();
|
||||||
|
let filesize = metadata.len();
|
||||||
|
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["name".into(), "type".into(), "size".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: path.to_string_lossy().to_string(),
|
||||||
|
span: call_span,
|
||||||
|
},
|
||||||
|
if is_file {
|
||||||
|
Value::string("file", call_span)
|
||||||
|
} else if is_dir {
|
||||||
|
Value::string("dir", call_span)
|
||||||
|
} else {
|
||||||
|
Value::Nothing { span: call_span }
|
||||||
|
},
|
||||||
|
Value::Int {
|
||||||
|
val: filesize as i64,
|
||||||
|
span: call_span,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: call_span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => Value::Record {
|
||||||
|
cols: vec!["name".into(), "type".into(), "size".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: path.to_string_lossy().to_string(),
|
||||||
|
span: call_span,
|
||||||
|
},
|
||||||
|
Value::Nothing { span: call_span },
|
||||||
|
Value::Nothing { span: call_span },
|
||||||
|
],
|
||||||
|
span: call_span,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => Value::Nothing { span: call_span },
|
||||||
|
})
|
||||||
|
.into_value_stream(),
|
||||||
|
span: call_span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
3
crates/nu-command/src/filesystem/mod.rs
Normal file
3
crates/nu-command/src/filesystem/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod ls;
|
||||||
|
|
||||||
|
pub use ls::Ls;
|
227
crates/nu-command/src/filters/each.rs
Normal file
227
crates/nu-command/src/filters/each.rs
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
use nu_engine::eval_block;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{IntoValueStream, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Each;
|
||||||
|
|
||||||
|
impl Command for Each {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"each"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Run a block on each element of input"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("each")
|
||||||
|
.required(
|
||||||
|
"block",
|
||||||
|
SyntaxShape::Block(Some(vec![SyntaxShape::Any])),
|
||||||
|
"the block to run",
|
||||||
|
)
|
||||||
|
.switch("numbered", "iterate with an index", Some('n'))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
let block_id = call.positional[0]
|
||||||
|
.as_block()
|
||||||
|
.expect("internal error: expected block");
|
||||||
|
|
||||||
|
let numbered = call.has_flag("numbered");
|
||||||
|
let context = context.clone();
|
||||||
|
let span = call.head;
|
||||||
|
|
||||||
|
match input {
|
||||||
|
Value::Range { val, .. } => Ok(Value::Stream {
|
||||||
|
stream: val
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(move |(idx, x)| {
|
||||||
|
let engine_state = context.engine_state.borrow();
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let state = context.enter_scope();
|
||||||
|
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
if numbered {
|
||||||
|
state.add_var(
|
||||||
|
*var_id,
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int {
|
||||||
|
val: idx as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
x,
|
||||||
|
],
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
state.add_var(*var_id, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match eval_block(&state, block, Value::nothing()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(error) => Value::Error { error },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into_value_stream(),
|
||||||
|
span: call.head,
|
||||||
|
}),
|
||||||
|
Value::List { vals: val, .. } => Ok(Value::Stream {
|
||||||
|
stream: val
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(move |(idx, x)| {
|
||||||
|
let engine_state = context.engine_state.borrow();
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let state = context.enter_scope();
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
if numbered {
|
||||||
|
state.add_var(
|
||||||
|
*var_id,
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int {
|
||||||
|
val: idx as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
x,
|
||||||
|
],
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
state.add_var(*var_id, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match eval_block(&state, block, Value::nothing()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(error) => Value::Error { error },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into_value_stream(),
|
||||||
|
span: call.head,
|
||||||
|
}),
|
||||||
|
Value::Stream { stream, .. } => Ok(Value::Stream {
|
||||||
|
stream: stream
|
||||||
|
.enumerate()
|
||||||
|
.map(move |(idx, x)| {
|
||||||
|
let engine_state = context.engine_state.borrow();
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let state = context.enter_scope();
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
if numbered {
|
||||||
|
state.add_var(
|
||||||
|
*var_id,
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int {
|
||||||
|
val: idx as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
x,
|
||||||
|
],
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
state.add_var(*var_id, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match eval_block(&state, block, Value::nothing()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(error) => Value::Error { error },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into_value_stream(),
|
||||||
|
span: call.head,
|
||||||
|
}),
|
||||||
|
Value::Record { cols, vals, .. } => {
|
||||||
|
let mut output_cols = vec![];
|
||||||
|
let mut output_vals = vec![];
|
||||||
|
|
||||||
|
for (col, val) in cols.into_iter().zip(vals.into_iter()) {
|
||||||
|
let engine_state = context.engine_state.borrow();
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let state = context.enter_scope();
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
state.add_var(
|
||||||
|
*var_id,
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["column".into(), "value".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: col.clone(),
|
||||||
|
span: call.head,
|
||||||
|
},
|
||||||
|
val,
|
||||||
|
],
|
||||||
|
span: call.head,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match eval_block(&state, block, Value::nothing())? {
|
||||||
|
Value::Record {
|
||||||
|
mut cols, mut vals, ..
|
||||||
|
} => {
|
||||||
|
// TODO check that the lengths match
|
||||||
|
output_cols.append(&mut cols);
|
||||||
|
output_vals.append(&mut vals);
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
output_cols.push(col);
|
||||||
|
output_vals.push(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Record {
|
||||||
|
cols: output_cols,
|
||||||
|
vals: output_vals,
|
||||||
|
span: call.head,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
//TODO: we need to watch to make sure this is okay
|
||||||
|
let engine_state = context.engine_state.borrow();
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let state = context.enter_scope();
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
state.add_var(*var_id, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eval_block(&state, block, Value::nothing())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
crates/nu-command/src/filters/for_.rs
Normal file
94
crates/nu-command/src/filters/for_.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use nu_engine::{eval_block, eval_expression};
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{IntoValueStream, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct For;
|
||||||
|
|
||||||
|
impl Command for For {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"for"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Loop over a range"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("for")
|
||||||
|
.required(
|
||||||
|
"var_name",
|
||||||
|
SyntaxShape::Variable,
|
||||||
|
"name of the looping variable",
|
||||||
|
)
|
||||||
|
.required(
|
||||||
|
"range",
|
||||||
|
SyntaxShape::Keyword(
|
||||||
|
b"in".to_vec(),
|
||||||
|
Box::new(SyntaxShape::List(Box::new(SyntaxShape::Int))),
|
||||||
|
),
|
||||||
|
"range of the loop",
|
||||||
|
)
|
||||||
|
.required(
|
||||||
|
"block",
|
||||||
|
SyntaxShape::Block(Some(vec![])),
|
||||||
|
"the block to run",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
let var_id = call.positional[0]
|
||||||
|
.as_var()
|
||||||
|
.expect("internal error: missing variable");
|
||||||
|
|
||||||
|
let keyword_expr = call.positional[1]
|
||||||
|
.as_keyword()
|
||||||
|
.expect("internal error: missing keyword");
|
||||||
|
let values = eval_expression(context, keyword_expr)?;
|
||||||
|
|
||||||
|
let block = call.positional[2]
|
||||||
|
.as_block()
|
||||||
|
.expect("internal error: expected block");
|
||||||
|
let context = context.clone();
|
||||||
|
|
||||||
|
match values {
|
||||||
|
Value::Stream { stream, .. } => Ok(Value::Stream {
|
||||||
|
stream: stream
|
||||||
|
.map(move |x| {
|
||||||
|
let engine_state = context.engine_state.borrow();
|
||||||
|
let block = engine_state.get_block(block);
|
||||||
|
|
||||||
|
let state = context.enter_scope();
|
||||||
|
state.add_var(var_id, x);
|
||||||
|
|
||||||
|
//FIXME: DON'T UNWRAP
|
||||||
|
eval_block(&state, block, Value::nothing()).unwrap()
|
||||||
|
})
|
||||||
|
.into_value_stream(),
|
||||||
|
span: call.head,
|
||||||
|
}),
|
||||||
|
Value::List { vals: val, .. } => Ok(Value::List {
|
||||||
|
vals: val
|
||||||
|
.into_iter()
|
||||||
|
.map(move |x| {
|
||||||
|
let engine_state = context.engine_state.borrow();
|
||||||
|
let block = engine_state.get_block(block);
|
||||||
|
|
||||||
|
let state = context.enter_scope();
|
||||||
|
state.add_var(var_id, x);
|
||||||
|
|
||||||
|
//FIXME: DON'T UNWRAP
|
||||||
|
eval_block(&state, block, Value::nothing()).unwrap()
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
span: call.head,
|
||||||
|
}),
|
||||||
|
_ => Ok(Value::nothing()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
crates/nu-command/src/filters/length.rs
Normal file
53
crates/nu-command/src/filters/length.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, Value};
|
||||||
|
|
||||||
|
pub struct Length;
|
||||||
|
|
||||||
|
impl Command for Length {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"length"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Count the number of elements in the input."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("length")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
match input {
|
||||||
|
Value::List { vals: val, .. } => {
|
||||||
|
let length = val.len();
|
||||||
|
|
||||||
|
Ok(Value::Int {
|
||||||
|
val: length as i64,
|
||||||
|
span: call.head,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Value::Stream { stream, .. } => {
|
||||||
|
let length = stream.count();
|
||||||
|
|
||||||
|
Ok(Value::Int {
|
||||||
|
val: length as i64,
|
||||||
|
span: call.head,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Value::Nothing { .. } => Ok(Value::Int {
|
||||||
|
val: 0,
|
||||||
|
span: call.head,
|
||||||
|
}),
|
||||||
|
_ => Ok(Value::Int {
|
||||||
|
val: 1,
|
||||||
|
span: call.head,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
92
crates/nu-command/src/filters/lines.rs
Normal file
92
crates/nu-command/src/filters/lines.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{ShellError, Signature, Span, Value, ValueStream};
|
||||||
|
|
||||||
|
pub struct Lines;
|
||||||
|
|
||||||
|
const SPLIT_CHAR: char = '\n';
|
||||||
|
|
||||||
|
impl Command for Lines {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"lines"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Converts input to lines"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("lines")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
match input {
|
||||||
|
#[allow(clippy::needless_collect)]
|
||||||
|
// Collect is needed because the string may not live long enough for
|
||||||
|
// the Rc structure to continue using it. If split could take ownership
|
||||||
|
// of the split values, then this wouldn't be needed
|
||||||
|
Value::String { val, span } => {
|
||||||
|
let lines = val
|
||||||
|
.split(SPLIT_CHAR)
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
let iter = lines.into_iter().filter_map(move |s| {
|
||||||
|
if !s.is_empty() {
|
||||||
|
Some(Value::String { val: s, span })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Value::Stream {
|
||||||
|
stream: ValueStream(Rc::new(RefCell::new(iter))),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Value::Stream { stream, span: _ } => {
|
||||||
|
let iter = stream
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|value| {
|
||||||
|
if let Value::String { val, span } = value {
|
||||||
|
let inner = val
|
||||||
|
.split(SPLIT_CHAR)
|
||||||
|
.filter_map(|s| {
|
||||||
|
if !s.is_empty() {
|
||||||
|
Some(Value::String {
|
||||||
|
val: s.trim().into(),
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<Value>>();
|
||||||
|
|
||||||
|
Some(inner)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
Ok(Value::Stream {
|
||||||
|
stream: ValueStream(Rc::new(RefCell::new(iter))),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
val => Err(ShellError::UnsupportedInput(
|
||||||
|
format!("Not supported input: {}", val.as_string()?),
|
||||||
|
call.head,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
crates/nu-command/src/filters/mod.rs
Normal file
11
crates/nu-command/src/filters/mod.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
mod each;
|
||||||
|
mod for_;
|
||||||
|
mod length;
|
||||||
|
mod lines;
|
||||||
|
mod where_;
|
||||||
|
|
||||||
|
pub use each::Each;
|
||||||
|
pub use for_::For;
|
||||||
|
pub use length::Length;
|
||||||
|
pub use lines::Lines;
|
||||||
|
pub use where_::Where;
|
92
crates/nu-command/src/filters/where_.rs
Normal file
92
crates/nu-command/src/filters/where_.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
use nu_engine::eval_expression;
|
||||||
|
use nu_protocol::ast::{Call, Expr, Expression};
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{IntoValueStream, ShellError, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Where;
|
||||||
|
|
||||||
|
impl Command for Where {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"where"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Filter values based on a condition."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("where").required("cond", SyntaxShape::RowCondition, "condition")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
let cond = call.positional[0].clone();
|
||||||
|
|
||||||
|
let context = context.enter_scope();
|
||||||
|
|
||||||
|
let (var_id, cond) = match cond {
|
||||||
|
Expression {
|
||||||
|
expr: Expr::RowCondition(var_id, expr),
|
||||||
|
..
|
||||||
|
} => (var_id, expr),
|
||||||
|
_ => return Err(ShellError::InternalError("Expected row condition".into())),
|
||||||
|
};
|
||||||
|
|
||||||
|
match input {
|
||||||
|
Value::Stream { stream, span } => {
|
||||||
|
let output_stream = stream
|
||||||
|
.filter(move |value| {
|
||||||
|
context.add_var(var_id, value.clone());
|
||||||
|
|
||||||
|
let result = eval_expression(&context, &cond);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(result) => result.is_true(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into_value_stream();
|
||||||
|
|
||||||
|
Ok(Value::Stream {
|
||||||
|
stream: output_stream,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Value::List { vals, span } => {
|
||||||
|
let output_stream = vals
|
||||||
|
.into_iter()
|
||||||
|
.filter(move |value| {
|
||||||
|
context.add_var(var_id, value.clone());
|
||||||
|
|
||||||
|
let result = eval_expression(&context, &cond);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(result) => result.is_true(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into_value_stream();
|
||||||
|
|
||||||
|
Ok(Value::Stream {
|
||||||
|
stream: output_stream,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
context.add_var(var_id, x.clone());
|
||||||
|
|
||||||
|
let result = eval_expression(&context, &cond)?;
|
||||||
|
|
||||||
|
if result.is_true() {
|
||||||
|
Ok(x)
|
||||||
|
} else {
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
crates/nu-command/src/strings/build_string.rs
Normal file
38
crates/nu-command/src/strings/build_string.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use nu_engine::eval_expression;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct BuildString;
|
||||||
|
|
||||||
|
impl Command for BuildString {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"build-string"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Create a string from the arguments."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("build-string").rest("rest", SyntaxShape::String, "list of string")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
let output = call
|
||||||
|
.positional
|
||||||
|
.iter()
|
||||||
|
.map(|expr| eval_expression(context, expr).map(|val| val.into_string()))
|
||||||
|
.collect::<Result<Vec<String>, ShellError>>()?;
|
||||||
|
|
||||||
|
Ok(Value::String {
|
||||||
|
val: output.join(""),
|
||||||
|
span: call.head,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
3
crates/nu-command/src/strings/mod.rs
Normal file
3
crates/nu-command/src/strings/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod build_string;
|
||||||
|
|
||||||
|
pub use build_string::BuildString;
|
48
crates/nu-command/src/system/benchmark.rs
Normal file
48
crates/nu-command/src/system/benchmark.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use nu_engine::eval_block;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Benchmark;
|
||||||
|
|
||||||
|
impl Command for Benchmark {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"benchmark"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Time the running time of a block"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("benchmark").required(
|
||||||
|
"block",
|
||||||
|
SyntaxShape::Block(Some(vec![])),
|
||||||
|
"the block to run",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
let block = call.positional[0]
|
||||||
|
.as_block()
|
||||||
|
.expect("internal error: expected block");
|
||||||
|
let engine_state = context.engine_state.borrow();
|
||||||
|
let block = engine_state.get_block(block);
|
||||||
|
|
||||||
|
let state = context.enter_scope();
|
||||||
|
let start_time = Instant::now();
|
||||||
|
eval_block(&state, block, Value::nothing())?;
|
||||||
|
let end_time = Instant::now();
|
||||||
|
println!("{} ms", (end_time - start_time).as_millis());
|
||||||
|
Ok(Value::Nothing {
|
||||||
|
span: call.positional[0].span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
5
crates/nu-command/src/system/mod.rs
Normal file
5
crates/nu-command/src/system/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod benchmark;
|
||||||
|
mod run_external;
|
||||||
|
|
||||||
|
pub use benchmark::Benchmark;
|
||||||
|
pub use run_external::{External, ExternalCommand};
|
281
crates/nu-command/src/system/run_external.rs
Normal file
281
crates/nu-command/src/system/run_external.rs
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::env;
|
||||||
|
use std::io::{BufRead, BufReader, Write};
|
||||||
|
use std::process::{ChildStdin, Command as CommandSys, Stdio};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::{Call, Expression},
|
||||||
|
engine::{Command, EvaluationContext},
|
||||||
|
ShellError, Signature, SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
use nu_protocol::{Span, ValueStream};
|
||||||
|
|
||||||
|
use nu_engine::eval_expression;
|
||||||
|
|
||||||
|
const OUTPUT_BUFFER_SIZE: usize = 8192;
|
||||||
|
|
||||||
|
pub struct External;
|
||||||
|
|
||||||
|
impl Command for External {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"run_external"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Runs external command"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("run_external")
|
||||||
|
.switch("last_expression", "last_expression", None)
|
||||||
|
.rest("rest", SyntaxShape::Any, "external command to run")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
input: Value,
|
||||||
|
) -> Result<Value, ShellError> {
|
||||||
|
let command = ExternalCommand::try_new(call, context)?;
|
||||||
|
command.run_with_input(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ExternalCommand<'call, 'contex> {
|
||||||
|
pub name: &'call Expression,
|
||||||
|
pub args: &'call [Expression],
|
||||||
|
pub context: &'contex EvaluationContext,
|
||||||
|
pub last_expression: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'call, 'contex> ExternalCommand<'call, 'contex> {
|
||||||
|
pub fn try_new(
|
||||||
|
call: &'call Call,
|
||||||
|
context: &'contex EvaluationContext,
|
||||||
|
) -> Result<Self, ShellError> {
|
||||||
|
if call.positional.is_empty() {
|
||||||
|
return Err(ShellError::ExternalNotSupported(call.head));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
name: &call.positional[0],
|
||||||
|
args: &call.positional[1..],
|
||||||
|
context,
|
||||||
|
last_expression: call.has_flag("last_expression"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_name(&self) -> Result<String, ShellError> {
|
||||||
|
let value = eval_expression(self.context, self.name)?;
|
||||||
|
value.as_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_args(&self) -> Vec<String> {
|
||||||
|
self.args
|
||||||
|
.iter()
|
||||||
|
.filter_map(|expr| eval_expression(self.context, expr).ok())
|
||||||
|
.filter_map(|value| value.as_string().ok())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_with_input(&self, input: Value) -> Result<Value, ShellError> {
|
||||||
|
let mut process = self.create_command();
|
||||||
|
|
||||||
|
// TODO. We don't have a way to know the current directory
|
||||||
|
// This should be information from the EvaluationContex or EngineState
|
||||||
|
let path = env::current_dir().unwrap();
|
||||||
|
process.current_dir(path);
|
||||||
|
|
||||||
|
let envs = self.context.stack.get_env_vars();
|
||||||
|
process.envs(envs);
|
||||||
|
|
||||||
|
// If the external is not the last command, its output will get piped
|
||||||
|
// either as a string or binary
|
||||||
|
if !self.last_expression {
|
||||||
|
process.stdout(Stdio::piped());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is an input from the pipeline. The stdin from the process
|
||||||
|
// is piped so it can be used to send the input information
|
||||||
|
if let Value::String { .. } = input {
|
||||||
|
process.stdin(Stdio::piped());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Value::Stream { .. } = input {
|
||||||
|
process.stdin(Stdio::piped());
|
||||||
|
}
|
||||||
|
|
||||||
|
match process.spawn() {
|
||||||
|
Err(err) => Err(ShellError::ExternalCommand(
|
||||||
|
format!("{}", err),
|
||||||
|
self.name.span,
|
||||||
|
)),
|
||||||
|
Ok(mut child) => {
|
||||||
|
// if there is a string or a stream, that is sent to the pipe std
|
||||||
|
match input {
|
||||||
|
Value::String { val, span: _ } => {
|
||||||
|
if let Some(mut stdin_write) = child.stdin.take() {
|
||||||
|
self.write_to_stdin(&mut stdin_write, val.as_bytes())?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Binary { val, span: _ } => {
|
||||||
|
if let Some(mut stdin_write) = child.stdin.take() {
|
||||||
|
self.write_to_stdin(&mut stdin_write, &val)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Stream { stream, span: _ } => {
|
||||||
|
if let Some(mut stdin_write) = child.stdin.take() {
|
||||||
|
for value in stream {
|
||||||
|
match value {
|
||||||
|
Value::String { val, span: _ } => {
|
||||||
|
self.write_to_stdin(&mut stdin_write, val.as_bytes())?
|
||||||
|
}
|
||||||
|
Value::Binary { val, span: _ } => {
|
||||||
|
self.write_to_stdin(&mut stdin_write, &val)?
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this external is not the last expression, then its output is piped to a channel
|
||||||
|
// and we create a ValueStream that can be consumed
|
||||||
|
let value = if !self.last_expression {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let stdout = child.stdout.take().ok_or_else(|| {
|
||||||
|
ShellError::ExternalCommand(
|
||||||
|
"Error taking stdout from external".to_string(),
|
||||||
|
self.name.span,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
// Stdout is read using the Buffer reader. It will do so until there is an
|
||||||
|
// error or there are no more bytes to read
|
||||||
|
let mut buf_read = BufReader::with_capacity(OUTPUT_BUFFER_SIZE, stdout);
|
||||||
|
while let Ok(bytes) = buf_read.fill_buf() {
|
||||||
|
if bytes.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Cow generated from the function represents the conversion
|
||||||
|
// from bytes to String. If no replacements are required, then the
|
||||||
|
// borrowed value is a proper UTF-8 string. The Owned option represents
|
||||||
|
// a string where the values had to be replaced, thus marking it as bytes
|
||||||
|
let data = match String::from_utf8_lossy(bytes) {
|
||||||
|
Cow::Borrowed(s) => Data::String(s.into()),
|
||||||
|
Cow::Owned(_) => Data::Bytes(bytes.to_vec()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let length = bytes.len();
|
||||||
|
buf_read.consume(length);
|
||||||
|
|
||||||
|
match tx.send(data) {
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(_) => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// The ValueStream is consumed by the next expression in the pipeline
|
||||||
|
Value::Stream {
|
||||||
|
stream: ValueStream(Rc::new(RefCell::new(ChannelReceiver::new(rx)))),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Value::nothing()
|
||||||
|
};
|
||||||
|
|
||||||
|
match child.wait() {
|
||||||
|
Err(err) => Err(ShellError::ExternalCommand(
|
||||||
|
format!("{}", err),
|
||||||
|
self.name.span,
|
||||||
|
)),
|
||||||
|
Ok(_) => Ok(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_command(&self) -> CommandSys {
|
||||||
|
// in all the other cases shell out
|
||||||
|
if cfg!(windows) {
|
||||||
|
//TODO. This should be modifiable from the config file.
|
||||||
|
// We could give the option to call from powershell
|
||||||
|
// for minimal builds cwd is unused
|
||||||
|
let mut process = CommandSys::new("cmd");
|
||||||
|
process.arg("/c");
|
||||||
|
process.arg(&self.get_name().unwrap());
|
||||||
|
for arg in self.get_args() {
|
||||||
|
// Clean the args before we use them:
|
||||||
|
// https://stackoverflow.com/questions/1200235/how-to-pass-a-quoted-pipe-character-to-cmd-exe
|
||||||
|
// cmd.exe needs to have a caret to escape a pipe
|
||||||
|
let arg = arg.replace("|", "^|");
|
||||||
|
process.arg(&arg);
|
||||||
|
}
|
||||||
|
process
|
||||||
|
} else {
|
||||||
|
let cmd_with_args = vec![self.get_name().unwrap(), self.get_args().join(" ")].join(" ");
|
||||||
|
let mut process = CommandSys::new("sh");
|
||||||
|
process.arg("-c").arg(cmd_with_args);
|
||||||
|
process
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to_stdin(&self, stdin_write: &mut ChildStdin, val: &[u8]) -> Result<(), ShellError> {
|
||||||
|
if stdin_write.write(val).is_err() {
|
||||||
|
Err(ShellError::ExternalCommand(
|
||||||
|
"Error writing input to stdin".to_string(),
|
||||||
|
self.name.span,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The piped data from stdout from the external command can be either String
|
||||||
|
// or binary. We use this enum to pass the data from the spawned process
|
||||||
|
enum Data {
|
||||||
|
String(String),
|
||||||
|
Bytes(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receiver used for the ValueStream
|
||||||
|
// It implements iterator so it can be used as a ValueStream
|
||||||
|
struct ChannelReceiver {
|
||||||
|
rx: mpsc::Receiver<Data>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChannelReceiver {
|
||||||
|
pub fn new(rx: mpsc::Receiver<Data>) -> Self {
|
||||||
|
Self { rx }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for ChannelReceiver {
|
||||||
|
type Item = Value;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.rx.recv() {
|
||||||
|
Ok(v) => match v {
|
||||||
|
Data::String(s) => Some(Value::String {
|
||||||
|
val: s,
|
||||||
|
span: Span::unknown(),
|
||||||
|
}),
|
||||||
|
Data::Bytes(b) => Some(Value::Binary {
|
||||||
|
val: b,
|
||||||
|
span: Span::unknown(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
crates/nu-command/src/viewers/mod.rs
Normal file
3
crates/nu-command/src/viewers/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod table;
|
||||||
|
|
||||||
|
pub use table::Table;
|
137
crates/nu-command/src/viewers/table.rs
Normal file
137
crates/nu-command/src/viewers/table.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use nu_protocol::ast::{Call, PathMember};
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, Span, Value};
|
||||||
|
use nu_table::StyledString;
|
||||||
|
|
||||||
|
pub struct Table;
|
||||||
|
|
||||||
|
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
|
||||||
|
impl Command for Table {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"table"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Render the table."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("table")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
match input {
|
||||||
|
Value::List { vals, .. } => {
|
||||||
|
let table = convert_to_table(vals);
|
||||||
|
|
||||||
|
if let Some(table) = table {
|
||||||
|
let result = nu_table::draw_table(&table, 80, &HashMap::new());
|
||||||
|
|
||||||
|
Ok(Value::String {
|
||||||
|
val: result,
|
||||||
|
span: call.head,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Stream { stream, .. } => {
|
||||||
|
let table = convert_to_table(stream);
|
||||||
|
|
||||||
|
if let Some(table) = table {
|
||||||
|
let result = nu_table::draw_table(&table, 80, &HashMap::new());
|
||||||
|
|
||||||
|
Ok(Value::String {
|
||||||
|
val: result,
|
||||||
|
span: call.head,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x => Ok(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_to_table(iter: impl IntoIterator<Item = Value>) -> Option<nu_table::Table> {
|
||||||
|
let mut iter = iter.into_iter().peekable();
|
||||||
|
|
||||||
|
if let Some(first) = iter.peek() {
|
||||||
|
let mut headers = first.columns();
|
||||||
|
|
||||||
|
if !headers.is_empty() {
|
||||||
|
headers.insert(0, "#".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut data = vec![];
|
||||||
|
|
||||||
|
for (row_num, item) in iter.enumerate() {
|
||||||
|
let mut row = vec![row_num.to_string()];
|
||||||
|
|
||||||
|
if headers.is_empty() {
|
||||||
|
row.push(item.into_string())
|
||||||
|
} else {
|
||||||
|
for header in headers.iter().skip(1) {
|
||||||
|
let result = match item {
|
||||||
|
Value::Record { .. } => {
|
||||||
|
item.clone().follow_cell_path(&[PathMember::String {
|
||||||
|
val: header.into(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
_ => Ok(item.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(value) => row.push(value.into_string()),
|
||||||
|
Err(_) => row.push(String::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(nu_table::Table {
|
||||||
|
headers: headers
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| StyledString {
|
||||||
|
contents: x,
|
||||||
|
style: nu_table::TextStyle::default_header(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
data: data
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| {
|
||||||
|
x.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(col, y)| {
|
||||||
|
if col == 0 {
|
||||||
|
StyledString {
|
||||||
|
contents: y,
|
||||||
|
style: nu_table::TextStyle::default_header(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
StyledString {
|
||||||
|
contents: y,
|
||||||
|
style: nu_table::TextStyle::basic_left(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<StyledString>>()
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
theme: nu_table::Theme::rounded(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user