2022-06-18 21:56:31 -07:00
|
|
|
use super::*;
|
Gargantuan refactor (#522)
- Instead of changing the current directory with `env::set_current_dir`
to be implicitly inherited by subprocesses, we now use
`Command::current_dir` to set it explicitly. This feels much better,
since we aren't dependent on the implicit state of the process's
current directory.
- Subcommand execution is much improved.
- Added a ton of tests for config parsing, config execution, working
dir, and search dir.
- Error messages are improved. Many more will be colored.
- The Config is now onwed, instead of borrowing from the arguments and
the `clap::ArgMatches` object. This is a huge ergonomic improvement,
especially in tests, and I don't think anyone will notice.
- `--edit` now uses `$VISUAL`, `$EDITOR`, or `vim`, in that order,
matching git, which I think is what most people will expect.
- Added a cute `tmptree!{}` macro, for creating temporary directories
populated with directories and files for tests.
- Admitted that grammer is LL(k) and I don't know what `k` is.
2019-11-09 21:43:20 -08:00
|
|
|
|
2021-07-26 01:26:06 -07:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) enum Error<'src> {
|
|
|
|
ArgumentCountMismatch {
|
2021-09-16 06:44:40 -07:00
|
|
|
recipe: &'src str,
|
2021-07-26 01:26:06 -07:00
|
|
|
parameters: Vec<Parameter<'src>>,
|
2021-09-16 06:44:40 -07:00
|
|
|
found: usize,
|
|
|
|
min: usize,
|
|
|
|
max: usize,
|
2021-07-26 01:26:06 -07:00
|
|
|
},
|
|
|
|
Backtick {
|
2021-09-16 06:44:40 -07:00
|
|
|
token: Token<'src>,
|
2021-07-26 01:26:06 -07:00
|
|
|
output_error: OutputError,
|
|
|
|
},
|
|
|
|
ChooserInvoke {
|
2021-09-16 06:44:40 -07:00
|
|
|
shell_binary: String,
|
2021-07-26 01:26:06 -07:00
|
|
|
shell_arguments: String,
|
2021-09-16 06:44:40 -07:00
|
|
|
chooser: OsString,
|
|
|
|
io_error: io::Error,
|
2021-07-26 01:26:06 -07:00
|
|
|
},
|
|
|
|
ChooserRead {
|
2021-09-16 06:44:40 -07:00
|
|
|
chooser: OsString,
|
2021-07-26 01:26:06 -07:00
|
|
|
io_error: io::Error,
|
|
|
|
},
|
|
|
|
ChooserStatus {
|
|
|
|
chooser: OsString,
|
2021-09-16 06:44:40 -07:00
|
|
|
status: ExitStatus,
|
2021-07-26 01:26:06 -07:00
|
|
|
},
|
|
|
|
ChooserWrite {
|
2021-09-16 06:44:40 -07:00
|
|
|
chooser: OsString,
|
2021-07-26 01:26:06 -07:00
|
|
|
io_error: io::Error,
|
|
|
|
},
|
2023-01-12 19:25:28 -08:00
|
|
|
CircularInclude {
|
|
|
|
current: PathBuf,
|
|
|
|
include: PathBuf,
|
|
|
|
},
|
2021-07-26 01:26:06 -07:00
|
|
|
Code {
|
2021-09-16 06:44:40 -07:00
|
|
|
recipe: &'src str,
|
2021-07-26 01:26:06 -07:00
|
|
|
line_number: Option<usize>,
|
2021-09-16 06:44:40 -07:00
|
|
|
code: i32,
|
2022-10-31 00:52:03 -07:00
|
|
|
print_message: bool,
|
2021-07-26 01:26:06 -07:00
|
|
|
},
|
|
|
|
CommandInvoke {
|
2021-09-16 06:44:40 -07:00
|
|
|
binary: OsString,
|
2021-07-26 01:26:06 -07:00
|
|
|
arguments: Vec<OsString>,
|
2021-09-16 06:44:40 -07:00
|
|
|
io_error: io::Error,
|
2021-07-26 01:26:06 -07:00
|
|
|
},
|
|
|
|
CommandStatus {
|
2021-09-16 06:44:40 -07:00
|
|
|
binary: OsString,
|
2021-07-26 01:26:06 -07:00
|
|
|
arguments: Vec<OsString>,
|
2021-09-16 06:44:40 -07:00
|
|
|
status: ExitStatus,
|
2021-07-26 01:26:06 -07:00
|
|
|
},
|
|
|
|
Compile {
|
|
|
|
compile_error: CompileError<'src>,
|
|
|
|
},
|
|
|
|
Config {
|
|
|
|
config_error: ConfigError,
|
|
|
|
},
|
|
|
|
Cygpath {
|
2021-09-16 06:44:40 -07:00
|
|
|
recipe: &'src str,
|
2021-07-26 01:26:06 -07:00
|
|
|
output_error: OutputError,
|
|
|
|
},
|
|
|
|
DefaultRecipeRequiresArguments {
|
2021-09-16 06:44:40 -07:00
|
|
|
recipe: &'src str,
|
2021-07-26 01:26:06 -07:00
|
|
|
min_arguments: usize,
|
|
|
|
},
|
|
|
|
Dotenv {
|
2022-12-14 20:32:27 -08:00
|
|
|
dotenv_error: dotenvy::Error,
|
2021-07-26 01:26:06 -07:00
|
|
|
},
|
2021-11-17 00:07:48 -08:00
|
|
|
DumpJson {
|
|
|
|
serde_json_error: serde_json::Error,
|
|
|
|
},
|
2021-07-26 01:26:06 -07:00
|
|
|
EditorInvoke {
|
2021-09-16 06:44:40 -07:00
|
|
|
editor: OsString,
|
2021-07-26 01:26:06 -07:00
|
|
|
io_error: io::Error,
|
|
|
|
},
|
|
|
|
EditorStatus {
|
|
|
|
editor: OsString,
|
|
|
|
status: ExitStatus,
|
|
|
|
},
|
|
|
|
EvalUnknownVariable {
|
2021-09-16 06:44:40 -07:00
|
|
|
variable: String,
|
2021-07-26 01:26:06 -07:00
|
|
|
suggestion: Option<Suggestion<'src>>,
|
|
|
|
},
|
2021-10-31 21:27:59 -07:00
|
|
|
FormatCheckFoundDiff,
|
2021-07-26 01:26:06 -07:00
|
|
|
FunctionCall {
|
|
|
|
function: Name<'src>,
|
2021-09-16 06:44:40 -07:00
|
|
|
message: String,
|
2021-07-26 01:26:06 -07:00
|
|
|
},
|
2023-01-12 19:25:28 -08:00
|
|
|
IncludeMissingPath {
|
|
|
|
file: PathBuf,
|
|
|
|
line: usize,
|
|
|
|
},
|
2021-07-26 01:26:06 -07:00
|
|
|
InitExists {
|
|
|
|
justfile: PathBuf,
|
|
|
|
},
|
|
|
|
Internal {
|
|
|
|
message: String,
|
|
|
|
},
|
2023-01-12 19:25:28 -08:00
|
|
|
InvalidDirective {
|
|
|
|
line: String,
|
|
|
|
},
|
2021-07-26 01:26:06 -07:00
|
|
|
Io {
|
2021-09-16 06:44:40 -07:00
|
|
|
recipe: &'src str,
|
2021-07-26 01:26:06 -07:00
|
|
|
io_error: io::Error,
|
|
|
|
},
|
|
|
|
Load {
|
2021-09-16 06:44:40 -07:00
|
|
|
path: PathBuf,
|
2021-07-26 01:26:06 -07:00
|
|
|
io_error: io::Error,
|
|
|
|
},
|
|
|
|
NoChoosableRecipes,
|
|
|
|
NoRecipes,
|
2021-09-16 16:45:56 -07:00
|
|
|
RegexCompile {
|
|
|
|
source: regex::Error,
|
|
|
|
},
|
2021-07-26 01:26:06 -07:00
|
|
|
Search {
|
|
|
|
search_error: SearchError,
|
|
|
|
},
|
|
|
|
Shebang {
|
2021-09-16 06:44:40 -07:00
|
|
|
recipe: &'src str,
|
|
|
|
command: String,
|
2021-07-26 01:26:06 -07:00
|
|
|
argument: Option<String>,
|
|
|
|
io_error: io::Error,
|
|
|
|
},
|
|
|
|
Signal {
|
2021-09-16 06:44:40 -07:00
|
|
|
recipe: &'src str,
|
2021-07-26 01:26:06 -07:00
|
|
|
line_number: Option<usize>,
|
2021-09-16 06:44:40 -07:00
|
|
|
signal: i32,
|
2021-07-26 01:26:06 -07:00
|
|
|
},
|
|
|
|
TmpdirIo {
|
2021-09-16 06:44:40 -07:00
|
|
|
recipe: &'src str,
|
2021-07-26 01:26:06 -07:00
|
|
|
io_error: io::Error,
|
|
|
|
},
|
|
|
|
Unknown {
|
2021-09-16 06:44:40 -07:00
|
|
|
recipe: &'src str,
|
2021-07-26 01:26:06 -07:00
|
|
|
line_number: Option<usize>,
|
|
|
|
},
|
|
|
|
UnknownOverrides {
|
|
|
|
overrides: Vec<String>,
|
|
|
|
},
|
|
|
|
UnknownRecipes {
|
2021-09-16 06:44:40 -07:00
|
|
|
recipes: Vec<String>,
|
2021-07-26 01:26:06 -07:00
|
|
|
suggestion: Option<Suggestion<'src>>,
|
|
|
|
},
|
|
|
|
Unstable {
|
|
|
|
message: String,
|
|
|
|
},
|
|
|
|
WriteJustfile {
|
|
|
|
justfile: PathBuf,
|
|
|
|
io_error: io::Error,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'src> Error<'src> {
|
|
|
|
pub(crate) fn code(&self) -> Option<i32> {
|
|
|
|
match self {
|
|
|
|
Self::Code { code, .. }
|
|
|
|
| Self::Backtick {
|
|
|
|
output_error: OutputError::Code(code),
|
|
|
|
..
|
|
|
|
} => Some(*code),
|
|
|
|
Self::ChooserStatus { status, .. } | Self::EditorStatus { status, .. } => status.code(),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn context(&self) -> Option<Token<'src>> {
|
|
|
|
match self {
|
|
|
|
Self::Backtick { token, .. } => Some(*token),
|
|
|
|
Self::Compile { compile_error } => Some(compile_error.context()),
|
|
|
|
Self::FunctionCall { function, .. } => Some(function.token()),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn internal(message: impl Into<String>) -> Self {
|
|
|
|
Self::Internal {
|
|
|
|
message: message.into(),
|
|
|
|
}
|
|
|
|
}
|
2022-10-25 16:32:36 -07:00
|
|
|
|
2022-10-31 00:52:03 -07:00
|
|
|
pub(crate) fn print_message(&self) -> bool {
|
|
|
|
!matches!(
|
2022-10-25 16:32:36 -07:00
|
|
|
self,
|
|
|
|
Error::Code {
|
2022-10-31 00:52:03 -07:00
|
|
|
print_message: false,
|
2022-10-25 16:32:36 -07:00
|
|
|
..
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'src> From<CompileError<'src>> for Error<'src> {
|
|
|
|
fn from(compile_error: CompileError<'src>) -> Self {
|
|
|
|
Self::Compile { compile_error }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'src> From<ConfigError> for Error<'src> {
|
|
|
|
fn from(config_error: ConfigError) -> Self {
|
|
|
|
Self::Config { config_error }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-14 20:32:27 -08:00
|
|
|
impl<'src> From<dotenvy::Error> for Error<'src> {
|
|
|
|
fn from(dotenv_error: dotenvy::Error) -> Error<'src> {
|
2021-07-26 01:26:06 -07:00
|
|
|
Self::Dotenv { dotenv_error }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'src> From<SearchError> for Error<'src> {
|
|
|
|
fn from(search_error: SearchError) -> Self {
|
|
|
|
Self::Search { search_error }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-28 18:06:57 -07:00
|
|
|
impl<'src> ColorDisplay for Error<'src> {
|
|
|
|
fn fmt(&self, f: &mut Formatter, color: Color) -> fmt::Result {
|
2021-07-26 01:26:06 -07:00
|
|
|
use Error::*;
|
|
|
|
|
2021-07-28 18:06:57 -07:00
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"{}: {}",
|
|
|
|
color.error().paint("error"),
|
|
|
|
color.message().prefix()
|
|
|
|
)?;
|
|
|
|
|
2021-07-26 01:26:06 -07:00
|
|
|
match self {
|
|
|
|
ArgumentCountMismatch {
|
|
|
|
recipe,
|
|
|
|
found,
|
|
|
|
min,
|
|
|
|
max,
|
2021-07-28 18:27:47 -07:00
|
|
|
..
|
2021-09-16 06:44:40 -07:00
|
|
|
} => {
|
2021-07-26 01:26:06 -07:00
|
|
|
if min == max {
|
|
|
|
let expected = min;
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Recipe `{}` got {} {} but {}takes {}",
|
|
|
|
recipe,
|
|
|
|
found,
|
|
|
|
Count("argument", *found),
|
|
|
|
if expected < found { "only " } else { "" },
|
|
|
|
expected
|
|
|
|
)?;
|
|
|
|
} else if found < min {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Recipe `{}` got {} {} but takes at least {}",
|
|
|
|
recipe,
|
|
|
|
found,
|
|
|
|
Count("argument", *found),
|
|
|
|
min
|
|
|
|
)?;
|
|
|
|
} else if found > max {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Recipe `{}` got {} {} but takes at most {}",
|
|
|
|
recipe,
|
|
|
|
found,
|
|
|
|
Count("argument", *found),
|
|
|
|
max
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
Backtick { output_error, .. } => match output_error {
|
|
|
|
OutputError::Code(code) => {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "Backtick failed with exit code {code}")?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
OutputError::Signal(signal) => {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "Backtick was terminated by signal {signal}")?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
OutputError::Unknown => {
|
|
|
|
write!(f, "Backtick failed for an unknown reason")?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
OutputError::Io(io_error) => {
|
|
|
|
match io_error.kind() {
|
|
|
|
io::ErrorKind::NotFound => write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Backtick could not be run because just could not find the shell:\n{io_error}"
|
2021-07-26 01:26:06 -07:00
|
|
|
),
|
|
|
|
io::ErrorKind::PermissionDenied => write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Backtick could not be run because just could not run the shell:\n{io_error}"
|
2021-07-26 01:26:06 -07:00
|
|
|
),
|
|
|
|
_ => write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Backtick could not be run because of an IO error while launching the shell:\n{io_error}"
|
2021-07-26 01:26:06 -07:00
|
|
|
),
|
|
|
|
}?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
OutputError::Utf8(utf8_error) => {
|
|
|
|
write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Backtick succeeded but stdout was not utf8: {utf8_error}"
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
},
|
|
|
|
ChooserInvoke {
|
|
|
|
shell_binary,
|
|
|
|
shell_arguments,
|
|
|
|
chooser,
|
|
|
|
io_error,
|
|
|
|
} => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Chooser `{} {} {}` invocation failed: {}",
|
|
|
|
shell_binary,
|
|
|
|
shell_arguments,
|
|
|
|
chooser.to_string_lossy(),
|
|
|
|
io_error,
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
ChooserRead { chooser, io_error } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Failed to read output from chooser `{}`: {}",
|
|
|
|
chooser.to_string_lossy(),
|
|
|
|
io_error
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
ChooserStatus { chooser, status } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Chooser `{}` failed: {}",
|
|
|
|
chooser.to_string_lossy(),
|
|
|
|
status
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
ChooserWrite { chooser, io_error } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Failed to write to chooser `{}`: {}",
|
|
|
|
chooser.to_string_lossy(),
|
|
|
|
io_error
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2023-01-12 19:25:28 -08:00
|
|
|
CircularInclude { current, include } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Include `{}` in `{}` is a circular include", include.display(), current.display()
|
|
|
|
)?;
|
|
|
|
},
|
2021-07-26 01:26:06 -07:00
|
|
|
Code {
|
|
|
|
recipe,
|
|
|
|
line_number,
|
|
|
|
code,
|
2022-10-25 16:32:36 -07:00
|
|
|
..
|
2021-09-16 06:44:40 -07:00
|
|
|
} => {
|
2021-07-26 01:26:06 -07:00
|
|
|
if let Some(n) = line_number {
|
|
|
|
write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Recipe `{recipe}` failed on line {n} with exit code {code}"
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
|
|
|
} else {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "Recipe `{recipe}` failed with exit code {code}")?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
CommandInvoke {
|
|
|
|
binary,
|
|
|
|
arguments,
|
|
|
|
io_error,
|
|
|
|
} => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Failed to invoke {}: {}",
|
|
|
|
iter::once(binary)
|
|
|
|
.chain(arguments)
|
|
|
|
.map(|value| Enclosure::tick(value.to_string_lossy()).to_string())
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.join(" "),
|
|
|
|
io_error,
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
CommandStatus {
|
|
|
|
binary,
|
|
|
|
arguments,
|
|
|
|
status,
|
|
|
|
} => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Command {} failed: {}",
|
|
|
|
iter::once(binary)
|
|
|
|
.chain(arguments)
|
|
|
|
.map(|value| Enclosure::tick(value.to_string_lossy()).to_string())
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.join(" "),
|
|
|
|
status,
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
Compile { compile_error } => Display::fmt(compile_error, f)?,
|
|
|
|
Config { config_error } => Display::fmt(config_error, f)?,
|
|
|
|
Cygpath {
|
|
|
|
recipe,
|
|
|
|
output_error,
|
|
|
|
} => match output_error {
|
|
|
|
OutputError::Code(code) => {
|
|
|
|
write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Cygpath failed with exit code {code} while translating recipe `{recipe}` shebang interpreter \
|
|
|
|
path"
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
OutputError::Signal(signal) => {
|
|
|
|
write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Cygpath terminated by signal {signal} while translating recipe `{recipe}` shebang interpreter \
|
|
|
|
path"
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
OutputError::Unknown => {
|
|
|
|
write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Cygpath experienced an unknown failure while translating recipe `{recipe}` shebang \
|
|
|
|
interpreter path"
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
OutputError::Io(io_error) => {
|
|
|
|
match io_error.kind() {
|
|
|
|
io::ErrorKind::NotFound => write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Could not find `cygpath` executable to translate recipe `{recipe}` shebang interpreter \
|
|
|
|
path:\n{io_error}"
|
2021-07-26 01:26:06 -07:00
|
|
|
),
|
|
|
|
io::ErrorKind::PermissionDenied => write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Could not run `cygpath` executable to translate recipe `{recipe}` shebang interpreter \
|
|
|
|
path:\n{io_error}"
|
2021-07-26 01:26:06 -07:00
|
|
|
),
|
2022-12-15 16:53:21 -08:00
|
|
|
_ => write!(f, "Could not run `cygpath` executable:\n{io_error}"),
|
2021-07-26 01:26:06 -07:00
|
|
|
}?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
OutputError::Utf8(utf8_error) => {
|
|
|
|
write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Cygpath successfully translated recipe `{recipe}` shebang interpreter path, but output was \
|
|
|
|
not utf8: {utf8_error}"
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
},
|
|
|
|
DefaultRecipeRequiresArguments {
|
|
|
|
recipe,
|
|
|
|
min_arguments,
|
|
|
|
} => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Recipe `{}` cannot be used as default recipe since it requires at least {} {}.",
|
|
|
|
recipe,
|
|
|
|
min_arguments,
|
|
|
|
Count("argument", *min_arguments),
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
Dotenv { dotenv_error } => {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "Failed to load environment file: {dotenv_error}")?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-11-17 00:07:48 -08:00
|
|
|
DumpJson { serde_json_error } => {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "Failed to dump JSON to stdout: {serde_json_error}")?;
|
2021-11-17 00:07:48 -08:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
EditorInvoke { editor, io_error } => {
|
|
|
|
write!(
|
|
|
|
f,
|
2022-12-15 16:53:21 -08:00
|
|
|
"Editor `{}` invocation failed: {io_error}",
|
2021-07-26 01:26:06 -07:00
|
|
|
editor.to_string_lossy(),
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
EditorStatus { editor, status } => {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "Editor `{}` failed: {status}", editor.to_string_lossy(),)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
EvalUnknownVariable {
|
|
|
|
variable,
|
|
|
|
suggestion,
|
|
|
|
} => {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "Justfile does not contain variable `{variable}`.")?;
|
2021-07-26 01:26:06 -07:00
|
|
|
if let Some(suggestion) = *suggestion {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "\n{suggestion}")?;
|
2021-07-26 01:26:06 -07:00
|
|
|
}
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-10-31 21:27:59 -07:00
|
|
|
FormatCheckFoundDiff => {
|
|
|
|
write!(f, "Formatted justfile differs from original.")?;
|
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
FunctionCall { function, message } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Call to function `{}` failed: {}",
|
|
|
|
function.lexeme(),
|
|
|
|
message
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2023-01-12 19:25:28 -08:00
|
|
|
IncludeMissingPath {
|
|
|
|
file: justfile, line
|
|
|
|
} => {
|
|
|
|
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"!include directive on line {} of `{}` has no argument",
|
|
|
|
line.ordinal(),
|
|
|
|
justfile.display(),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
},
|
2021-07-26 01:26:06 -07:00
|
|
|
InitExists { justfile } => {
|
|
|
|
write!(f, "Justfile `{}` already exists", justfile.display())?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
Internal { message } => {
|
|
|
|
write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Internal runtime error, this may indicate a bug in just: {message} \
|
|
|
|
consider filing an issue: https://github.com/casey/just/issues/new"
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2023-01-12 19:25:28 -08:00
|
|
|
InvalidDirective { line } => {
|
|
|
|
write!(f, "Invalid directive: {line}")?;
|
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
Io { recipe, io_error } => {
|
|
|
|
match io_error.kind() {
|
|
|
|
io::ErrorKind::NotFound => write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Recipe `{recipe}` could not be run because just could not find the shell: {io_error}"
|
2021-07-26 01:26:06 -07:00
|
|
|
),
|
|
|
|
io::ErrorKind::PermissionDenied => write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Recipe `{recipe}` could not be run because just could not run the shell: {io_error}"
|
2021-07-26 01:26:06 -07:00
|
|
|
),
|
|
|
|
_ => write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"Recipe `{recipe}` could not be run because of an IO error while launching the shell: {io_error}"
|
2021-07-26 01:26:06 -07:00
|
|
|
),
|
|
|
|
}?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
Load { io_error, path } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Failed to read justfile at `{}`: {}",
|
|
|
|
path.display(),
|
|
|
|
io_error
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
NoChoosableRecipes => {
|
|
|
|
write!(f, "Justfile contains no choosable recipes.")?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
NoRecipes => {
|
|
|
|
write!(f, "Justfile contains no recipes.")?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-09-16 16:45:56 -07:00
|
|
|
RegexCompile { source } => {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "{source}")?;
|
2021-09-16 16:45:56 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
Search { search_error } => Display::fmt(search_error, f)?,
|
|
|
|
Shebang {
|
|
|
|
recipe,
|
|
|
|
command,
|
|
|
|
argument,
|
|
|
|
io_error,
|
2021-09-16 06:44:40 -07:00
|
|
|
} => {
|
2021-07-26 01:26:06 -07:00
|
|
|
if let Some(argument) = argument {
|
|
|
|
write!(
|
|
|
|
f,
|
2022-12-15 16:53:21 -08:00
|
|
|
"Recipe `{recipe}` with shebang `#!{command} {argument}` execution error: {io_error}",
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
|
|
|
} else {
|
|
|
|
write!(
|
|
|
|
f,
|
2022-12-15 16:53:21 -08:00
|
|
|
"Recipe `{recipe}` with shebang `#!{command}` execution error: {io_error}",
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
Signal {
|
|
|
|
recipe,
|
|
|
|
line_number,
|
|
|
|
signal,
|
2021-09-16 06:44:40 -07:00
|
|
|
} => {
|
2021-07-26 01:26:06 -07:00
|
|
|
if let Some(n) = line_number {
|
|
|
|
write!(
|
|
|
|
f,
|
2022-12-15 16:53:21 -08:00
|
|
|
"Recipe `{recipe}` was terminated on line {n} by signal {signal}",
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
|
|
|
} else {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "Recipe `{recipe}` was terminated by signal {signal}")?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
TmpdirIo { recipe, io_error } => write!(
|
|
|
|
f,
|
2022-12-15 16:53:21 -08:00
|
|
|
"Recipe `{recipe}` could not be run because of an IO error while trying to create a temporary \
|
|
|
|
directory or write a file to that directory`:{io_error}",
|
2021-07-26 01:26:06 -07:00
|
|
|
)?,
|
|
|
|
Unknown {
|
|
|
|
recipe,
|
|
|
|
line_number,
|
2021-09-16 06:44:40 -07:00
|
|
|
} => {
|
2021-07-26 01:26:06 -07:00
|
|
|
if let Some(n) = line_number {
|
|
|
|
write!(
|
|
|
|
f,
|
2022-12-15 16:53:21 -08:00
|
|
|
"Recipe `{recipe}` failed on line {n} for an unknown reason",
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
|
|
|
} else {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "Recipe `{recipe}` failed for an unknown reason")?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
UnknownOverrides { overrides } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"{} {} overridden on the command line but not present in justfile",
|
|
|
|
Count("Variable", overrides.len()),
|
|
|
|
List::and_ticked(overrides),
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
UnknownRecipes {
|
|
|
|
recipes,
|
|
|
|
suggestion,
|
|
|
|
} => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Justfile does not contain {} {}.",
|
|
|
|
Count("recipe", recipes.len()),
|
|
|
|
List::or_ticked(recipes),
|
|
|
|
)?;
|
|
|
|
if let Some(suggestion) = *suggestion {
|
2022-12-15 16:53:21 -08:00
|
|
|
write!(f, "\n{suggestion}")?;
|
2021-07-26 01:26:06 -07:00
|
|
|
}
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
Unstable { message } => {
|
|
|
|
write!(
|
|
|
|
f,
|
2023-01-26 18:49:03 -08:00
|
|
|
"{message} Invoke `just` with the `--unstable` flag to enable unstable features."
|
2021-07-26 01:26:06 -07:00
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
WriteJustfile { justfile, io_error } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Failed to write justfile to `{}`: {}",
|
|
|
|
justfile.display(),
|
|
|
|
io_error
|
|
|
|
)?;
|
2021-09-16 06:44:40 -07:00
|
|
|
}
|
2021-07-26 01:26:06 -07:00
|
|
|
}
|
|
|
|
|
2021-07-28 18:06:57 -07:00
|
|
|
write!(f, "{}", color.message().suffix())?;
|
|
|
|
|
2021-07-28 18:27:47 -07:00
|
|
|
if let ArgumentCountMismatch {
|
|
|
|
recipe, parameters, ..
|
|
|
|
} = self
|
|
|
|
{
|
|
|
|
writeln!(f)?;
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"{}:\n just {}",
|
|
|
|
color.message().paint("usage"),
|
|
|
|
recipe
|
|
|
|
)?;
|
|
|
|
for param in parameters {
|
|
|
|
write!(f, " {}", param.color_display(color))?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-28 18:06:57 -07:00
|
|
|
if let Some(token) = self.context() {
|
|
|
|
writeln!(f)?;
|
|
|
|
write!(f, "{}", token.color_display(color.error()))?;
|
|
|
|
}
|
|
|
|
|
2021-07-26 01:26:06 -07:00
|
|
|
Ok(())
|
Gargantuan refactor (#522)
- Instead of changing the current directory with `env::set_current_dir`
to be implicitly inherited by subprocesses, we now use
`Command::current_dir` to set it explicitly. This feels much better,
since we aren't dependent on the implicit state of the process's
current directory.
- Subcommand execution is much improved.
- Added a ton of tests for config parsing, config execution, working
dir, and search dir.
- Error messages are improved. Many more will be colored.
- The Config is now onwed, instead of borrowing from the arguments and
the `clap::ArgMatches` object. This is a huge ergonomic improvement,
especially in tests, and I don't think anyone will notice.
- `--edit` now uses `$VISUAL`, `$EDITOR`, or `vim`, in that order,
matching git, which I think is what most people will expect.
- Added a cute `tmptree!{}` macro, for creating temporary directories
populated with directories and files for tests.
- Admitted that grammer is LL(k) and I don't know what `k` is.
2019-11-09 21:43:20 -08:00
|
|
|
}
|
|
|
|
}
|