use std::{
borrow::Borrow,
fs::{self, OpenOptions},
io::Write,
path::Path,
};
use crate::api::record::Record;
pub fn write_to_jsonl_file<P: AsRef<Path>, R: Borrow<Record>>(
file_path: P,
records: &[R],
) -> Result<(), Box<dyn std::error::Error>> {
let Ok(mut file) = OpenOptions::new().create(true).append(true).open(file_path) else {
return Err("Failed to open file".into());
};
for record in records {
let json_line = record.borrow().to_jsonl_string()?;
writeln!(file, "{json_line}")?;
}
file.flush()?;
Ok(())
}
pub fn read_jsonl_file<P: AsRef<Path>>(
file_path: P,
) -> Result<Vec<Record>, Box<dyn std::error::Error>> {
let content = fs::read_to_string(&file_path)?;
let mut records = Vec::new();
for (line_num, line) in content.lines().enumerate() {
let line = line.trim();
if line.is_empty() {
continue; }
let record: Record = serde_json::from_str(line)
.map_err(|e| format!("{}:{}: {}", file_path.as_ref().display(), line_num + 1, e))?;
records.push(record);
}
Ok(records)
}
#[cfg(test)]
mod tests {
use std::fs;
use tempfile::NamedTempFile;
use super::*;
use crate::api::record::RecordKind;
#[test]
fn test_jsonl_round_trip() {
let records = vec![
Record::new(
RecordKind::Struct,
"TestStruct".to_string(),
"pub struct TestStruct { }".to_string(),
Default::default(),
None,
),
Record::new(
RecordKind::Function,
"test_func".to_string(),
"pub fn test_func() { }".to_string(),
Default::default(),
None,
),
Record::new(
RecordKind::Enum,
"TestEnum".to_string(),
"pub enum TestEnum { A, B }".to_string(),
Default::default(),
None,
),
];
let temp_file = NamedTempFile::new().unwrap();
let temp_path = temp_file.path();
write_to_jsonl_file(temp_path, &records).unwrap();
let loaded_records = read_jsonl_file(temp_path).unwrap();
assert_eq!(records.len(), loaded_records.len());
for (original, loaded) in records.iter().zip(loaded_records.iter()) {
assert_eq!(original.kind, loaded.kind);
assert_eq!(original.name, loaded.name);
assert_eq!(original.content, loaded.content);
}
}
#[test]
fn test_jsonl_file_format() {
let record = Record::new(
RecordKind::Struct,
"Test".to_string(),
"pub struct Test { }".to_string(),
Default::default(),
None,
);
let temp_file = NamedTempFile::new().unwrap();
let temp_path = temp_file.path();
write_to_jsonl_file(temp_path, &[record]).unwrap();
let content = fs::read_to_string(temp_path).unwrap();
let lines: Vec<&str> = content.lines().collect();
assert_eq!(lines.len(), 1);
let parsed: Record = serde_json::from_str(lines[0]).unwrap();
assert_eq!(parsed.name, "Test");
assert_eq!(parsed.kind, RecordKind::Struct);
let record_with_cfg = Record::new(
RecordKind::Function,
"test_func".to_string(),
"pub fn test_func() { }".to_string(),
Default::default(),
Some("feature = \"unstable\"".to_string()),
);
write_to_jsonl_file(temp_path, &[record_with_cfg]).unwrap();
let content = fs::read_to_string(temp_path).unwrap();
let lines: Vec<&str> = content.lines().collect();
dbg!(&lines);
assert_eq!(lines.len(), 2);
let parsed: Record = serde_json::from_str(lines[1]).unwrap();
assert_eq!(parsed.cfg, Some("feature = \"unstable\"".to_string()));
}
}