use clap::{builder::ValueParser, Arg, ArgAction, Command as ClapCommand}; #[derive(Debug)] pub struct Command { inner: ClapCommand, } impl Default for Command { fn default() -> Self { Self::new() } } impl Command { #[must_use] pub fn new() -> Self { let inner = ClapCommand::new("oxc") .bin_name("oxc") .version("alpha") .author("Boshen") .about("The JavaScript Oxidation Compiler") .subcommand_required(true) .arg_required_else_help(true) .subcommand(Self::lint_subcommand()); Self { inner } } fn lint_subcommand() -> ClapCommand { ClapCommand::new("lint") .alias("check") .about("Lint this repository.") .arg_required_else_help(true) .arg( Arg::new("quiet") .long("quiet") .required(false) .action(clap::ArgAction::SetTrue) .help("This option allows you to disable reporting on warnings. If you enable this option, only errors are reported by oxc_lint.") ) .arg( Arg::new("ignore-pattern") .long("ignore-pattern") .required(false) .action(ArgAction::Append) .help("This option allows you to specify patterns of files to ignore (in addition to those in .eslintignore).") ) .arg( Arg::new("path") .value_name("PATH") .num_args(1..) .required(true) .help("File or Directory paths to scan. Directories are scanned recursively.") .value_parser(ValueParser::path_buf()), ) } #[must_use] #[allow(clippy::missing_const_for_fn)] pub fn build(self) -> ClapCommand { self.inner } } #[cfg(test)] mod test { use std::path::PathBuf; use super::Command; #[test] fn verify_command() { Command::new().build().debug_assert(); } #[test] fn test_lint_path() { let arg = "oxc lint ."; let matches = Command::new().build().try_get_matches_from(arg.split(' ')).unwrap(); let matches = matches.subcommand_matches("lint"); assert!(matches.is_some()); assert_eq!(matches.unwrap().get_one::("path"), Some(&PathBuf::from("."))); } #[test] fn test_lint_multiple_paths() { let arg = "oxc lint foo bar baz"; let matches = Command::new().build().try_get_matches_from(arg.split(' ')).unwrap(); let matches = matches.subcommand_matches("lint"); assert!(matches.is_some()); assert_eq!( matches.unwrap().get_many::("path").unwrap().collect::>(), [&PathBuf::from("foo"), &PathBuf::from("bar"), &PathBuf::from("baz")] ); } #[test] fn test_check_path() { let arg = "oxc check /path/to/dir"; let matches = Command::new().build().try_get_matches_from(arg.split(' ')).unwrap(); let matches = matches.subcommand_matches("lint"); assert!(matches.is_some()); assert_eq!( matches.unwrap().get_one::("path"), Some(&PathBuf::from("/path/to/dir")) ); } #[test] fn test_quiet_true() { let arg = "oxc lint foo.js --quiet"; let matches = Command::new().build().try_get_matches_from(arg.split(' ')).unwrap(); let matches = matches.subcommand_matches("lint"); assert!(matches.unwrap().get_flag("quiet")); } #[test] fn test_quiet_false() { let arg = "oxc lint foo.js"; let matches = Command::new().build().try_get_matches_from(arg.split(' ')).unwrap(); let matches = matches.subcommand_matches("lint"); assert!(!matches.unwrap().get_flag("quiet")); } #[test] fn test_single_ignore_pattern() { let arg = "oxc lint --ignore-pattern \"./test\" foo.js"; let matches = Command::new().build().try_get_matches_from(arg.split(' ')).unwrap(); let matches = matches.subcommand_matches("lint").unwrap(); assert_eq!(matches.get_one::("ignore-pattern"), Some(&"\"./test\"".to_string())); } #[test] fn test_multiple_ignore_pattern() { let arg = "oxc lint --ignore-pattern \"./test\" --ignore-pattern \"bar.js\" foo.js"; let matches = Command::new().build().try_get_matches_from(arg.split(' ')).unwrap(); let matches = matches.subcommand_matches("lint").unwrap(); let ignore_pattern = matches.get_many::("ignore-pattern").unwrap(); let mut compare = vec![]; for pattern in ignore_pattern { compare.push(pattern); } assert_eq!(compare, vec!["\"./test\"", "\"bar.js\""]); } }