Skip to main content

polyhook_core/
lib.rs

1pub mod detect;
2pub mod events;
3pub mod parse;
4pub mod response;
5pub mod tools;
6pub mod types;
7mod type_impls;
8pub mod wasm;
9mod stdin_wrappers;
10
11pub use types::*;
12pub use stdin_wrappers::{read, respond};
13
14use std::cell::RefCell;
15use std::io::{Read, Write};
16
17use parse::parse_event;
18use response::serialize_response_with_event;
19
20// Store the caller and event from the most recently parsed event so that
21// `respond` can serialise the response in the correct format.
22thread_local! {
23    static LAST_CALLER: RefCell<CallerKind> = const { RefCell::new(CallerKind::Unknown) };
24    static LAST_EVENT: RefCell<Option<HookEventEvent>> = const { RefCell::new(None) };
25}
26
27/// Read a [`HookEvent`] from an arbitrary reader.
28///
29/// Reads until EOF, then parses the JSON payload. The detected [`CallerKind`]
30/// and event type are stored in thread-locals so that a subsequent [`respond_to`]
31/// call can serialise the response in the correct format.
32pub fn read_from(r: &mut impl Read) -> Result<HookEvent, String> {
33    let mut buf = Vec::new();
34    r.read_to_end(&mut buf)
35        .map_err(|e| format!("read error: {e}"))?;
36
37    let event = parse_event(&buf)?;
38
39    LAST_CALLER.with(|c| {
40        *c.borrow_mut() = event.caller;
41    });
42    LAST_EVENT.with(|e| {
43        *e.borrow_mut() = Some(event.event);
44    });
45
46    Ok(event)
47}
48
49/// Write a [`HookResponse`] to an arbitrary writer in the format expected by
50/// the agent that was detected during the most recent [`read_from`] call.
51pub fn respond_to(w: &mut impl Write, response: &HookResponse) -> Result<(), String> {
52    let caller = LAST_CALLER.with(|c| *c.borrow());
53    let event = LAST_EVENT.with(|e| *e.borrow());
54    let value = serialize_response_with_event(response, &caller, event);
55    // serde_json::Value is always serializable; expect is safe here.
56    let json = serde_json::to_string(&value).expect("serde_json::Value is always serializable");
57
58    w.write_all(json.as_bytes())
59        .map_err(|e| format!("write error: {e}"))?;
60
61    Ok(())
62}
63
64// ---------------------------------------------------------------------------
65// Tests
66// ---------------------------------------------------------------------------
67
68#[cfg(test)]
69#[path = "lib_tests.rs"]
70mod tests;