oxc/crates/oxc_cli/src/command/mod.rs
Andi Pabst 4c5abb590e
feat(cli): wildcard expansion in paths for windows (#2767)
Unlike on other OS, on Windows there is no wildcard expansion/globbing
by the shell. Instead the application has to handle this. Therefore I
used the `glob` package to handle wildcards on Windows.

I also had to make the parent directory check more strict due to the
glob package resolving `..` in the middle of the path as well.

This closes #2695.
2024-03-22 00:21:30 +08:00

117 lines
3 KiB
Rust

mod format;
mod ignore;
mod lint;
use bpaf::Bpaf;
use std::path::PathBuf;
pub use self::{
format::{format_command, FormatOptions},
ignore::IgnoreOptions,
lint::{lint_command, LintOptions, OutputFormat, OutputOptions, WarningOptions},
};
use self::{format::format_options, lint::lint_options};
const VERSION: &str = match option_env!("OXC_VERSION") {
Some(v) => v,
None => "dev",
};
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version(VERSION))]
pub enum CliCommand {
/// Lint this repository
#[bpaf(command)]
Lint(#[bpaf(external(lint_options))] LintOptions),
/// Format this repository
#[bpaf(command)]
Format(#[bpaf(external(format_options))] FormatOptions),
}
impl CliCommand {
pub fn handle_threads(&self) {
match self {
Self::Lint(options) => {
Self::set_rayon_threads(options.misc_options.threads);
}
Self::Format(options) => {
Self::set_rayon_threads(options.misc_options.threads);
}
}
}
fn set_rayon_threads(threads: Option<usize>) {
if let Some(threads) = threads {
rayon::ThreadPoolBuilder::new().num_threads(threads).build_global().unwrap();
}
}
}
/// Miscellaneous
#[derive(Debug, Clone, Bpaf)]
pub struct MiscOptions {
/// Number of threads to use. Set to 1 for using only 1 CPU core
#[bpaf(argument("INT"), hide_usage)]
pub threads: Option<usize>,
}
#[allow(clippy::ptr_arg)]
fn validate_paths(paths: &Vec<PathBuf>) -> bool {
if paths.is_empty() {
true
} else {
paths.iter().all(|p| p.components().all(|c| c != std::path::Component::ParentDir))
}
}
const PATHS_ERROR_MESSAGE: &str = "PATH must not contain \"..\"";
#[cfg(target_os = "windows")]
#[allow(clippy::needless_pass_by_value)]
fn expand_glob(paths: Vec<PathBuf>) -> Vec<PathBuf> {
let match_options = glob::MatchOptions {
case_sensitive: true,
require_literal_separator: false,
require_literal_leading_dot: false,
};
paths
.iter()
.filter_map(|path| path.to_str())
.filter_map(|path| glob::glob_with(path, match_options).ok())
.flatten()
.filter_map(Result::ok)
.collect()
}
#[cfg(not(target_os = "windows"))]
#[allow(clippy::needless_pass_by_value)]
fn expand_glob(paths: Vec<PathBuf>) -> Vec<PathBuf> {
// no-op on any os other than windows, since they expand globs
paths
}
#[cfg(test)]
mod misc_options {
use super::lint::lint_command;
use super::MiscOptions;
fn get_misc_options(arg: &str) -> MiscOptions {
let args = arg.split(' ').map(std::string::ToString::to_string).collect::<Vec<_>>();
lint_command().run_inner(args.as_slice()).unwrap().lint_options.misc_options
}
#[test]
fn default() {
let options = get_misc_options(".");
assert!(options.threads.is_none());
}
#[test]
fn threads() {
let options = get_misc_options("--threads 4 .");
assert_eq!(options.threads, Some(4));
}
}