just/src/runtime_error.rs

404 lines
10 KiB
Rust
Raw Normal View History

use crate::common::*;
2018-03-05 13:21:35 -08:00
use crate::misc::{maybe_s, ticks, write_message_context, And, Or, Tick};
2017-11-16 23:30:08 -08:00
#[derive(Debug)]
pub(crate) enum RuntimeError<'a> {
ArgumentCountMismatch {
2018-11-06 00:41:33 -08:00
recipe: &'a str,
parameters: Vec<&'a Parameter<'a>>,
found: usize,
min: usize,
max: usize,
},
Backtick {
token: Token<'a>,
output_error: OutputError,
},
Code {
recipe: &'a str,
line_number: Option<usize>,
code: i32,
},
Cygpath {
recipe: &'a str,
output_error: OutputError,
},
Dotenv {
dotenv_error: dotenv::Error,
},
FunctionCall {
token: Token<'a>,
message: String,
},
Internal {
message: String,
},
IoError {
recipe: &'a str,
io_error: io::Error,
},
Shebang {
recipe: &'a str,
command: String,
argument: Option<String>,
io_error: io::Error,
},
Signal {
recipe: &'a str,
line_number: Option<usize>,
signal: i32,
},
TmpdirIoError {
recipe: &'a str,
io_error: io::Error,
},
UnknownOverrides {
overrides: Vec<&'a str>,
},
UnknownRecipes {
recipes: Vec<&'a str>,
suggestion: Option<&'a str>,
},
Unknown {
recipe: &'a str,
line_number: Option<usize>,
},
2017-11-16 23:30:08 -08:00
}
impl<'a> RuntimeError<'a> {
pub(crate) fn code(&self) -> Option<i32> {
use RuntimeError::*;
2017-11-16 23:30:08 -08:00
match *self {
Code { code, .. }
| Backtick {
output_error: OutputError::Code(code),
..
} => Some(code),
2017-11-16 23:30:08 -08:00
_ => None,
}
}
}
impl<'a> Display for RuntimeError<'a> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
2017-11-16 23:30:08 -08:00
use RuntimeError::*;
let color = if f.alternate() {
Color::always()
} else {
Color::never()
};
2017-11-16 23:30:08 -08:00
let error = color.error();
let message = color.message();
write!(f, "{} {}", error.paint("error:"), message.prefix())?;
let mut error_token = None;
match *self {
UnknownRecipes {
ref recipes,
ref suggestion,
} => {
write!(
f,
"Justfile does not contain recipe{} {}.",
maybe_s(recipes.len()),
Or(&ticks(recipes))
)?;
2017-11-16 23:30:08 -08:00
if let Some(suggestion) = *suggestion {
write!(f, "\nDid you mean `{}`?", suggestion)?;
}
}
UnknownOverrides { ref overrides } => {
write!(
f,
"Variable{} {} overridden on the command line but not present in justfile",
maybe_s(overrides.len()),
And(&overrides.iter().map(Tick).collect::<Vec<_>>())
)?;
}
ArgumentCountMismatch {
recipe,
ref parameters,
found,
min,
max,
} => {
2017-11-16 23:30:08 -08:00
if min == max {
let expected = min;
write!(
f,
"Recipe `{}` got {} argument{} but {}takes {}",
recipe,
found,
maybe_s(found),
if expected < found { "only " } else { "" },
expected
)?;
2017-11-16 23:30:08 -08:00
} else if found < min {
write!(
f,
"Recipe `{}` got {} argument{} but takes at least {}",
recipe,
found,
maybe_s(found),
min
)?;
2017-11-16 23:30:08 -08:00
} else if found > max {
write!(
f,
"Recipe `{}` got {} argument{} but takes at most {}",
recipe,
found,
maybe_s(found),
max
)?;
2017-11-16 23:30:08 -08:00
}
write!(f, "\nusage:\n just {}", recipe)?;
for param in parameters {
if color.stderr().active() {
write!(f, " {:#}", param)?;
} else {
write!(f, " {}", param)?;
}
}
}
Code {
recipe,
line_number,
code,
} => {
2017-11-16 23:30:08 -08:00
if let Some(n) = line_number {
write!(
f,
"Recipe `{}` failed on line {} with exit code {}",
recipe, n, code
)?;
2017-11-16 23:30:08 -08:00
} else {
write!(f, "Recipe `{}` failed with exit code {}", recipe, code)?;
}
}
Cygpath {
recipe,
ref output_error,
} => match *output_error {
2017-11-16 23:30:08 -08:00
OutputError::Code(code) => {
write!(
f,
"Cygpath failed with exit code {} while translating recipe `{}` \
shebang interpreter path",
code, recipe
)?;
2017-11-16 23:30:08 -08:00
}
OutputError::Signal(signal) => {
write!(
f,
"Cygpath terminated by signal {} while translating recipe `{}` \
shebang interpreter path",
signal, recipe
)?;
2017-11-16 23:30:08 -08:00
}
OutputError::Unknown => {
write!(
f,
"Cygpath experienced an unknown failure while translating recipe `{}` \
shebang interpreter path",
recipe
)?;
2017-11-16 23:30:08 -08:00
}
OutputError::Io(ref io_error) => {
match io_error.kind() {
io::ErrorKind::NotFound => write!(
f,
"Could not find `cygpath` executable to translate recipe `{}` \
shebang interpreter path:\n{}",
recipe, io_error
),
2017-11-16 23:30:08 -08:00
io::ErrorKind::PermissionDenied => write!(
f,
"Could not run `cygpath` executable to translate recipe `{}` \
shebang interpreter path:\n{}",
recipe, io_error
),
2017-11-16 23:30:08 -08:00
_ => write!(f, "Could not run `cygpath` executable:\n{}", io_error),
}?;
}
OutputError::Utf8(ref utf8_error) => {
write!(
f,
"Cygpath successfully translated recipe `{}` shebang interpreter path, \
but output was not utf8: {}",
recipe, utf8_error
)?;
2017-11-16 23:30:08 -08:00
}
},
Dotenv { ref dotenv_error } => {
2018-08-27 18:36:40 -07:00
writeln!(f, "Failed to load .env: {}", dotenv_error)?;
2018-03-05 13:21:35 -08:00
}
FunctionCall {
ref token,
ref message,
} => {
writeln!(
f,
"Call to function `{}` failed: {}",
token.lexeme(),
message
)?;
error_token = Some(token);
}
Shebang {
recipe,
ref command,
ref argument,
ref io_error,
} => {
2017-11-16 23:30:08 -08:00
if let Some(ref argument) = *argument {
write!(
f,
"Recipe `{}` with shebang `#!{} {}` execution error: {}",
recipe, command, argument, io_error
)?;
2017-11-16 23:30:08 -08:00
} else {
write!(
f,
"Recipe `{}` with shebang `#!{}` execution error: {}",
recipe, command, io_error
)?;
2017-11-16 23:30:08 -08:00
}
}
Signal {
recipe,
line_number,
signal,
} => {
2017-11-16 23:30:08 -08:00
if let Some(n) = line_number {
write!(
f,
"Recipe `{}` was terminated on line {} by signal {}",
recipe, n, signal
)?;
2017-11-16 23:30:08 -08:00
} else {
write!(f, "Recipe `{}` was terminated by signal {}", recipe, signal)?;
}
}
Unknown {
recipe,
line_number,
} => {
2017-11-16 23:30:08 -08:00
if let Some(n) = line_number {
write!(
f,
"Recipe `{}` failed on line {} for an unknown reason",
recipe, n
)?;
2017-11-16 23:30:08 -08:00
} else {
}
}
IoError {
recipe,
ref io_error,
} => {
2017-11-16 23:30:08 -08:00
match io_error.kind() {
io::ErrorKind::NotFound => writeln!(
f,
2018-08-27 18:36:40 -07:00
"Recipe `{}` could not be run because just could not find `sh`:{}",
recipe, io_error
),
2018-08-27 18:36:40 -07:00
io::ErrorKind::PermissionDenied => writeln!(
f,
"Recipe `{}` could not be run because just could not run `sh`:{}",
recipe, io_error
),
_ => writeln!(
f,
"Recipe `{}` could not be run because of an IO error while \
launching `sh`:{}",
recipe, io_error
),
2017-11-16 23:30:08 -08:00
}?;
}
TmpdirIoError {
recipe,
ref io_error,
} => writeln!(
f,
"Recipe `{}` could not be run because of an IO error while trying \
to create a temporary directory or write a file to that directory`:{}",
recipe, io_error
)?,
Backtick {
ref token,
ref output_error,
} => match *output_error {
2017-11-16 23:30:08 -08:00
OutputError::Code(code) => {
2018-08-27 18:36:40 -07:00
writeln!(f, "Backtick failed with exit code {}", code)?;
2017-11-16 23:30:08 -08:00
error_token = Some(token);
}
OutputError::Signal(signal) => {
2018-08-27 18:36:40 -07:00
writeln!(f, "Backtick was terminated by signal {}", signal)?;
2017-11-16 23:30:08 -08:00
error_token = Some(token);
}
OutputError::Unknown => {
2018-08-27 18:36:40 -07:00
writeln!(f, "Backtick failed for an unknown reason")?;
2017-11-16 23:30:08 -08:00
error_token = Some(token);
}
OutputError::Io(ref io_error) => {
match io_error.kind() {
io::ErrorKind::NotFound => write!(
f,
"Backtick could not be run because just could not find `sh`:\n{}",
io_error
),
2017-11-16 23:30:08 -08:00
io::ErrorKind::PermissionDenied => write!(
f,
"Backtick could not be run because just could not run `sh`:\n{}",
io_error
),
_ => write!(
f,
"Backtick could not be run because of an IO \
error while launching `sh`:\n{}",
io_error
),
2017-11-16 23:30:08 -08:00
}?;
error_token = Some(token);
}
OutputError::Utf8(ref utf8_error) => {
writeln!(
f,
"Backtick succeeded but stdout was not utf8: {}",
utf8_error
)?;
2017-11-16 23:30:08 -08:00
error_token = Some(token);
}
},
Internal { ref message } => {
write!(
f,
"Internal runtime error, this may indicate a bug in just: {} \
consider filing an issue: https://github.com/casey/just/issues/new",
message
)?;
2017-11-16 23:30:08 -08:00
}
}
write!(f, "{}", message.suffix())?;
if let Some(token) = error_token {
write_message_context(
f,
Color::fmt(f).error(),
token.text,
token.offset,
token.line,
token.column,
token.lexeme().len(),
)?;
2017-11-16 23:30:08 -08:00
}
Ok(())
}
}