use std::str::FromStr;
use clap::{Parser, Subcommand};
use crate::schema::CheckMode;
#[derive(Subcommand, Debug, Clone, PartialEq, Eq)]
#[clap(rename_all = "kebab-case")]
pub(super) enum Command {
Run {
#[clap(short, long, conflicts_with_all = ["rebuild", "skip", "redo"])]
fresh: bool,
#[clap(short, long, conflicts_with_all = ["fresh", "redo"])]
rebuild: bool,
#[clap(short, long, requires = "rebuild", num_args = 1..)]
skip: Vec<String>,
#[clap(short = 'R', long, conflicts_with_all = ["fresh", "rebuild", "skip"], num_args = 1..)]
redo: Vec<String>,
#[clap(short = 'i', long, conflicts_with = "fresh")]
redo_inconsistent_tasks: bool,
},
Check {
#[clap(short, long, default_value = "quick")]
mode: CheckMode,
},
Quit {
#[clap(short, long)]
force: bool,
#[clap(short, long = "no-exit")]
no_exit: bool,
},
Pause {
#[clap()]
targets: Vec<String>,
#[clap(short, long)]
cascade: bool,
},
Resume {
#[clap()]
targets: Vec<String>,
},
Exit,
Clear,
Help,
}
#[derive(Parser)]
#[clap(name = "operon")]
#[command(disable_help_flag = true, disable_help_subcommand = true)]
struct PromptParser {
#[clap(subcommand)]
pub action: Command,
}
impl FromStr for Command {
type Err = clap::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let args = std::iter::once("operon").chain(s.split_whitespace());
let helper = PromptParser::try_parse_from(args)?;
Ok(helper.action)
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
#[case::run_fresh("run --fresh", Command::Run { fresh: true, rebuild: false, skip: vec![], redo: vec![], redo_inconsistent_tasks: false })]
#[case::run_rebuild("run --rebuild", Command::Run { fresh: false, rebuild: true, skip: vec![], redo: vec![], redo_inconsistent_tasks: false })]
#[case::run_skip("run --rebuild --skip task1 task2", Command::Run { fresh: false, rebuild: true, skip: vec!["task1".to_owned(), "task2".to_owned()], redo: vec![], redo_inconsistent_tasks: false })]
#[case::run_redo("run --redo task1 task2", Command::Run { fresh: false, rebuild: false, skip: vec![], redo: vec!["task1".to_owned(), "task2".to_owned()], redo_inconsistent_tasks: false })]
#[case::run_redo_inconsistent("run --redo-inconsistent-tasks", Command::Run { fresh: false, rebuild: false, skip: vec![], redo: vec![], redo_inconsistent_tasks: true })]
#[case::check_trust_all("check --mode trust-all", Command::Check { mode: CheckMode::TrustAll })]
#[case::check_metadata_only("check --mode metadata-only", Command::Check { mode: CheckMode::MetadataOnly })]
#[case::check_quick("check", Command::Check { mode: CheckMode::Quick })]
#[case::check_exhaustive("check --mode exhaustive", Command::Check { mode: CheckMode::Exhaustive })]
#[case::quit_force("quit --force", Command::Quit { force: true, no_exit: false })]
#[case::quit_no_exit("quit --no-exit", Command::Quit { force: false, no_exit: true })]
#[case::pause_all("pause", Command::Pause { targets: vec![], cascade: false })]
#[case::pause_specific("pause task1 task2 --cascade", Command::Pause { targets: vec!["task1".to_owned(), "task2".to_owned()], cascade: true })]
#[case::resume_all("resume", Command::Resume { targets: vec![] })]
#[case::resume_specific("resume task1 task2", Command::Resume { targets: vec!["task1".to_owned(), "task2".to_owned()] })]
#[case::exit("exit", Command::Exit)]
#[case::clear("clear", Command::Clear)]
#[case::help("help", Command::Help)]
fn test_parse_command(#[case] input: &str, #[case] expected: Command) {
let action = Command::from_str(input).unwrap();
assert_eq!(action, expected);
}
}