use std::sync::LazyLock; use std::{fmt::Display, path::PathBuf}; use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum}; pub static CLI_ARGS: LazyLock = LazyLock::new(|| CliArgs::parse()); #[derive(Debug, Parser)] #[clap(author, version)] #[command(help_template = "{author-section}\n{usage-heading} {usage}\n\n{all-args}")] pub struct CliArgs { #[command(subcommand)] pub command: Command, } #[derive(Debug, Clone, Subcommand)] pub enum Command { /// Create a new raven project New { #[clap(flatten)] type_: ProjectFlag, project_name: String, }, /// Create a new raven project in an existing directory Init, /// Compile the current project Build, /// Run the current project Run { #[clap(value_hint = clap::ValueHint::DirPath)] entry_point: Option, #[clap(flatten)] assertions: Assertions, }, /// !!! BORKED !!! Run the tests Test { #[clap(flatten)] assertions: Assertions, }, /// Remove the target directory and caching Clean, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Args)] pub struct Assertions { /// Disable assertions. #[arg(short, long = "no-assert", action = ArgAction::SetFalse)] assertions: bool, } impl Into for &Assertions { fn into(self) -> bool { self.assertions } } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Args)] #[group(multiple = false)] pub struct ProjectFlag { #[arg(long, action = ArgAction::SetTrue)] _nest: (), #[arg(long, action = ArgAction::SetTrue)] _package: (), #[arg( hide = true, required = false, short, long, default_value_ifs = [ ("_nest", "true", "nest"), ("_package", "true", "package"), ("_nest", "false", "nest"), ], )] pub project_type: ProjectType, } #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, ValueEnum)] #[value(rename_all = "kebab-case")] pub enum ProjectType { #[default] Nest, Package, } impl Display for ProjectType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", Self::to_string(&self)) } } #[cfg(test)] mod tests { use super::*; #[test] fn args_new_w_arg() { let args: CliArgs = Parser::try_parse_from(["raven", "new", "demo"]).unwrap(); let type_ = match args.command { Command::New { type_, .. } => type_, _ => unreachable!(), }; assert!(type_.project_type == ProjectType::Nest); } #[test] fn args_new_nest() { let args: CliArgs = Parser::try_parse_from(["raven", "new", "--nest", "demo"]).unwrap(); let type_ = match args.command { Command::New { type_, .. } => type_, _ => unreachable!(), }; assert!(type_.project_type == ProjectType::Nest); } #[test] fn args_new_package() { let args: CliArgs = Parser::try_parse_from(["raven", "new", "--package", "demo"]).unwrap(); let type_ = match args.command { Command::New { type_, .. } => type_, _ => unreachable!(), }; assert!(type_.project_type == ProjectType::Package); } #[test] fn args_run_assert() { let args: CliArgs = Parser::try_parse_from(["raven", "run", "Main"]).unwrap(); let assertions: bool = match args.command { Command::Run { assertions, .. } => assertions.assertions, _ => unreachable!(), }; assert!(assertions == true) } #[test] fn args_run_no_assert() { let args: CliArgs = Parser::try_parse_from(["raven", "run", "--no-assert", "Main"]).unwrap(); let assertions: bool = match args.command { Command::Run { assertions, .. } => assertions.assertions, _ => unreachable!(), }; assert!(assertions == false) } }