use nu_engine::eval_block;
use nu_parser::parse;
use nu_protocol::{
OutDest, PipelineData, ShellError, Value,
debugger::WithoutDebug,
engine::{EngineState, Redirection, Stack, StateWorkingSet},
shell_error::generic::GenericError,
};
use std::sync::Arc;
pub fn run_command_with_value(
command: &str,
input: &Value,
engine_state: &EngineState,
stack: &mut Stack,
) -> Result<PipelineData, ShellError> {
if is_ignored_command(command) {
return Err(ShellError::Generic(GenericError::new_internal(
"Command ignored",
"the command is ignored",
)));
}
let pipeline = PipelineData::value(input.clone(), None);
let pipeline = run_nu_command(engine_state, stack, command, pipeline)?;
if let PipelineData::Value(Value::Error { error, .. }, ..) = pipeline {
Err(ShellError::Generic(
GenericError::new_internal("Error from pipeline", error.to_string())
.with_inner([*error]),
))
} else {
Ok(pipeline)
}
}
pub fn run_nu_command(
engine_state: &EngineState,
stack: &mut Stack,
cmd: &str,
current: PipelineData,
) -> std::result::Result<PipelineData, ShellError> {
let mut engine_state = engine_state.clone();
eval_source2(&mut engine_state, stack, cmd.as_bytes(), "", current)
}
pub fn is_ignored_command(command: &str) -> bool {
let ignore_list: &[&str] = &["clear", "explore", "exit", "nu"];
command
.split_whitespace()
.any(|token| ignore_list.contains(&token))
}
fn eval_source2(
engine_state: &mut EngineState,
stack: &mut Stack,
source: &[u8],
fname: &str,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let (mut block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state);
let output = parse(
&mut working_set,
Some(fname), source,
false,
);
if let Some(err) = working_set.parse_errors.first() {
return Err(ShellError::Generic(GenericError::new_internal(
"Parse error",
err.to_string(),
)));
}
(output, working_set.render())
};
if let Err(err) = engine_state.merge_delta(delta) {
return Err(ShellError::Generic(
GenericError::new_internal("Merge error", err.to_string()).with_inner([err]),
));
}
if block.len() > 1 {
let range = ..block.pipelines.len() - 1;
Arc::make_mut(&mut block).pipelines.drain(range);
}
let stack = &mut stack.push_redirection(
Some(Redirection::Pipe(OutDest::PipeSeparate)),
Some(Redirection::Pipe(OutDest::PipeSeparate)),
);
eval_block::<WithoutDebug>(engine_state, stack, &block, input).map(|p| p.body)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_ignored_command_cases() {
let cases = [
("Simple", "clear", true),
("With flag", "clear -x", true),
("Prepended", "sudo clear", true),
("Appended", "exit now", true),
("Valid", "ls -la", false),
("Starts with ignored", "clearly ok", false),
(
"Starts with ignored existing command",
"nu_highlight",
false,
),
("Empty string", "", false),
("Spaces only", " ", false),
];
for (name, input, expected) in cases {
assert_eq!(
is_ignored_command(input),
expected,
"Case failed for {name}: {input:?}"
);
}
}
}