syntaxmate 0.1.1

Rust-native TextMate syntax highlighting with bundled grammars and themes
Documentation
use syntaxmate::diagnostics::{RegexAnchorContext, RegexEngine, inspect_regex, match_regex};

fn main() {
    if let Err(error) = run() {
        eprintln!("error: {error}");
        std::process::exit(2);
    }
}

fn run() -> Result<(), String> {
    let mut args = std::env::args().skip(1).collect::<Vec<_>>();
    let match_mode = take_flag(&mut args, "--match");
    let engine = match take_value(&mut args, "--engine").as_deref() {
        None | Some("auto") => RegexEngine::Auto,
        Some("dfa" | "automata") => RegexEngine::Dfa,
        Some("fallback") => RegexEngine::Fallback,
        Some(engine) => return Err(format!("unknown --engine {engine:?}")),
    };
    let from = take_value(&mut args, "--from")
        .map(|value| value.parse::<usize>().map_err(|_| "--from must be a usize"))
        .transpose()?
        .unwrap_or(0);
    let budget = take_value(&mut args, "--budget")
        .map(|value| {
            value
                .parse::<usize>()
                .map_err(|_| "--budget must be a usize")
        })
        .transpose()?
        .unwrap_or(100_000);
    let allow_start_of_file = take_flag(&mut args, "--allow-a");
    let continuation_position = take_value(&mut args, "--allow-g")
        .map(|value| {
            value
                .parse::<usize>()
                .map_err(|_| "--allow-g must be a usize")
        })
        .transpose()?;
    let pattern = args.first().ok_or("missing pattern")?;

    let inspection = inspect_regex(pattern);
    println!("{}", inspection.parsed);
    println!("translated_pattern: {}", inspection.translated_pattern);
    println!("anchor_strategy: {}", inspection.anchor_strategy);
    println!("route: {}", inspection.route);
    if !match_mode {
        return Ok(());
    }

    let line = args.get(1).ok_or("--match needs a line argument")?;
    let report = match_regex(
        pattern,
        line,
        from,
        RegexAnchorContext {
            allow_start_of_file,
            continuation_position,
        },
        engine,
        budget,
    )
    .map_err(|error| error.to_string())?;
    println!("engine: {}", report.engine);
    if let Some(steps) = report.steps {
        println!("steps: {steps}");
    }
    match report.matched {
        Some(range) => println!("match: {}..{}", range.start, range.end),
        None => println!("match: <none>"),
    }
    for (index, capture) in report.captures.iter().enumerate() {
        println!("capture[{index}]: {capture:?}");
    }
    Ok(())
}

fn take_flag(args: &mut Vec<String>, flag: &str) -> bool {
    if let Some(index) = args.iter().position(|argument| argument == flag) {
        args.remove(index);
        true
    } else {
        false
    }
}

fn take_value(args: &mut Vec<String>, flag: &str) -> Option<String> {
    let index = args.iter().position(|argument| argument == flag)?;
    args.remove(index);
    (index < args.len()).then(|| args.remove(index))
}