fastgrep 0.1.8

Fast parallel grep with SIMD-accelerated search and trigram indexing
Documentation
mod common;

use std::io::Write;
use std::process::Command;
use std::process::Stdio;

use common::*;
use serde_json::Value;
use tempfile::NamedTempFile;
use tempfile::TempDir;

fn parse_json_lines(bytes: &[u8]) -> Vec<Value> {
    let text = String::from_utf8_lossy(bytes);
    text.lines()
        .map(|line| serde_json::from_str::<Value>(line).expect("expected valid JSON line"))
        .collect()
}

#[test]
fn json_outputs_match_objects_for_file_search() {
    let f = generate_test_file();
    let p = f.path().to_str().unwrap();

    let output = Command::new(fastgrep_bin())
        .args(["--no-index", "--json", "-n", "error", p])
        .output()
        .expect("failed to run fastgrep");

    assert!(output.status.success(), "expected match success");
    let lines = parse_json_lines(&output.stdout);
    assert_eq!(lines.len(), 2);

    assert_eq!(lines[0]["type"], "match");
    assert_eq!(lines[0]["path"], p);
    assert_eq!(lines[0]["line_number"], 6);
    assert_eq!(lines[0]["lines"]["text"], "line with error message");
    assert_eq!(lines[0]["submatches"][0]["match"]["text"], "error");
    assert_eq!(lines[0]["submatches"][0]["start"], 10);
    assert_eq!(lines[0]["submatches"][0]["end"], 15);

    assert_eq!(lines[1]["type"], "match");
    assert_eq!(lines[1]["line_number"], 7);
    assert_eq!(lines[1]["lines"]["text"], "another error here");
}

#[test]
fn json_outputs_match_objects_for_stdin_with_label() {
    let input = "hello world\nno match\nhello again\n";
    let mut child = Command::new(fastgrep_bin())
        .args(["--no-index", "--json", "-Hn", "--label", "stdin.txt", "hello"])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .expect("failed to spawn fastgrep");
    child.stdin.take().unwrap().write_all(input.as_bytes()).unwrap();
    let output = child.wait_with_output().unwrap();

    assert!(output.status.success(), "expected match success");
    let lines = parse_json_lines(&output.stdout);
    assert_eq!(lines.len(), 2);
    assert_eq!(lines[0]["path"], "stdin.txt");
    assert_eq!(lines[0]["line_number"], 1);
    assert_eq!(lines[1]["path"], "stdin.txt");
    assert_eq!(lines[1]["line_number"], 3);
}

#[test]
fn json_emits_structured_size_limit_warning_on_stderr() {
    let dir = TempDir::new().unwrap();

    let mut small = std::fs::File::create(dir.path().join("small.txt")).unwrap();
    writeln!(small, "needle in small file").unwrap();

    let mut big = NamedTempFile::new_in(dir.path()).unwrap();
    write!(big, "{}", "x".repeat(64)).unwrap();
    big.flush().unwrap();

    let output = Command::new(fastgrep_bin())
        .args([
            "--no-index",
            "--json",
            "--max-file-size=32",
            "-r",
            "needle",
            dir.path().to_str().unwrap(),
        ])
        .output()
        .expect("failed to run fastgrep");

    assert!(output.status.success(), "small file should still match");
    let stdout = parse_json_lines(&output.stdout);
    assert_eq!(stdout.len(), 1);

    let stderr = parse_json_lines(&output.stderr);
    assert_eq!(stderr.len(), 1);
    assert_eq!(stderr[0]["type"], "warning");
    assert_eq!(stderr[0]["kind"], "size_limit");
    assert_eq!(stderr[0]["path"], big.path().to_str().unwrap());
    assert_eq!(stderr[0]["size_bytes"], 64);
    assert_eq!(stderr[0]["max_file_size"], 32);
}