Skip to main content

gonzalo_parse_worker/
gonzalo-parse-worker.rs

1//! Parse worker: a subprocess that reads source and writes the parsed
2//! [`CodeGraph`], one request per line. It exists to contain tree-sitter — the
3//! grammars are C and can `abort()` on malformed input, which is uncatchable in
4//! Rust; running parsing here means such a crash kills only this worker, not the
5//! daemon (see [`gonzalo_parse::ParserPool`]).
6//!
7//! ## Protocol (newline-delimited JSON, one exchange per line)
8//! - **Request**: a JSON [`ParseRequest`] (`{ language, source }`). JSON escapes
9//!   newlines, so a whole file is exactly one line.
10//! - **Response**: a JSON [`CodeGraph`] on one line.
11//!
12//! ## Fault injection (tests only)
13//! Two env-gated sentinels give deterministic stand-ins for grammar
14//! pathologies, used to test pool respawn/timeout. Unset in production, so
15//! neither is a live code path:
16//! - `GONZALO_PARSE_CRASH_TOKEN` — a request whose `source` equals it `abort()`s
17//!   the worker (a grammar crash).
18//! - `GONZALO_PARSE_HANG_TOKEN` — a request whose `source` equals it blocks
19//!   forever (a grammar hang).
20
21use gonzalo_graph::build;
22use gonzalo_parse::ParseRequest;
23use std::io::{BufRead, Write};
24
25fn main() {
26    let crash_token = std::env::var("GONZALO_PARSE_CRASH_TOKEN").ok();
27    let hang_token = std::env::var("GONZALO_PARSE_HANG_TOKEN").ok();
28    let stdin = std::io::stdin();
29    let mut stdout = std::io::stdout();
30
31    for line in stdin.lock().lines() {
32        let Ok(line) = line else { break };
33        if line.is_empty() {
34            continue;
35        }
36        let Ok(request) = serde_json::from_str::<ParseRequest>(&line) else {
37            // A malformed request frame is a protocol error; skip it rather than
38            // die (the pool would otherwise see a spurious worker death).
39            continue;
40        };
41
42        if crash_token.as_deref() == Some(request.source.as_str()) {
43            std::process::abort();
44        }
45        if hang_token.as_deref() == Some(request.source.as_str()) {
46            std::thread::sleep(std::time::Duration::from_secs(3600));
47        }
48
49        let graph = build(request.language, &request.source);
50        let encoded = serde_json::to_string(&graph).expect("CodeGraph serializes");
51        if writeln!(stdout, "{encoded}").is_err() || stdout.flush().is_err() {
52            break; // parent closed the pipe
53        }
54    }
55}