Refactor run::run and Config (#490)

Improve color parsing, add `ConfigError`, put `invocation_directory` on Config object, return error code from `run::run` instead of exiting.
This commit is contained in:
Casey Rodarmor 2019-10-09 00:18:53 -07:00 committed by GitHub
parent 64ed94c8ac
commit ca4f2a44ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 176 additions and 117 deletions

View File

@ -167,14 +167,10 @@ mod test {
use super::*; use super::*;
use crate::testing::parse; use crate::testing::parse;
fn no_cwd_err() -> Result<PathBuf, String> {
Err(String::from("no cwd in tests"))
}
#[test] #[test]
fn backtick_code() { fn backtick_code() {
match parse("a:\n echo {{`f() { return 100; }; f`}}") match parse("a:\n echo {{`f() { return 100; }; f`}}")
.run(&no_cwd_err(), &["a"], &Default::default()) .run(&["a"], &Default::default())
.unwrap_err() .unwrap_err()
{ {
RuntimeError::Backtick { RuntimeError::Backtick {
@ -202,10 +198,7 @@ recipe:
..Default::default() ..Default::default()
}; };
match parse(text) match parse(text).run(&["recipe"], &config).unwrap_err() {
.run(&no_cwd_err(), &["recipe"], &config)
.unwrap_err()
{
RuntimeError::Backtick { RuntimeError::Backtick {
token, token,
output_error: OutputError::Code(_), output_error: OutputError::Code(_),

View File

@ -3,7 +3,9 @@ pub(crate) use std::{
borrow::Cow, borrow::Cow,
cmp, cmp,
collections::{BTreeMap, BTreeSet}, collections::{BTreeMap, BTreeSet},
convert::AsRef,
env, env,
ffi::OsStr,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
fs, io, iter, fs, io, iter,
ops::{Range, RangeInclusive}, ops::{Range, RangeInclusive},
@ -16,7 +18,7 @@ pub(crate) use std::{
// dependencies // dependencies
pub(crate) use edit_distance::edit_distance; pub(crate) use edit_distance::edit_distance;
pub(crate) use libc::{EXIT_FAILURE, EXIT_SUCCESS}; pub(crate) use libc::EXIT_FAILURE;
pub(crate) use log::warn; pub(crate) use log::warn;
pub(crate) use unicode_width::UnicodeWidthChar; pub(crate) use unicode_width::UnicodeWidthChar;
@ -38,21 +40,23 @@ pub(crate) use crate::{
pub(crate) use crate::{ pub(crate) use crate::{
alias::Alias, alias_resolver::AliasResolver, assignment_evaluator::AssignmentEvaluator, alias::Alias, alias_resolver::AliasResolver, assignment_evaluator::AssignmentEvaluator,
assignment_resolver::AssignmentResolver, color::Color, compilation_error::CompilationError, assignment_resolver::AssignmentResolver, color::Color, compilation_error::CompilationError,
compilation_error_kind::CompilationErrorKind, config::Config, expression::Expression, compilation_error_kind::CompilationErrorKind, config::Config, config_error::ConfigError,
fragment::Fragment, function::Function, function_context::FunctionContext, functions::Functions, expression::Expression, fragment::Fragment, function::Function,
interrupt_guard::InterruptGuard, interrupt_handler::InterruptHandler, justfile::Justfile, function_context::FunctionContext, functions::Functions, interrupt_guard::InterruptGuard,
lexer::Lexer, output_error::OutputError, parameter::Parameter, parser::Parser, interrupt_handler::InterruptHandler, justfile::Justfile, lexer::Lexer, output_error::OutputError,
platform::Platform, position::Position, recipe::Recipe, recipe_context::RecipeContext, parameter::Parameter, parser::Parser, platform::Platform, position::Position, recipe::Recipe,
recipe_resolver::RecipeResolver, runtime_error::RuntimeError, search_error::SearchError, recipe_context::RecipeContext, recipe_resolver::RecipeResolver, runtime_error::RuntimeError,
shebang::Shebang, state::State, string_literal::StringLiteral, subcommand::Subcommand, search_error::SearchError, shebang::Shebang, state::State, string_literal::StringLiteral,
token::Token, token_kind::TokenKind, use_color::UseColor, variables::Variables, subcommand::Subcommand, token::Token, token_kind::TokenKind, use_color::UseColor,
verbosity::Verbosity, warning::Warning, variables::Variables, verbosity::Verbosity, warning::Warning,
}; };
pub(crate) type CompilationResult<'a, T> = Result<T, CompilationError<'a>>; pub(crate) type CompilationResult<'a, T> = Result<T, CompilationError<'a>>;
pub(crate) type RunResult<'a, T> = Result<T, RuntimeError<'a>>; pub(crate) type RunResult<'a, T> = Result<T, RuntimeError<'a>>;
pub(crate) type ConfigResult<T> = Result<T, ConfigError>;
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use std::io::prelude::*; pub(crate) use std::io::prelude::*;

View File

@ -15,14 +15,24 @@ pub(crate) struct Config<'a> {
pub(crate) color: Color, pub(crate) color: Color,
pub(crate) verbosity: Verbosity, pub(crate) verbosity: Verbosity,
pub(crate) arguments: Vec<&'a str>, pub(crate) arguments: Vec<&'a str>,
pub(crate) justfile: Option<&'a Path>,
pub(crate) working_directory: Option<&'a Path>,
pub(crate) invocation_directory: Result<PathBuf, String>,
} }
mod arg { mod arg {
pub(crate) const EDIT: &str = "EDIT";
pub(crate) const SUMMARY: &str = "SUMMARY";
pub(crate) const DUMP: &str = "DUMP"; pub(crate) const DUMP: &str = "DUMP";
pub(crate) const COLOR: &str = "COLOR";
pub(crate) const EDIT: &str = "EDIT";
pub(crate) const LIST: &str = "LIST"; pub(crate) const LIST: &str = "LIST";
pub(crate) const SHOW: &str = "SHOW"; pub(crate) const SHOW: &str = "SHOW";
pub(crate) const SUMMARY: &str = "SUMMARY";
pub(crate) const WORKING_DIRECTORY: &str = "WORKING-DIRECTORY";
pub(crate) const COLOR_AUTO: &str = "auto";
pub(crate) const COLOR_ALWAYS: &str = "always";
pub(crate) const COLOR_NEVER: &str = "never";
pub(crate) const COLOR_VALUES: &[&str] = &[COLOR_AUTO, COLOR_ALWAYS, COLOR_NEVER];
} }
impl<'a> Config<'a> { impl<'a> Config<'a> {
@ -38,11 +48,11 @@ impl<'a> Config<'a> {
.help("The recipe(s) to run, defaults to the first recipe in the justfile"), .help("The recipe(s) to run, defaults to the first recipe in the justfile"),
) )
.arg( .arg(
Arg::with_name("COLOR") Arg::with_name(arg::COLOR)
.long("color") .long("color")
.takes_value(true) .takes_value(true)
.possible_values(&["auto", "always", "never"]) .possible_values(arg::COLOR_VALUES)
.default_value("auto") .default_value(arg::COLOR_AUTO)
.help("Print colorful output"), .help("Print colorful output"),
) )
.arg( .arg(
@ -129,7 +139,7 @@ impl<'a> Config<'a> {
.help("Use verbose output"), .help("Use verbose output"),
) )
.arg( .arg(
Arg::with_name("WORKING-DIRECTORY") Arg::with_name(arg::WORKING_DIRECTORY)
.short("d") .short("d")
.long("working-directory") .long("working-directory")
.takes_value(true) .takes_value(true)
@ -164,18 +174,28 @@ impl<'a> Config<'a> {
} }
} }
pub(crate) fn from_matches(matches: &'a ArgMatches<'a>) -> Config<'a> { fn color_from_value(value: &str) -> ConfigResult<Color> {
match value {
arg::COLOR_AUTO => Ok(Color::auto()),
arg::COLOR_ALWAYS => Ok(Color::always()),
arg::COLOR_NEVER => Ok(Color::never()),
_ => Err(ConfigError::Internal {
message: format!("Invalid argument `{}` to --color.", value),
}),
}
}
pub(crate) fn from_matches(matches: &'a ArgMatches<'a>) -> ConfigResult<Config<'a>> {
let invocation_directory =
env::current_dir().map_err(|e| format!("Error getting current directory: {}", e));
let verbosity = Verbosity::from_flag_occurrences(matches.occurrences_of("VERBOSE")); let verbosity = Verbosity::from_flag_occurrences(matches.occurrences_of("VERBOSE"));
let color = match matches.value_of("COLOR").expect("`--color` had no value") { let color = Self::color_from_value(
"auto" => Color::auto(), matches
"always" => Color::always(), .value_of(arg::COLOR)
"never" => Color::never(), .expect("`--color` had no value"),
other => die!( )?;
"Invalid argument `{}` to --color. This is a bug in just.",
other
),
};
let set_count = matches.occurrences_of("SET"); let set_count = matches.occurrences_of("SET");
let mut overrides = BTreeMap::new(); let mut overrides = BTreeMap::new();
@ -216,7 +236,7 @@ impl<'a> Config<'a> {
.flat_map(|(i, argument)| { .flat_map(|(i, argument)| {
if i == 0 { if i == 0 {
if let Some(i) = argument.rfind('/') { if let Some(i) = argument.rfind('/') {
if matches.is_present("WORKING-DIRECTORY") { if matches.is_present(arg::WORKING_DIRECTORY) {
die!("--working-directory and a path prefixed recipe may not be used together."); die!("--working-directory and a path prefixed recipe may not be used together.");
} }
@ -252,18 +272,21 @@ impl<'a> Config<'a> {
Subcommand::Run Subcommand::Run
}; };
Config { Ok(Config {
dry_run: matches.is_present("DRY-RUN"), dry_run: matches.is_present("DRY-RUN"),
evaluate: matches.is_present("EVALUATE"), evaluate: matches.is_present("EVALUATE"),
highlight: matches.is_present("HIGHLIGHT"), highlight: matches.is_present("HIGHLIGHT"),
quiet: matches.is_present("QUIET"), quiet: matches.is_present("QUIET"),
shell: matches.value_of("SHELL").unwrap(), shell: matches.value_of("SHELL").unwrap(),
justfile: matches.value_of("JUSTFILE").map(Path::new),
working_directory: matches.value_of("WORKING-DIRECTORY").map(Path::new),
invocation_directory,
subcommand, subcommand,
verbosity, verbosity,
color, color,
overrides, overrides,
arguments, arguments,
} })
} }
} }
@ -280,6 +303,10 @@ impl<'a> Default for Config<'a> {
shell: DEFAULT_SHELL, shell: DEFAULT_SHELL,
color: default(), color: default(),
verbosity: Verbosity::from_flag_occurrences(0), verbosity: Verbosity::from_flag_occurrences(0),
justfile: None,
working_directory: None,
invocation_directory: env::current_dir()
.map_err(|e| format!("Error getting current directory: {}", e)),
} }
} }
} }

20
src/config_error.rs Normal file
View File

@ -0,0 +1,20 @@
use crate::common::*;
pub(crate) enum ConfigError {
Internal { message: String },
}
impl Display for ConfigError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use ConfigError::*;
match self {
Internal { message } => write!(
f,
"Internal config error, this may indicate a bug in just: {} \
consider filing an issue: https://github.com/casey/just/issues/new",
message
),
}
}
}

View File

@ -43,12 +43,7 @@ impl<'a> Justfile<'a> {
None None
} }
pub(crate) fn run( pub(crate) fn run(&'a self, arguments: &[&'a str], config: &'a Config<'a>) -> RunResult<'a, ()> {
&'a self,
invocation_directory: &'a Result<PathBuf, String>,
arguments: &[&'a str],
config: &'a Config<'a>,
) -> RunResult<'a, ()> {
let unknown_overrides = config let unknown_overrides = config
.overrides .overrides
.keys() .keys()
@ -66,7 +61,7 @@ impl<'a> Justfile<'a> {
let scope = AssignmentEvaluator::evaluate_assignments( let scope = AssignmentEvaluator::evaluate_assignments(
&self.assignments, &self.assignments,
invocation_directory, &config.invocation_directory,
&dotenv, &dotenv,
&config.overrides, &config.overrides,
config.quiet, config.quiet,
@ -127,11 +122,7 @@ impl<'a> Justfile<'a> {
}); });
} }
let context = RecipeContext { let context = RecipeContext { config, scope };
invocation_directory,
config,
scope,
};
let mut ran = empty(); let mut ran = empty();
for (recipe, arguments) in grouped { for (recipe, arguments) in grouped {
@ -212,14 +203,10 @@ mod test {
use crate::runtime_error::RuntimeError::*; use crate::runtime_error::RuntimeError::*;
use crate::testing::parse; use crate::testing::parse;
fn no_cwd_err() -> Result<PathBuf, String> {
Err(String::from("no cwd in tests"))
}
#[test] #[test]
fn unknown_recipes() { fn unknown_recipes() {
match parse("a:\nb:\nc:") match parse("a:\nb:\nc:")
.run(&no_cwd_err(), &["a", "x", "y", "z"], &Default::default()) .run(&["a", "x", "y", "z"], &Default::default())
.unwrap_err() .unwrap_err()
{ {
UnknownRecipes { UnknownRecipes {
@ -251,10 +238,7 @@ a:
x x
"; ";
match parse(text) match parse(text).run(&["a"], &Default::default()).unwrap_err() {
.run(&no_cwd_err(), &["a"], &Default::default())
.unwrap_err()
{
Code { Code {
recipe, recipe,
line_number, line_number,
@ -271,7 +255,7 @@ a:
#[test] #[test]
fn code_error() { fn code_error() {
match parse("fail:\n @exit 100") match parse("fail:\n @exit 100")
.run(&no_cwd_err(), &["fail"], &Default::default()) .run(&["fail"], &Default::default())
.unwrap_err() .unwrap_err()
{ {
Code { Code {
@ -294,7 +278,7 @@ a return code:
@x() { {{return}} {{code + "0"}}; }; x"#; @x() { {{return}} {{code + "0"}}; }; x"#;
match parse(text) match parse(text)
.run(&no_cwd_err(), &["a", "return", "15"], &Default::default()) .run(&["a", "return", "15"], &Default::default())
.unwrap_err() .unwrap_err()
{ {
Code { Code {
@ -313,7 +297,7 @@ a return code:
#[test] #[test]
fn missing_some_arguments() { fn missing_some_arguments() {
match parse("a b c d:") match parse("a b c d:")
.run(&no_cwd_err(), &["a", "b", "c"], &Default::default()) .run(&["a", "b", "c"], &Default::default())
.unwrap_err() .unwrap_err()
{ {
ArgumentCountMismatch { ArgumentCountMismatch {
@ -337,7 +321,7 @@ a return code:
#[test] #[test]
fn missing_some_arguments_variadic() { fn missing_some_arguments_variadic() {
match parse("a b c +d:") match parse("a b c +d:")
.run(&no_cwd_err(), &["a", "B", "C"], &Default::default()) .run(&["a", "B", "C"], &Default::default())
.unwrap_err() .unwrap_err()
{ {
ArgumentCountMismatch { ArgumentCountMismatch {
@ -361,7 +345,7 @@ a return code:
#[test] #[test]
fn missing_all_arguments() { fn missing_all_arguments() {
match parse("a b c d:\n echo {{b}}{{c}}{{d}}") match parse("a b c d:\n echo {{b}}{{c}}{{d}}")
.run(&no_cwd_err(), &["a"], &Default::default()) .run(&["a"], &Default::default())
.unwrap_err() .unwrap_err()
{ {
ArgumentCountMismatch { ArgumentCountMismatch {
@ -385,7 +369,7 @@ a return code:
#[test] #[test]
fn missing_some_defaults() { fn missing_some_defaults() {
match parse("a b c d='hello':") match parse("a b c d='hello':")
.run(&no_cwd_err(), &["a", "b"], &Default::default()) .run(&["a", "b"], &Default::default())
.unwrap_err() .unwrap_err()
{ {
ArgumentCountMismatch { ArgumentCountMismatch {
@ -409,7 +393,7 @@ a return code:
#[test] #[test]
fn missing_all_defaults() { fn missing_all_defaults() {
match parse("a b c='r' d='h':") match parse("a b c='r' d='h':")
.run(&no_cwd_err(), &["a"], &Default::default()) .run(&["a"], &Default::default())
.unwrap_err() .unwrap_err()
{ {
ArgumentCountMismatch { ArgumentCountMismatch {
@ -436,7 +420,7 @@ a return code:
config.overrides.insert("foo", "bar"); config.overrides.insert("foo", "bar");
config.overrides.insert("baz", "bob"); config.overrides.insert("baz", "bob");
match parse("a:\n echo {{`f() { return 100; }; f`}}") match parse("a:\n echo {{`f() { return 100; }; f`}}")
.run(&no_cwd_err(), &["a"], &config) .run(&["a"], &config)
.unwrap_err() .unwrap_err()
{ {
UnknownOverrides { overrides } => { UnknownOverrides { overrides } => {
@ -463,10 +447,7 @@ wut:
..Default::default() ..Default::default()
}; };
match parse(text) match parse(text).run(&["wut"], &config).unwrap_err() {
.run(&no_cwd_err(), &["wut"], &config)
.unwrap_err()
{
Code { Code {
code: _, code: _,
line_number, line_number,

View File

@ -21,6 +21,7 @@ mod common;
mod compilation_error; mod compilation_error;
mod compilation_error_kind; mod compilation_error_kind;
mod config; mod config;
mod config_error;
mod expression; mod expression;
mod fragment; mod fragment;
mod function; mod function;

View File

@ -1,3 +1,5 @@
fn main() { fn main() {
just::run(); if let Err(code) = just::run() {
std::process::exit(code);
}
} }

View File

@ -82,7 +82,7 @@ impl<'a> Recipe<'a> {
assignments: &empty(), assignments: &empty(),
dry_run: config.dry_run, dry_run: config.dry_run,
evaluated: empty(), evaluated: empty(),
invocation_directory: context.invocation_directory, invocation_directory: &config.invocation_directory,
overrides: &empty(), overrides: &empty(),
quiet: config.quiet, quiet: config.quiet,
scope: &context.scope, scope: &context.scope,

View File

@ -1,7 +1,6 @@
use crate::common::*; use crate::common::*;
pub(crate) struct RecipeContext<'a> { pub(crate) struct RecipeContext<'a> {
pub(crate) invocation_directory: &'a Result<PathBuf, String>,
pub(crate) config: &'a Config<'a>, pub(crate) config: &'a Config<'a>,
pub(crate) scope: BTreeMap<&'a str, String>, pub(crate) scope: BTreeMap<&'a str, String>,
} }

View File

@ -1,27 +1,38 @@
use crate::common::*; use crate::common::*;
use crate::{interrupt_handler::InterruptHandler, misc::maybe_s}; use crate::{interrupt_handler::InterruptHandler, misc::maybe_s};
use std::{convert, ffi};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
#[cfg(windows)] fn edit<P: AsRef<OsStr>>(path: P) -> Result<(), i32> {
use ansi_term::enable_ansi_support; let editor = match env::var_os("EDITOR") {
None => {
fn edit<P: convert::AsRef<ffi::OsStr>>(path: P) -> ! { eprintln!("Error getting EDITOR environment variable");
let editor = return Err(EXIT_FAILURE);
env::var_os("EDITOR").unwrap_or_else(|| die!("Error getting EDITOR environment variable")); }
Some(editor) => editor,
};
let error = Command::new(editor).arg(path).status(); let error = Command::new(editor).arg(path).status();
match error { match error {
Ok(status) => process::exit(status.code().unwrap_or(EXIT_FAILURE)), Ok(status) => {
Err(error) => die!("Failed to invoke editor: {}", error), if status.success() {
Ok(())
} else {
eprintln!("Editor failed: {}", status);
Err(status.code().unwrap_or(EXIT_FAILURE))
}
}
Err(error) => {
eprintln!("Failed to invoke editor: {}", error);
Err(EXIT_FAILURE)
}
} }
} }
pub fn run() { pub fn run() -> Result<(), i32> {
#[cfg(windows)] #[cfg(windows)]
enable_ansi_support().ok(); ansi_term::enable_ansi_support().ok();
env_logger::Builder::from_env( env_logger::Builder::from_env(
env_logger::Env::new() env_logger::Env::new()
@ -30,18 +41,21 @@ pub fn run() {
) )
.init(); .init();
let invocation_directory =
env::current_dir().map_err(|e| format!("Error getting current directory: {}", e));
let app = Config::app(); let app = Config::app();
let matches = app.get_matches(); let matches = app.get_matches();
let config = Config::from_matches(&matches); let config = match Config::from_matches(&matches) {
Ok(config) => config,
Err(error) => {
eprintln!("error: {}", error);
return Err(EXIT_FAILURE);
}
};
let justfile = matches.value_of("JUSTFILE").map(Path::new); let justfile = config.justfile;
let mut working_directory = matches.value_of("WORKING-DIRECTORY").map(PathBuf::from); let mut working_directory = config.working_directory.map(PathBuf::from);
if let (Some(justfile), None) = (justfile, working_directory.as_ref()) { if let (Some(justfile), None) = (justfile, working_directory.as_ref()) {
let mut justfile = justfile.to_path_buf(); let mut justfile = justfile.to_path_buf();
@ -49,11 +63,14 @@ pub fn run() {
if !justfile.is_absolute() { if !justfile.is_absolute() {
match justfile.canonicalize() { match justfile.canonicalize() {
Ok(canonical) => justfile = canonical, Ok(canonical) => justfile = canonical,
Err(err) => die!( Err(err) => {
"Could not canonicalize justfile path `{}`: {}", eprintln!(
justfile.display(), "Could not canonicalize justfile path `{}`: {}",
err justfile.display(),
), err
);
return Err(EXIT_FAILURE);
}
} }
} }
@ -65,7 +82,7 @@ pub fn run() {
let text; let text;
if let (Some(justfile), Some(directory)) = (justfile, working_directory) { if let (Some(justfile), Some(directory)) = (justfile, working_directory) {
if config.subcommand == Subcommand::Edit { if config.subcommand == Subcommand::Edit {
edit(justfile); return edit(justfile);
} }
text = fs::read_to_string(justfile) text = fs::read_to_string(justfile)
@ -86,32 +103,45 @@ pub fn run() {
match search::justfile(&current_dir) { match search::justfile(&current_dir) {
Ok(name) => { Ok(name) => {
if config.subcommand == Subcommand::Edit { if config.subcommand == Subcommand::Edit {
edit(name); return edit(name);
} }
text = fs::read_to_string(&name) text = match fs::read_to_string(&name) {
.unwrap_or_else(|error| die!("Error reading justfile: {}", error)); Err(error) => {
eprintln!("Error reading justfile: {}", error);
return Err(EXIT_FAILURE);
}
Ok(text) => text,
};
let parent = name.parent().unwrap(); let parent = name.parent().unwrap();
if let Err(error) = env::set_current_dir(&parent) { if let Err(error) = env::set_current_dir(&parent) {
die!( eprintln!(
"Error changing directory to {}: {}", "Error changing directory to {}: {}",
parent.display(), parent.display(),
error error
); );
return Err(EXIT_FAILURE);
} }
} }
Err(search_error) => die!("{}", search_error), Err(search_error) => {
eprintln!("{}", search_error);
return Err(EXIT_FAILURE);
}
} }
} }
let justfile = Parser::parse(&text).unwrap_or_else(|error| { let justfile = match Parser::parse(&text) {
if config.color.stderr().active() { Err(error) => {
die!("{:#}", error); if config.color.stderr().active() {
} else { eprintln!("{:#}", error);
die!("{}", error); } else {
eprintln!("{}", error);
}
return Err(EXIT_FAILURE);
} }
}); Ok(justfile) => justfile,
};
for warning in &justfile.warnings { for warning in &justfile.warnings {
if config.color.stderr().active() { if config.color.stderr().active() {
@ -135,12 +165,12 @@ pub fn run() {
.join(" "); .join(" ");
println!("{}", summary); println!("{}", summary);
} }
process::exit(EXIT_SUCCESS); return Ok(());
} }
if config.subcommand == Subcommand::Dump { if config.subcommand == Subcommand::Dump {
println!("{}", justfile); println!("{}", justfile);
process::exit(EXIT_SUCCESS); return Ok(());
} }
if config.subcommand == Subcommand::List { if config.subcommand == Subcommand::List {
@ -227,7 +257,7 @@ pub fn run() {
} }
} }
process::exit(EXIT_SUCCESS); return Ok(());
} }
if let Subcommand::Show { name } = config.subcommand { if let Subcommand::Show { name } = config.subcommand {
@ -235,17 +265,17 @@ pub fn run() {
let recipe = justfile.get_recipe(alias.target).unwrap(); let recipe = justfile.get_recipe(alias.target).unwrap();
println!("{}", alias); println!("{}", alias);
println!("{}", recipe); println!("{}", recipe);
process::exit(EXIT_SUCCESS); return Ok(());
} }
if let Some(recipe) = justfile.get_recipe(name) { if let Some(recipe) = justfile.get_recipe(name) {
println!("{}", recipe); println!("{}", recipe);
process::exit(EXIT_SUCCESS); return Ok(());
} else { } else {
eprintln!("Justfile does not contain recipe `{}`.", name); eprintln!("Justfile does not contain recipe `{}`.", name);
if let Some(suggestion) = justfile.suggest(name) { if let Some(suggestion) = justfile.suggest(name) {
eprintln!("Did you mean `{}`?", suggestion); eprintln!("Did you mean `{}`?", suggestion);
} }
process::exit(EXIT_FAILURE) return Err(EXIT_FAILURE);
} }
} }
@ -270,7 +300,7 @@ pub fn run() {
warn!("Failed to set CTRL-C handler: {}", error) warn!("Failed to set CTRL-C handler: {}", error)
} }
if let Err(run_error) = justfile.run(&invocation_directory, &arguments, &config) { if let Err(run_error) = justfile.run(&arguments, &config) {
if !config.quiet { if !config.quiet {
if config.color.stderr().active() { if config.color.stderr().active() {
eprintln!("{:#}", run_error); eprintln!("{:#}", run_error);
@ -279,6 +309,8 @@ pub fn run() {
} }
} }
process::exit(run_error.code().unwrap_or(EXIT_FAILURE)); return Err(run_error.code().unwrap_or(EXIT_FAILURE));
} }
Ok(())
} }

View File

@ -377,7 +377,7 @@ impl<'a> Display for RuntimeError<'a> {
Internal { ref message } => { Internal { ref message } => {
write!( write!(
f, f,
"Internal error, this may indicate a bug in just: {} \ "Internal runtime error, this may indicate a bug in just: {} \
consider filing an issue: https://github.com/casey/just/issues/new", consider filing an issue: https://github.com/casey/just/issues/new",
message message
)?; )?;