You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tourmaline/src/task/commands/with_cwd.rs

103 lines
3.3 KiB
Rust

use embed_nu::{
nu_engine::eval_block_with_early_return,
nu_protocol::{engine::Closure, Signature, Span, SyntaxShape},
CallExt, NewEmpty, Value,
};
#[derive(Clone)]
pub struct WithCwdCommand;
impl embed_nu::nu_protocol::engine::Command for WithCwdCommand {
fn name(&self) -> &str {
"with-cwd"
}
fn signature(&self) -> embed_nu::nu_protocol::Signature {
Signature::new("with-cwd")
.required(
"dir",
SyntaxShape::String,
"The directory to run the closure in",
)
.required("closure", SyntaxShape::Any, "The closure to run")
.rest("rest", SyntaxShape::Any, "The parameters for the closure")
.category(embed_nu::nu_protocol::Category::Custom("Tourmalin".into()))
}
fn usage(&self) -> &str {
"with-cwd <path> <block> [<args>...]"
}
fn run(
&self,
engine_state: &embed_nu::nu_protocol::engine::EngineState,
stack: &mut embed_nu::nu_protocol::engine::Stack,
call: &embed_nu::nu_protocol::ast::Call,
input: embed_nu::PipelineData,
) -> Result<embed_nu::PipelineData, embed_nu::nu_protocol::ShellError> {
let path: String = call.req(engine_state, stack, 0)?;
let block: Closure = call.req(engine_state, stack, 1)?;
let block_args: Vec<Value> = call.rest(engine_state, stack, 2)?;
let block = engine_state.get_block(block.block_id);
let old_cwd = engine_state.get_env_var("PWD");
tracing::debug!("Executing block with CWD {path}");
stack.add_env_var("PWD".into(), Value::string(path, Span::empty()));
let params: Vec<_> = block
.signature
.required_positional
.iter()
.chain(block.signature.optional_positional.iter())
.collect();
for param in params.iter().zip(&block_args) {
if let Some(var_id) = param.0.var_id {
stack.add_var(var_id, param.1.clone())
}
}
if let Some(param) = &block.signature.rest_positional {
if block_args.len() > params.len() {
let mut args = vec![];
for r in block_args.into_iter().skip(params.len()) {
args.push(r);
}
let span = if let Some(arg) = args.first() {
arg.span()?
} else {
call.head
};
stack.add_var(
param
.var_id
.expect("Internal error: rest positional parameter lacks var_id"),
Value::List { vals: args, span },
)
}
}
let result = eval_block_with_early_return(
engine_state,
stack,
block,
input,
call.redirect_stdout,
call.redirect_stdout,
);
stack.add_env_var(
"PWD".into(),
old_cwd.cloned().unwrap_or_else(|| {
std::env::current_dir()
.map(|d| Value::string(d.to_string_lossy(), Span::empty()))
.unwrap_or_else(|_| Value::nothing(Span::empty()))
}),
);
result
}
}