Split Recipe::run into Recipe::{run_shebang,run_linewise} (#1270)

This commit is contained in:
Casey Rodarmor 2022-07-20 18:46:52 -07:00 committed by GitHub
parent 64b4d71d66
commit 4a4c669db9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 231 additions and 202 deletions

View File

@ -17,6 +17,13 @@ export JUST_LOG := log
test: test:
cargo test cargo test
ci: build-book
cargo test --all
cargo clippy --all --all-targets
cargo fmt --all -- --check
./bin/forbid
cargo update --locked --package just
fuzz: fuzz:
cargo +nightly fuzz run fuzz-compiler cargo +nightly fuzz run fuzz-compiler

View File

@ -11,6 +11,7 @@
clippy::shadow_unrelated, clippy::shadow_unrelated,
clippy::struct_excessive_bools, clippy::struct_excessive_bools,
clippy::too_many_lines, clippy::too_many_lines,
clippy::type_repetition_in_bounds,
clippy::wildcard_imports clippy::wildcard_imports
)] )]

View File

@ -85,10 +85,134 @@ impl<'src, D> Recipe<'src, D> {
); );
} }
let mut evaluator = let evaluator =
Evaluator::recipe_evaluator(context.config, dotenv, &scope, context.settings, search); Evaluator::recipe_evaluator(context.config, dotenv, &scope, context.settings, search);
if self.shebang { if self.shebang {
self.run_shebang(context, dotenv, &scope, positional, config, evaluator)
} else {
self.run_linewise(context, dotenv, &scope, positional, config, evaluator)
}
}
pub(crate) fn run_linewise<'run>(
&self,
context: &RecipeContext<'src, 'run>,
dotenv: &BTreeMap<String, String>,
scope: &Scope<'src, 'run>,
positional: &[String],
config: &Config,
mut evaluator: Evaluator<'src, 'run>,
) -> RunResult<'src, ()> {
let mut lines = self.body.iter().peekable();
let mut line_number = self.line_number() + 1;
loop {
if lines.peek().is_none() {
return Ok(());
}
let mut evaluated = String::new();
let mut continued = false;
let quiet_command = lines.peek().map_or(false, |line| line.is_quiet());
let infallible_command = lines.peek().map_or(false, |line| line.is_infallible());
loop {
if lines.peek().is_none() {
break;
}
let line = lines.next().unwrap();
line_number += 1;
evaluated += &evaluator.evaluate_line(line, continued)?;
if line.is_continuation() {
continued = true;
evaluated.pop();
} else {
break;
}
}
let mut command = evaluated.as_str();
if quiet_command {
command = &command[1..];
}
if infallible_command {
command = &command[1..];
}
if command.is_empty() {
continue;
}
if config.dry_run
|| config.verbosity.loquacious()
|| !((quiet_command ^ self.quiet) || config.verbosity.quiet())
{
let color = if config.highlight {
config.color.command()
} else {
config.color
};
eprintln!("{}", color.stderr().paint(command));
}
if config.dry_run {
continue;
}
let mut cmd = context.settings.shell_command(config);
cmd.current_dir(&context.search.working_directory);
cmd.arg(command);
if context.settings.positional_arguments {
cmd.arg(self.name.lexeme());
cmd.args(positional);
}
if config.verbosity.quiet() {
cmd.stderr(Stdio::null());
cmd.stdout(Stdio::null());
}
cmd.export(context.settings, dotenv, scope);
match InterruptHandler::guard(|| cmd.status()) {
Ok(exit_status) => {
if let Some(code) = exit_status.code() {
if code != 0 && !infallible_command {
return Err(Error::Code {
recipe: self.name(),
line_number: Some(line_number),
code,
});
}
} else {
return Err(error_from_signal(
self.name(),
Some(line_number),
exit_status,
));
}
}
Err(io_error) => {
return Err(Error::Io {
recipe: self.name(),
io_error,
});
}
};
}
}
pub(crate) fn run_shebang<'run>(
&self,
context: &RecipeContext<'src, 'run>,
dotenv: &BTreeMap<String, String>,
scope: &Scope<'src, 'run>,
positional: &[String],
config: &Config,
mut evaluator: Evaluator<'src, 'run>,
) -> RunResult<'src, ()> {
let mut evaluated_lines = vec![]; let mut evaluated_lines = vec![];
for line in &self.body { for line in &self.body {
evaluated_lines.push(evaluator.evaluate_line(line, false)?); evaluated_lines.push(evaluator.evaluate_line(line, false)?);
@ -177,133 +301,31 @@ impl<'src, D> Recipe<'src, D> {
command.args(positional); command.args(positional);
} }
command.export(context.settings, dotenv, &scope); command.export(context.settings, dotenv, scope);
// run it! // run it!
match InterruptHandler::guard(|| command.status()) { match InterruptHandler::guard(|| command.status()) {
Ok(exit_status) => { Ok(exit_status) => exit_status.code().map_or_else(
if let Some(code) = exit_status.code() { || Err(error_from_signal(self.name(), None, exit_status)),
if code != 0 { |code| {
return Err(Error::Code { if code == 0 {
Ok(())
} else {
Err(Error::Code {
recipe: self.name(), recipe: self.name(),
line_number: None, line_number: None,
code, code,
}); })
} }
} else { },
return Err(error_from_signal(self.name(), None, exit_status)); ),
} Err(io_error) => Err(Error::Shebang {
}
Err(io_error) => {
return Err(Error::Shebang {
recipe: self.name(), recipe: self.name(),
command: shebang.interpreter.to_owned(), command: shebang.interpreter.to_owned(),
argument: shebang.argument.map(String::from), argument: shebang.argument.map(String::from),
io_error, io_error,
}); }),
} }
};
} else {
let mut lines = self.body.iter().peekable();
let mut line_number = self.line_number() + 1;
loop {
if lines.peek().is_none() {
break;
}
let mut evaluated = String::new();
let mut continued = false;
let quiet_command = lines.peek().map_or(false, |line| line.is_quiet());
let infallible_command = lines.peek().map_or(false, |line| line.is_infallible());
loop {
if lines.peek().is_none() {
break;
}
let line = lines.next().unwrap();
line_number += 1;
evaluated += &evaluator.evaluate_line(line, continued)?;
if line.is_continuation() {
continued = true;
evaluated.pop();
} else {
break;
}
}
let mut command = evaluated.as_str();
if quiet_command {
command = &command[1..];
}
if infallible_command {
command = &command[1..];
}
if command.is_empty() {
continue;
}
if config.dry_run
|| config.verbosity.loquacious()
|| !((quiet_command ^ self.quiet) || config.verbosity.quiet())
{
let color = if config.highlight {
config.color.command()
} else {
config.color
};
eprintln!("{}", color.stderr().paint(command));
}
if config.dry_run {
continue;
}
let mut cmd = context.settings.shell_command(config);
cmd.current_dir(&context.search.working_directory);
cmd.arg(command);
if context.settings.positional_arguments {
cmd.arg(self.name.lexeme());
cmd.args(positional);
}
if config.verbosity.quiet() {
cmd.stderr(Stdio::null());
cmd.stdout(Stdio::null());
}
cmd.export(context.settings, dotenv, &scope);
match InterruptHandler::guard(|| cmd.status()) {
Ok(exit_status) => {
if let Some(code) = exit_status.code() {
if code != 0 && !infallible_command {
return Err(Error::Code {
recipe: self.name(),
line_number: Some(line_number),
code,
});
}
} else {
return Err(error_from_signal(
self.name(),
Some(line_number),
exit_status,
));
}
}
Err(io_error) => {
return Err(Error::Io {
recipe: self.name(),
io_error,
});
}
};
}
}
Ok(())
} }
} }

View File

@ -254,7 +254,6 @@ impl Subcommand {
let stdout = String::from_utf8_lossy(&output.stdout); let stdout = String::from_utf8_lossy(&output.stdout);
let recipes = stdout let recipes = stdout
.trim()
.split_whitespace() .split_whitespace()
.map(str::to_owned) .map(str::to_owned)
.collect::<Vec<String>>(); .collect::<Vec<String>>();