use clap::{Args, CommandFactory, Parser, Subcommand};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum CommandKind {
subcommand: ToolingSubcommand,
action: AutoUpdateAction,
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum BuildMode {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum InitKind {
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum ToolingSubcommand {
AutoUpdate { action: AutoUpdateAction },
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum AutoUpdateAction {
about = "xtazy project orchestrator",
disable_version_flag = true
#[derive(Subcommand, Debug)]
#[command(name = "xtazy")]
#[command(name = "self")]
#[arg(default_value = ".")]
#[arg(default_value = ".")]
command: CacheSubcommand,
#[derive(Subcommand, Debug)]
#[arg(long, conflicts_with = "prod")]
#[arg(long, conflicts_with = "dev")]
#[arg(default_value = ".")]
#[derive(Subcommand, Debug)]
#[derive(Subcommand, Debug)]
#[command(name = "auto-update")]
AutoUpdate(AutoUpdateArgs),
action: AutoUpdateCommand,
#[derive(Subcommand, Debug)]
#[derive(Subcommand, Debug)]
#[command(name = "auto-update")]
AutoUpdate(AutoUpdateArgs),
pub(crate) fn parse_args(args: &[String]) -> Result<CommandKind, String> {
return Err(DealerCli::command().render_usage().to_string());
if args.get(1).map(String::as_str) == Some("--version")
|| args.get(1).map(String::as_str) == Some("-V")
return Ok(CommandKind::Version);
// Preserve the historical dev shortcut: `dealer <project>` means check that project.
if args.len() == 2 && !args[1].starts_with('-') && !is_known_command(&args[1]) {
return Ok(CommandKind::Check {
project: args[1].clone(),
let cli = DealerCli::try_parse_from(args).map_err(|error| error.to_string())?;
Ok(command_from_cli(cli.command))
fn is_known_command(value: &str) -> bool {
fn command_from_cli(command: DealerCommand) -> CommandKind {
DealerCommand::Check(args) => CommandKind::Check {
DealerCommand::Fmt(args) => CommandKind::Fmt {
DealerCommand::Install(args) => CommandKind::Install {
DealerCommand::Update(args) => CommandKind::Update {
DealerCommand::Outdated => CommandKind::Outdated,
DealerCommand::Remove(args) => CommandKind::Remove {
DealerCommand::Cache(args) => match args.command {
CacheSubcommand::Clean => CommandKind::CacheClean,
DealerCommand::Build(args) => CommandKind::Build {
mode: mode_from_args(args.dev, args.prod),
DealerCommand::Run(args) => CommandKind::Run {
mode: mode_from_args(args.dev, args.prod),
DealerCommand::Test(args) => CommandKind::Test {
DealerCommand::Clean(args) => CommandKind::Clean {
DealerCommand::Init(args) => init_command(args.kind),
DealerCommand::Tooling(args) => CommandKind::Tooling {
subcommand: tooling_command(args.command),
DealerCommand::SelfCommand(args) => match args.command {
SelfCommand::Update => CommandKind::SelfUpdate,
SelfCommand::AutoUpdate(args) => CommandKind::SelfAutoUpdate {
action: auto_update_action(args.action),
fn mode_from_args(_dev: bool, prod: bool) -> BuildMode {
fn init_command(command: InitCommand) -> CommandKind {
InitCommand::App(path) => CommandKind::Init {
InitCommand::Package(path) => CommandKind::Init {
fn tooling_command(command: ToolingCommand) -> ToolingSubcommand {
ToolingCommand::Update => ToolingSubcommand::Update,
ToolingCommand::AutoUpdate(args) => ToolingSubcommand::AutoUpdate {
action: auto_update_action(args.action),
ToolingCommand::Doctor => ToolingSubcommand::Doctor,
fn auto_update_action(command: AutoUpdateCommand) -> AutoUpdateAction {
AutoUpdateCommand::On => AutoUpdateAction::On,
AutoUpdateCommand::Off => AutoUpdateAction::Off,
AutoUpdateCommand::Status => AutoUpdateAction::Status,
fn args(values: &[&str]) -> Vec<String> {
values.iter().map(|value| value.to_string()).collect()
fn parse_version_flag() {
let cmd = parse_args(&args(&["dealer", "--version"])).expect("version should parse");
assert_eq!(cmd, CommandKind::Version);
fn parse_defaults_to_check_for_single_project_argument() {
parse_args(&args(&["dealer", "project"])).expect("single project arg should parse");
project: "project".to_string()
fn parse_check_defaults_to_current_directory() {
let cmd = parse_args(&args(&["dealer", "check"])).expect("check should parse");
fn parse_build_prod_command() {
let cmd = parse_args(&args(&["dealer", "build", "--prod", "project"]))
.expect("build command should parse");
project: "project".to_string(),
fn parse_install_package() {
parse_args(&args(&["dealer", "install", "foo"])).expect("install command should parse");
package: Some("foo".to_string()),
fn parse_xtazy_auto_update_status() {
let cmd = parse_args(&args(&["dealer", "xtazy", "auto-update", "status"]))
.expect("auto-update status should parse");
subcommand: ToolingSubcommand::AutoUpdate {
action: AutoUpdateAction::Status
fn parse_self_auto_update_on() {
let cmd = parse_args(&args(&["dealer", "self", "auto-update", "on"]))
.expect("self auto-update on should parse");
CommandKind::SelfAutoUpdate {
action: AutoUpdateAction::On
parse_args(&args(&["dealer", "self", "update"])).expect("self update should parse");
assert_eq!(cmd, CommandKind::SelfUpdate);