Compare commits

..

5 Commits

8 changed files with 123 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
.idea/
.vscode/
target/
Cargo.lock
*.pdb
**/*.rs.bk

24
Cargo.toml Normal file
View File

@@ -0,0 +1,24 @@
[package]
name = "pmp"
description = "PHP Macro Pre-processor (PMP) written in Rust."
version = "0.1.0-alpha"
edition = "2024"
[dependencies]
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"
mago-span = "^1.13"
mago-names = "^1.13"
mago-project = "^0.26"
mago-semantics = "^1.13"
regex = "^1"
walkdir = "^2"

49
src/cli/flags.rs Normal file
View File

@@ -0,0 +1,49 @@
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, value_hint = FilePath, value_parser = fs::path_exists, help = "Path to PHP binary")]
php: Option<PathBuf>,
#[arg(long, short = 'd', hide = true)]
debug: bool,
#[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(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<PathBuf>,
#[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<PathBuf>,
#[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<PathBuf>,
#[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<PathBuf>,
#[arg(last = true)]
passthrough: Vec<String>,
}
// 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 {
<Self as Parser>::parse()
}
}

3
src/cli/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
mod flags;
pub use flags::Args;

2
src/lib.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod cli;
pub mod util;

8
src/main.rs Normal file
View File

@@ -0,0 +1,8 @@
use pmp::{ cli::Args };
fn main()
{
let args = Args::parse();
println!("{:#?}", args);
}

27
src/util/fs.rs Normal file
View File

@@ -0,0 +1,27 @@
use regex::Regex;
use std::path::PathBuf;
use once_cell::sync::Lazy;
static PATH_REGEX: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$").unwrap()
});
pub fn path_exists(s: &str) -> Result<PathBuf, String> {
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<PathBuf, String>
{
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))
}

1
src/util/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod fs;