Skip to content

Commit

Permalink
Add --init subcommand (#541)
Browse files Browse the repository at this point in the history
When `--init` is passed on the command line, search upward for the
project root, identified by the presence of a VCS directory like `.git`,
falling back to the current directory, and create a default justfile in
that directory.
  • Loading branch information
casey authored Nov 20, 2019
1 parent c4e9857 commit e948f11
Show file tree
Hide file tree
Showing 6 changed files with 387 additions and 65 deletions.
151 changes: 112 additions & 39 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use clap::{App, AppSettings, Arg, ArgGroup, ArgMatches};
use unicode_width::UnicodeWidthStr;

pub(crate) const DEFAULT_SHELL: &str = "sh";
pub(crate) const INIT_JUSTFILE: &str = "default:\n\techo 'Hello, world!'\n";

#[derive(Debug, PartialEq)]
pub(crate) struct Config {
Expand All @@ -22,12 +23,13 @@ mod cmd {
pub(crate) const DUMP: &str = "DUMP";
pub(crate) const EDIT: &str = "EDIT";
pub(crate) const EVALUATE: &str = "EVALUATE";
pub(crate) const INIT: &str = "INIT";
pub(crate) const LIST: &str = "LIST";
pub(crate) const SHOW: &str = "SHOW";
pub(crate) const SUMMARY: &str = "SUMMARY";

pub(crate) const ALL: &[&str] = &[DUMP, EDIT, LIST, SHOW, SUMMARY, EVALUATE];
pub(crate) const ARGLESS: &[&str] = &[DUMP, EDIT, LIST, SHOW, SUMMARY];
pub(crate) const ALL: &[&str] = &[DUMP, EDIT, INIT, EVALUATE, LIST, SHOW, SUMMARY];
pub(crate) const ARGLESS: &[&str] = &[DUMP, EDIT, INIT, LIST, SHOW, SUMMARY];
}

mod arg {
Expand Down Expand Up @@ -70,22 +72,6 @@ impl Config {
.help("Print what just would do without doing it")
.conflicts_with(arg::QUIET),
)
.arg(
Arg::with_name(cmd::DUMP)
.long("dump")
.help("Print entire justfile"),
)
.arg(
Arg::with_name(cmd::EDIT)
.short("e")
.long("edit")
.help("Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`"),
)
.arg(
Arg::with_name(cmd::EVALUATE)
.long("evaluate")
.help("Print evaluated variables"),
)
.arg(
Arg::with_name(arg::HIGHLIGHT)
.long("highlight")
Expand All @@ -105,12 +91,6 @@ impl Config {
.takes_value(true)
.help("Use <JUSTFILE> as justfile."),
)
.arg(
Arg::with_name(cmd::LIST)
.short("l")
.long("list")
.help("List available recipes and their arguments"),
)
.arg(
Arg::with_name(arg::QUIET)
.short("q")
Expand All @@ -134,19 +114,6 @@ impl Config {
.default_value(DEFAULT_SHELL)
.help("Invoke <SHELL> to run recipes"),
)
.arg(
Arg::with_name(cmd::SHOW)
.short("s")
.long("show")
.takes_value(true)
.value_name("RECIPE")
.help("Show information about <RECIPE>"),
)
.arg(
Arg::with_name(cmd::SUMMARY)
.long("summary")
.help("List names of available recipes"),
)
.arg(
Arg::with_name(arg::VERBOSE)
.short("v")
Expand All @@ -167,6 +134,46 @@ impl Config {
.multiple(true)
.help("Overrides and recipe(s) to run, defaulting to the first recipe in the justfile"),
)
.arg(
Arg::with_name(cmd::DUMP)
.long("dump")
.help("Print entire justfile"),
)
.arg(
Arg::with_name(cmd::EDIT)
.short("e")
.long("edit")
.help("Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`"),
)
.arg(
Arg::with_name(cmd::EVALUATE)
.long("evaluate")
.help("Print evaluated variables"),
)
.arg(
Arg::with_name(cmd::INIT)
.long("init")
.help("Initialize new justfile in project root"),
)
.arg(
Arg::with_name(cmd::LIST)
.short("l")
.long("list")
.help("List available recipes and their arguments"),
)
.arg(
Arg::with_name(cmd::SHOW)
.short("s")
.long("show")
.takes_value(true)
.value_name("RECIPE")
.help("Show information about <RECIPE>"),
)
.arg(
Arg::with_name(cmd::SUMMARY)
.long("summary")
.help("List names of available recipes"),
)
.group(ArgGroup::with_name("SUBCOMMAND").args(cmd::ALL));

if cfg!(feature = "help4help2man") {
Expand Down Expand Up @@ -288,13 +295,21 @@ impl Config {
Subcommand::Summary
} else if matches.is_present(cmd::DUMP) {
Subcommand::Dump
} else if matches.is_present(cmd::INIT) {
Subcommand::Init
} else if matches.is_present(cmd::LIST) {
Subcommand::List
} else if let Some(name) = matches.value_of(cmd::SHOW) {
Subcommand::Show {
name: name.to_owned(),
}
} else if matches.is_present(cmd::EVALUATE) {
if !positional.arguments.is_empty() {
return Err(ConfigError::SubcommandArguments {
subcommand: format!("--{}", cmd::EVALUATE.to_lowercase()),
arguments: positional.arguments,
});
}
Subcommand::Evaluate { overrides }
} else {
Subcommand::Run {
Expand All @@ -319,8 +334,12 @@ impl Config {
pub(crate) fn run_subcommand(self) -> Result<(), i32> {
use Subcommand::*;

if self.subcommand == Init {
return self.init();
}

let search =
Search::search(&self.search_config, &self.invocation_directory).eprint(self.color)?;
Search::find(&self.search_config, &self.invocation_directory).eprint(self.color)?;

if self.subcommand == Edit {
return self.edit(&search);
Expand Down Expand Up @@ -355,7 +374,7 @@ impl Config {
List => self.list(justfile),
Show { ref name } => self.show(&name, justfile),
Summary => self.summary(justfile),
Edit => unreachable!(),
Edit | Init => unreachable!(),
}
}

Expand Down Expand Up @@ -394,6 +413,26 @@ impl Config {
}
}

pub(crate) fn init(&self) -> Result<(), i32> {
let search =
Search::init(&self.search_config, &self.invocation_directory).eprint(self.color)?;

if search.justfile.exists() {
eprintln!("Justfile `{}` already exists", search.justfile.display());
Err(EXIT_FAILURE)
} else if let Err(err) = fs::write(&search.justfile, INIT_JUSTFILE) {
eprintln!(
"Failed to write justfile to `{}`: {}",
search.justfile.display(),
err
);
Err(EXIT_FAILURE)
} else {
eprintln!("Wrote justfile to `{}`", search.justfile.display());
Ok(())
}
}

fn list(&self, justfile: Justfile) -> Result<(), i32> {
// Construct a target to alias map.
let mut recipe_aliases: BTreeMap<&str, Vec<&str>> = BTreeMap::new();
Expand Down Expand Up @@ -561,6 +600,7 @@ FLAGS:
Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`
--evaluate Print evaluated variables
--highlight Highlight echoed recipe lines in bold
--init Initialize new justfile in project root
-l, --list List available recipes and their arguments
--no-highlight Don't highlight echoed recipe lines in bold
-q, --quiet Suppress all output
Expand Down Expand Up @@ -922,6 +962,14 @@ ARGS:
},
}

test! {
name: subcommand_evaluate_overrides,
args: ["--evaluate", "x=y"],
subcommand: Subcommand::Evaluate {
overrides: map!{"x": "y"},
},
}

test! {
name: subcommand_list_long,
args: ["--list"],
Expand Down Expand Up @@ -1097,6 +1145,16 @@ ARGS:
},
}

error! {
name: evaluate_arguments,
args: ["--evaluate", "bar"],
error: ConfigError::SubcommandArguments { subcommand, arguments },
check: {
assert_eq!(subcommand, "--evaluate");
assert_eq!(arguments, &["bar"]);
},
}

error! {
name: dump_arguments,
args: ["--dump", "bar"],
Expand All @@ -1117,6 +1175,16 @@ ARGS:
},
}

error! {
name: init_arguments,
args: ["--init", "bar"],
error: ConfigError::SubcommandArguments { subcommand, arguments },
check: {
assert_eq!(subcommand, "--init");
assert_eq!(arguments, &["bar"]);
},
}

error! {
name: show_arguments,
args: ["--show", "foo", "bar"],
Expand Down Expand Up @@ -1157,4 +1225,9 @@ ARGS:
assert_eq!(overrides, map!{"bar": "baz"});
},
}

#[test]
fn init_justfile() {
testing::compile(INIT_JUSTFILE);
}
}
3 changes: 2 additions & 1 deletion src/config_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ pub(crate) enum ConfigError {
))]
SearchDirConflict,
#[snafu(display(
"`{}` used with unexpected arguments: {}",
"`{}` used with unexpected {}: {}",
subcommand,
Count("argument", arguments.len()),
List::and_ticked(arguments)
))]
SubcommandArguments {
Expand Down
Loading

0 comments on commit e948f11

Please sign in to comment.