From e8cf10eee384f53707791e792e8a467cb0512f77 Mon Sep 17 00:00:00 2001 From: Overlord Date: Mon, 2 Mar 2026 20:13:38 +0100 Subject: [PATCH 1/4] Initialize project structure with basic CLI setup --- .gitignore | 9 +++++++++ Cargo.toml | 12 ++++++++++++ src/cli/flags.rs | 29 +++++++++++++++++++++++++++++ src/cli/mod.rs | 3 +++ src/lib.rs | 1 + src/main.rs | 8 ++++++++ 6 files changed, 62 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/cli/flags.rs create mode 100644 src/cli/mod.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..762d1bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.idea/ +.vscode/ + +target/ + +Cargo.lock + +*.pdb +**/*.rs.bk diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..79ee7eb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "pmp" +description = """\ + PHP Macro Pre-processor (PMP) \ + A php lexer, parser and macro pre-processor written in Rust. \ +""" + +version = "0.1.0-alpha" +edition = "2024" + +[dependencies] +clap = { version = "^4", features = ["derive"] } diff --git a/src/cli/flags.rs b/src/cli/flags.rs new file mode 100644 index 0000000..97760ee --- /dev/null +++ b/src/cli/flags.rs @@ -0,0 +1,29 @@ +use clap::Parser; +use std::{ + ffi::OsStr, + path::PathBuf +}; + +#[derive(Parser, Debug)] +#[command(name = "pmp", about, version)] +pub struct Args { + #[arg(long)] + php: Option, + + #[arg(long, short = 'd', hide = true)] + debug: bool, + #[arg(long, short = 'v')] + verbose: bool, + + #[arg(default_value_os = OsStr::new("."))] + files: Vec, + #[arg(last = true)] + passthrough: Vec, +} + +impl Args +{ + pub fn parse() -> Self { + ::parse() + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..4539068 --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,3 @@ +pub mod flags; + +pub use flags::Args; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4f77372 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod cli; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..30fa7e3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,8 @@ +use pmp::{ cli::Args }; + +fn main() +{ + let args = Args::parse(); + + println!("{:?}", args); +} From 08aa198b009def18d3f43e19375381a0a9ba5b07 Mon Sep 17 00:00:00 2001 From: Overlord Date: Mon, 2 Mar 2026 20:37:31 +0100 Subject: [PATCH 2/4] Add path validation utility and integrate with CLI --- Cargo.toml | 5 +---- src/cli/flags.rs | 13 +++++++++---- src/lib.rs | 1 + src/main.rs | 4 ++-- src/util/fs.rs | 6 ++++++ src/util/mod.rs | 1 + 6 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 src/util/fs.rs create mode 100644 src/util/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 79ee7eb..6c1d6d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,6 @@ [package] name = "pmp" -description = """\ - PHP Macro Pre-processor (PMP) \ - A php lexer, parser and macro pre-processor written in Rust. \ -""" +description = "PHP Macro Pre-processor (PMP) written in Rust." version = "0.1.0-alpha" edition = "2024" diff --git a/src/cli/flags.rs b/src/cli/flags.rs index 97760ee..e763160 100644 --- a/src/cli/flags.rs +++ b/src/cli/flags.rs @@ -1,13 +1,18 @@ -use clap::Parser; use std::{ ffi::OsStr, path::PathBuf }; +use clap::{ + Parser, + ValueHint::FilePath, +}; + +use crate::util::fs; #[derive(Parser, Debug)] #[command(name = "pmp", about, version)] pub struct Args { - #[arg(long)] + #[arg(long, value_hint = FilePath, value_parser = fs::path_exists)] php: Option, #[arg(long, short = 'd', hide = true)] @@ -15,8 +20,8 @@ pub struct Args { #[arg(long, short = 'v')] verbose: bool, - #[arg(default_value_os = OsStr::new("."))] - files: Vec, + #[arg(default_value_os = OsStr::new("."), value_hint = FilePath, value_parser = fs::path_exists)] + directory: Option, #[arg(last = true)] passthrough: Vec, } diff --git a/src/lib.rs b/src/lib.rs index 4f77372..714f64a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ pub mod cli; +pub mod util; diff --git a/src/main.rs b/src/main.rs index 30fa7e3..fcbf818 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ use pmp::{ cli::Args }; -fn main() +fn main() { let args = Args::parse(); - println!("{:?}", args); + println!("{:#?}", args); } diff --git a/src/util/fs.rs b/src/util/fs.rs new file mode 100644 index 0000000..2611755 --- /dev/null +++ b/src/util/fs.rs @@ -0,0 +1,6 @@ +use std::path::PathBuf; + +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()) +} diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..d521fbd --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1 @@ +pub mod fs; From ccc993976f84529a081eceb4e554cdd9afa70886 Mon Sep 17 00:00:00 2001 From: Overlord Date: Mon, 2 Mar 2026 21:01:24 +0100 Subject: [PATCH 3/4] Update dependencies and adjust CLI module visibility --- Cargo.toml | 12 ++++++++++++ src/cli/mod.rs | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6c1d6d3..92b308d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,15 @@ edition = "2024" [dependencies] clap = { version = "^4", features = ["derive"] } + +rayon = "^1" + +mago-syntax = "^1.13" +mago-interner = "^1.0.0-alpha" +mago-source = "^1.0.0-alpha" +mago-span = "^1.13" +mago-names = "^1.13" +mago-project = "^0.26" +mago-semantics = "^1.13" + +walkdir = "^2" diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 4539068..741db5a 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,3 +1,3 @@ -pub mod flags; +mod flags; pub use flags::Args; From baca32e57c638c833d75ca36a2cc2d5ef51e36a9 Mon Sep 17 00:00:00 2001 From: Overlord Date: Tue, 3 Mar 2026 08:28:11 +0100 Subject: [PATCH 4/4] Enhance CLI arguments with new options, validation, and detailed help messages --- Cargo.toml | 3 +++ src/cli/flags.rs | 21 ++++++++++++++++++--- src/util/fs.rs | 21 +++++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) 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)) +}