toolpath_claude/
writer.rs1use crate::error::Result;
2use crate::types::Conversation;
3use std::io::Write;
4
5pub struct ConversationWriter;
6
7impl ConversationWriter {
8 pub fn write_conversation<W: Write>(conv: &Conversation, mut w: W) -> Result<()> {
12 for raw in &conv.preamble {
15 serde_json::to_writer(&mut w, raw)?;
16 w.write_all(b"\n")?;
17 }
18 for entry in &conv.entries {
19 serde_json::to_writer(&mut w, entry)?;
20 w.write_all(b"\n")?;
21 }
22 Ok(())
23 }
24}
25
26#[cfg(test)]
27mod tests {
28 use super::*;
29 use crate::reader::ConversationReader;
30 use crate::types::ConversationEntry;
31 use serde_json::Value;
32
33 fn conversation() -> Conversation {
34 let mut convo = Conversation::new("test-session".to_string());
35 convo
36 .preamble
37 .push(serde_json::json!({"type": "summary", "summary": "s"}));
38 let entries = [
39 r#"{"uuid":"uuid-1","type":"user","timestamp":"2024-01-01T00:00:00Z","message":{"role":"user","content":"Hello"}}"#,
40 r#"{"uuid":"uuid-2","type":"assistant","timestamp":"2024-01-01T00:00:01Z","message":{"role":"assistant","content":"Hi"}}"#,
41 ];
42 for entry_json in entries {
43 let entry: ConversationEntry = serde_json::from_str(entry_json).unwrap();
44 convo.add_entry(entry);
45 }
46 convo
47 }
48
49 #[test]
50 fn one_record_per_line_newline_terminated() {
51 let convo = conversation();
52
53 let mut buf = Vec::new();
54 ConversationWriter::write_conversation(&convo, &mut buf).unwrap();
55 let out = String::from_utf8(buf).unwrap();
56
57 assert!(out.ends_with('\n'));
58 assert!(!out.ends_with("\n\n"));
59 let lines: Vec<&str> = out.lines().collect();
60 assert_eq!(lines.len(), 3);
61 assert!(lines[0].contains("\"summary\""));
62 for line in &lines {
63 serde_json::from_str::<Value>(line).unwrap();
64 }
65 }
66
67 #[test]
68 fn round_trips_through_the_reader() {
69 let convo = conversation();
70 let file = tempfile::NamedTempFile::new().unwrap();
71 ConversationWriter::write_conversation(&convo, file.as_file()).unwrap();
72
73 let back = ConversationReader::read_conversation(file.path()).unwrap();
74 let entries = |c: &Conversation| serde_json::to_value(&c.entries).unwrap();
75 assert_eq!(entries(&back), entries(&convo));
76 assert_eq!(back.preamble, convo.preamble);
77 }
78}