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}