use crate::paths;
pub const USAGE: &str = "\
usage: shep-deploy <verb> [args]
deploy <sheep> [--watch auto|manual] deploy one sheep, or set how it is watched
setup <sheep> take a sheep over
survey report where every registered sheep stands
on-remove lifecycle hook; shep runs this itself
help print this
With no arguments the poll loop starts only when SHEP_DOG_NAME is set, as shep
does when it starts the dog; without it, the bare command prints this and exits.
Adopted as `deploy`, the same verbs run as `shep deploy <verb> [args]`, and
`shep deploy <sheep>` deploys one sheep directly. A sheep whose name is one of
the verbs above is reached with the verb spelled out: `shep deploy deploy survey`,
`shep deploy deploy help`.";
#[derive(Debug, PartialEq, Eq)]
pub enum Route<'a> {
Poll,
Help,
OnRemove,
Survey,
Deploy(&'a str),
Setup(&'a str),
Watch { sheep: &'a str, mode: &'a str },
Usage,
}
pub fn route<'a>(args: &[&'a str], supervised: bool) -> Route<'a> {
let route = match args {
[] if supervised => Route::Poll,
[] => Route::Usage,
["-h" | "--help" | "help"] => Route::Help,
["on-remove"] => Route::OnRemove,
["survey"] => Route::Survey,
["setup" | "deploy"] => Route::Usage,
["setup", sheep] => Route::Setup(sheep),
["deploy", sheep] => Route::Deploy(sheep),
["deploy", sheep, "--watch", mode] => Route::Watch { sheep, mode },
[sheep] => Route::Deploy(sheep),
[sheep, "--watch", mode] => Route::Watch { sheep, mode },
_ => Route::Usage,
};
match route {
Route::Setup(sheep) | Route::Deploy(sheep) if !paths::is_sheep_name(sheep) => Route::Usage,
Route::Watch { sheep, .. } if !paths::is_sheep_name(sheep) => Route::Usage,
named => named,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_bare_name_is_a_deploy_and_a_verb_still_wins() {
assert_eq!(route(&["koji"], false), Route::Deploy("koji"));
assert_eq!(route(&["deploy", "koji"], false), Route::Deploy("koji"));
assert_eq!(route(&["survey"], false), Route::Survey);
assert_eq!(route(&["setup", "koji"], false), Route::Setup("koji"));
assert_eq!(route(&["deploy", "survey"], false), Route::Deploy("survey"));
assert_eq!(route(&[], true), Route::Poll);
}
#[test]
fn a_bare_run_polls_only_when_shep_started_it() {
assert_eq!(route(&[], true), Route::Poll);
assert_eq!(route(&[], false), Route::Usage);
}
#[test]
fn help_in_each_spelling_routes_to_help() {
for supervised in [true, false] {
for arg in ["-h", "--help", "help"] {
assert_eq!(
route(&[arg], supervised),
Route::Help,
"{arg}, supervised: {supervised}"
);
}
}
}
#[test]
fn a_sheep_named_help_is_reached_with_the_verb_spelled_out() {
assert_eq!(route(&["deploy", "help"], false), Route::Deploy("help"));
}
#[test]
fn on_remove_routes_to_its_own_hook() {
assert_eq!(route(&["on-remove"], false), Route::OnRemove);
assert_eq!(
route(&["deploy", "on-remove"], false),
Route::Deploy("on-remove")
);
}
#[test]
fn a_verb_without_its_sheep_is_a_usage_error() {
assert_eq!(route(&["setup"], false), Route::Usage);
assert_eq!(route(&["deploy"], false), Route::Usage);
assert_eq!(route(&["deploy", "setup"], false), Route::Deploy("setup"));
assert_eq!(route(&["deploy", "deploy"], false), Route::Deploy("deploy"));
}
#[test]
fn a_sheep_name_that_is_a_path_is_refused() {
for name in ["/tmp/anywhere", "../sibling", "a/b", "", ".", ".."] {
assert_eq!(route(&[name], false), Route::Usage, "bare: {name}");
assert_eq!(
route(&["deploy", name], false),
Route::Usage,
"deploy: {name}"
);
assert_eq!(
route(&["setup", name], false),
Route::Usage,
"setup: {name}"
);
assert_eq!(
route(&[name, "--watch", "auto"], false),
Route::Usage,
"watch: {name}"
);
}
}
}