diff --git a/src/line.rs b/src/line.rs index bbc0a4e..f296627 100644 --- a/src/line.rs +++ b/src/line.rs @@ -31,4 +31,11 @@ impl<'src> Line<'src> { _ => false, } } + + pub(crate) fn is_infallable(&self) -> bool { + match self.fragments.first() { + Some(Fragment::Text { token }) => token.lexeme().starts_with('-'), + _ => false, + } + } } diff --git a/src/recipe.rs b/src/recipe.rs index aa3548a..d854dd5 100644 --- a/src/recipe.rs +++ b/src/recipe.rs @@ -211,6 +211,10 @@ impl<'src, D> Recipe<'src, D> { let mut evaluated = String::new(); let mut continued = false; let quiet_command = lines.peek().map(|line| line.is_quiet()).unwrap_or(false); + let infallable_command = lines + .peek() + .map(|line| line.is_infallable()) + .unwrap_or(false); loop { if lines.peek().is_none() { break; @@ -226,7 +230,7 @@ impl<'src, D> Recipe<'src, D> { } } let mut command = evaluated.as_str(); - if quiet_command { + if quiet_command || infallable_command { command = &command[1..]; } @@ -266,7 +270,7 @@ impl<'src, D> Recipe<'src, D> { match InterruptHandler::guard(|| cmd.status()) { Ok(exit_status) => if let Some(code) = exit_status.code() { - if code != 0 { + if code != 0 && !infallable_command { return Err(RuntimeError::Code { recipe: self.name(), line_number: Some(line_number), diff --git a/tests/misc.rs b/tests/misc.rs index 2a02573..58bed43 100644 --- a/tests/misc.rs +++ b/tests/misc.rs @@ -1285,6 +1285,30 @@ test! { status: EXIT_FAILURE, } +test! { + name: infallable_command, + justfile: r#" +infallable: + -exit 101 +"#, + stderr: "exit 101\n", + status: EXIT_SUCCESS, +} + +test! { + name: infallable_with_failing, + justfile: r#" +infallable: + -exit 101 + exit 202 +"#, + stderr: r#"exit 101 +exit 202 +error: Recipe `infallable` failed on line 4 with exit code 202 +"#, + status: 202, +} + test! { name: quiet_recipe, justfile: r#"