use crate::{CodeLoreError, Result};
use std::io::Write;
pub fn write_ndjson<W: Write, T: serde::Serialize>(rows: &[T], w: &mut W) -> Result<()> {
for row in rows {
serde_json::to_writer(&mut *w, row)
.map_err(|e| super::serde_json_io_err("ndjson row", &e))?;
w.write_all(b"\n").map_err(CodeLoreError::Io)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(serde::Serialize)]
struct Row {
path: String,
score: f64,
}
#[test]
fn emits_one_object_per_line_no_trailing_array() {
let rows = vec![
Row {
path: "src/a.rs".into(),
score: 1.5,
},
Row {
path: "src/b.rs".into(),
score: 2.0,
},
];
let mut buf = Vec::new();
write_ndjson(&rows, &mut buf).unwrap();
let out = String::from_utf8(buf).unwrap();
assert!(
!out.contains('['),
"ndjson must not wrap in an array: {out}"
);
assert!(!out.contains(']'), "ndjson must not close an array: {out}");
let lines: Vec<&str> = out.lines().collect();
assert_eq!(lines.len(), 2);
assert!(lines[0].starts_with('{') && lines[0].ends_with('}'));
assert!(lines[1].starts_with('{') && lines[1].ends_with('}'));
let r0: serde_json::Value = serde_json::from_str(lines[0]).unwrap();
assert_eq!(r0["path"], "src/a.rs");
}
#[test]
fn empty_input_emits_no_output() {
let rows: Vec<Row> = vec![];
let mut buf = Vec::new();
write_ndjson(&rows, &mut buf).unwrap();
assert!(buf.is_empty(), "empty slice emits zero bytes");
}
#[test]
fn writer_uses_compact_form_not_pretty() {
let rows = vec![Row {
path: "p".into(),
score: 1.0,
}];
let mut buf = Vec::new();
write_ndjson(&rows, &mut buf).unwrap();
let out = String::from_utf8(buf).unwrap();
assert!(out.contains("\"path\":\"p\""));
assert!(out.contains("\"score\":1"));
}
}