prx 0.5.9

Praxis — agent-native Unix tools. Single binary replacing grep, cat, find, sed, diff for AI coding agents.
use super::{Diagnostic, ParsedResult, define_regex};

define_regex!(PACKAGE_RE, r"^(ok|FAIL)\s+(\S+)\s+([\d.]+)s");
define_regex!(FAIL_HEADER_RE, r"^--- FAIL: (\S+)");

pub fn parse(output: &str) -> ParsedResult {
    let mut passed_pkgs = 0;
    let mut failed_pkgs = 0;
    let mut failures = Vec::new();

    let lines: Vec<&str> = output.lines().collect();
    let mut i = 0;

    while i < lines.len() {
        if let Some(caps) = PACKAGE_RE.captures(lines[i]) {
            if &caps[1] == "ok" {
                passed_pkgs += 1;
            } else {
                failed_pkgs += 1;
            }
        }

        if let Some(caps) = FAIL_HEADER_RE.captures(lines[i]) {
            let test_name = caps[1].to_string();
            let mut message_lines = Vec::new();

            i += 1;
            while i < lines.len() && !lines[i].starts_with("---") && !PACKAGE_RE.is_match(lines[i])
            {
                let trimmed = lines[i].trim();
                if !trimmed.is_empty() {
                    message_lines.push(trimmed.to_string());
                }
                i += 1;
            }

            let location = message_lines.first().and_then(|l| {
                if l.contains(".go:") {
                    Some(l.split(':').take(2).collect::<Vec<_>>().join(":"))
                } else {
                    None
                }
            });

            failures.push(Diagnostic {
                name: test_name,
                location,
                message: message_lines.join("\n"),
            });
            continue;
        }
        i += 1;
    }

    let summary = format!("{passed_pkgs} ok, {failed_pkgs} failed");

    ParsedResult::new(summary, passed_pkgs, failed_pkgs, 0, failures)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_all_passing() {
        let output = "ok      github.com/user/project/auth     0.003s\nok      github.com/user/project/handler  0.005s\n";
        let result = parse(output);
        assert_eq!(result.passed, 2);
        assert_eq!(result.failed, 0);
    }

    #[test]
    fn parse_with_failure() {
        let output = "\
--- FAIL: TestLogin (0.00s)
    auth_test.go:42: expected true, got false
FAIL    github.com/user/project/auth    0.005s
ok      github.com/user/project/handler 0.003s
";
        let result = parse(output);
        assert_eq!(result.passed, 1);
        assert_eq!(result.failed, 1);
        assert_eq!(result.failures.len(), 1);
        assert_eq!(result.failures[0].name, "TestLogin");
    }
}