use std::io::Write;
use serde_json::{Value as JsonValue, json};
use super::{Branch, CaptureLimits, EvalInfo, Outcome, Tracer, ValueRef};
use crate::ast::Node;
pub struct JsonlTracer<W: Write> {
out: W,
limits: CaptureLimits,
error: Option<std::io::Error>,
}
impl<W: Write> JsonlTracer<W> {
pub fn new(out: W, limits: CaptureLimits) -> Self {
JsonlTracer {
out,
limits,
error: None,
}
}
pub fn error(&self) -> Option<&std::io::Error> {
self.error.as_ref()
}
pub fn into_inner(self) -> W {
self.out
}
fn write(&mut self, line: JsonValue) {
if self.error.is_some() {
return;
}
if let Err(e) = writeln!(self.out, "{line}") {
self.error = Some(e);
}
}
fn node(node: &Node) -> JsonValue {
json!({"node": node.id, "kind": node.type_name(), "span": [node.span.start, node.span.end]})
}
}
fn outcome_json(outcome: Outcome<'_>, limits: &CaptureLimits) -> JsonValue {
match outcome {
Outcome::Value(v) => json!({"value": v.capture(limits)}),
Outcome::Error(e) => json!({"error": {"code": e.code(), "message": e.message()}}),
}
}
impl<W: Write> Tracer for JsonlTracer<W> {
fn begin(&mut self, info: &EvalInfo) {
self.write(json!({"event": "begin", "timestamp": info.timestamp as i64}));
}
fn enter(&mut self, node: &Node, _input: ValueRef<'_>) {
let mut line = Self::node(node);
line["event"] = "enter".into();
self.write(line);
}
fn exit(&mut self, node: &Node, outcome: Outcome<'_>) {
let mut line = Self::node(node);
line["event"] = "exit".into();
line["outcome"] = outcome_json(outcome, &self.limits);
self.write(line);
}
fn operands(&mut self, node: &Node, lhs: ValueRef<'_>, rhs: ValueRef<'_>) {
let mut line = Self::node(node);
line["event"] = "operands".into();
line["lhs"] = lhs.capture(&self.limits).into();
line["rhs"] = rhs.capture(&self.limits).into();
self.write(line);
}
fn short_circuit(&mut self, node: &Node) {
let mut line = Self::node(node);
line["event"] = "shortCircuit".into();
self.write(line);
}
fn branch(&mut self, node: &Node, taken: Branch) {
let mut line = Self::node(node);
line["event"] = "branch".into();
line["taken"] = serde_json::to_value(taken).unwrap_or_default();
self.write(line);
}
fn bind(&mut self, node: &Node, name: &str, value: ValueRef<'_>) {
let mut line = Self::node(node);
line["event"] = "bind".into();
line["name"] = name.into();
line["value"] = value.capture(&self.limits).into();
self.write(line);
}
fn filter_item(&mut self, predicate: &Node, index: usize, _item: ValueRef<'_>, passed: bool) {
let mut line = Self::node(predicate);
line["event"] = "filter".into();
line["index"] = index.into();
line["passed"] = passed.into();
self.write(line);
}
fn end(&mut self, outcome: Outcome<'_>) {
let mut line = json!({"event": "end"});
line["outcome"] = outcome_json(outcome, &self.limits);
self.write(line);
}
}