diff --git a/Cargo.toml b/Cargo.toml index 92b308d..c816dc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ clap = { version = "^4", features = ["derive"] } rayon = "^1" +once_cell = "^1" + mago-syntax = "^1.13" mago-interner = "^1.0.0-alpha" mago-source = "^1.0.0-alpha" @@ -18,4 +20,5 @@ mago-names = "^1.13" mago-project = "^0.26" mago-semantics = "^1.13" +regex = "^1" walkdir = "^2" diff --git a/src/cli/flags.rs b/src/cli/flags.rs index e763160..a7d5e2c 100644 --- a/src/cli/flags.rs +++ b/src/cli/flags.rs @@ -12,20 +12,35 @@ use crate::util::fs; #[derive(Parser, Debug)] #[command(name = "pmp", about, version)] pub struct Args { - #[arg(long, value_hint = FilePath, value_parser = fs::path_exists)] + #[arg(long, value_hint = FilePath, value_parser = fs::path_exists, help = "Path to PHP binary")] php: Option, #[arg(long, short = 'd', hide = true)] debug: bool, - #[arg(long, short = 'v')] + #[arg(long, short = 'v', help = "Verbose output")] verbose: bool, + #[arg(long, short = 'n', help = "Dry run, does not modify files or cache")] + dry_run: bool, - #[arg(default_value_os = OsStr::new("."), value_hint = FilePath, value_parser = fs::path_exists)] + #[arg(long, short = 'e', value_hint = FilePath, value_parser = fs::path_exists, default_values_os = [OsStr::new(".gitignore"), OsStr::new("vendor/")], help = "Exclude files from pre-processing, e.g. vendor/")] + exclude: Vec, + #[arg(long, short = 'c', value_hint = FilePath, value_parser = fs::valid_possible_path, default_value_os = OsStr::new(".cache"), help = "Cache directory location")] + cache_dir: Option, + #[arg(long, short = 'o', value_hint = FilePath, value_parser = fs::valid_possible_path, default_value_os = OsStr::new("."), help = "Output directory location")] + output_dir: Option, + #[arg(long, short = 'i', conflicts_with = "dry_run", help = "Invalidate cache and re-process all files")] + invalidate_cache: bool, + + #[arg(value_hint = FilePath, value_parser = fs::path_exists, default_value_os = OsStr::new("."), help = "Directory to recursively scan and pre-process")] directory: Option, #[arg(last = true)] passthrough: Vec, } +// todo: ignore .gitignore marked content per default +// todo: diff output with verbose and ofc trace for warnings/errors +// todo: cache with file hashes and system last edited timestamp + impl Args { pub fn parse() -> Self { diff --git a/src/util/fs.rs b/src/util/fs.rs index 2611755..29a2fd2 100644 --- a/src/util/fs.rs +++ b/src/util/fs.rs @@ -1,6 +1,27 @@ +use regex::Regex; use std::path::PathBuf; +use once_cell::sync::Lazy; + +static PATH_REGEX: Lazy = Lazy::new(|| { + Regex::new(r"^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$").unwrap() +}); pub fn path_exists(s: &str) -> Result { let p = PathBuf::from(s); p.exists().then_some(p).ok_or("path does not exist".to_string()) } + +pub fn valid_possible_path(s: &str) -> Result +{ + let lower = s.to_ascii_lowercase(); + + if lower == "true" || lower == "false" { + return Err("boolean literal is not allowed".into()); + } + + if PATH_REGEX.is_match(s) { + return Err("numeric literal is not allowed".into()); + } + + Ok(PathBuf::from(s)) +}