use serde::{Deserialize, Serialize};
use std::fs;
use std::io;
use std::path::Path;
use crate::core::ToolInvocation;
pub(crate) fn read_jsonl<T: serde::de::DeserializeOwned>(path: &Path) -> io::Result<Vec<T>> {
let raw = fs::read_to_string(path)?;
Ok(raw
.split('\n')
.filter(|line| !line.trim().is_empty())
.filter_map(|line| serde_json::from_str::<T>(line).ok())
.collect())
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TranscriptSummary {
pub tool_invocations: Vec<ToolInvocation>,
pub total_tokens: Option<i64>,
pub duration_ms: Option<i64>,
pub final_text: Option<String>,
}
#[cfg(test)]
mod tests {
use super::read_jsonl;
use serde_json::Value;
use std::fs;
use tempfile::TempDir;
#[test]
fn read_jsonl_skips_malformed_lines() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("events.jsonl");
fs::write(&path, "{\"a\":1}\nnot json\n{\"a\":2}\n").unwrap();
let values: Vec<Value> = read_jsonl(&path).unwrap();
assert_eq!(values.len(), 2);
assert_eq!(values[0]["a"], 1);
assert_eq!(values[1]["a"], 2);
}
#[test]
fn read_jsonl_skips_blank_and_whitespace_only_lines() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("events.jsonl");
fs::write(&path, "{\"a\":1}\n\n \n\t\n{\"a\":2}\n").unwrap();
let values: Vec<Value> = read_jsonl(&path).unwrap();
assert_eq!(values.len(), 2);
}
#[test]
fn read_jsonl_errors_on_a_missing_file() {
let dir = TempDir::new().unwrap();
let missing = dir.path().join("absent.jsonl");
assert!(read_jsonl::<Value>(&missing).is_err());
}
}