1use std::path::Path;
2use tokio::io::AsyncWriteExt;
3
4use crate::model::ChatMessage;
5
6pub async fn load_context(path: &Path) -> Vec<ChatMessage> {
15 let content = match tokio::fs::read_to_string(path).await {
16 Ok(c) => c,
17 Err(e) if e.kind() == std::io::ErrorKind::NotFound => return vec![],
18 Err(e) => {
19 tracing::error!(path = %path.display(), error = %e, "context load failed");
20 return vec![];
21 }
22 };
23 content
24 .lines()
25 .filter(|l| !l.trim().is_empty())
26 .filter_map(|line| {
27 match serde_json::from_str::<ChatMessage>(line) {
28 Ok(msg) => Some(msg),
29 Err(e) => {
30 tracing::warn!(error = %e, "skipping malformed context line");
31 None
32 }
33 }
34 })
35 .collect()
36}
37
38pub async fn append_context(path: &Path, messages: &[ChatMessage]) {
44 if messages.is_empty() {
45 return;
46 }
47 if let Some(parent) = path.parent() {
48 if !parent.as_os_str().is_empty() {
49 let _ = tokio::fs::create_dir_all(parent).await;
50 }
51 }
52 let mut file = match tokio::fs::OpenOptions::new()
53 .create(true)
54 .append(true)
55 .open(path)
56 .await
57 {
58 Ok(f) => f,
59 Err(e) => {
60 tracing::error!(path = %path.display(), error = %e, "context append open failed");
61 return;
62 }
63 };
64 for msg in messages {
65 match serde_json::to_string(msg) {
66 Ok(line) => {
67 let _ = file.write_all(line.as_bytes()).await;
68 let _ = file.write_all(b"\n").await;
69 }
70 Err(e) => tracing::warn!(error = %e, "context message serialize failed; skipping"),
71 }
72 }
73}
74
75pub async fn rewrite_context(path: &Path, messages: &[ChatMessage]) {
80 if let Some(parent) = path.parent() {
81 if !parent.as_os_str().is_empty() {
82 let _ = tokio::fs::create_dir_all(parent).await;
83 }
84 }
85 let mut content = String::with_capacity(messages.len() * 128);
86 for msg in messages {
87 match serde_json::to_string(msg) {
88 Ok(line) => {
89 content.push_str(&line);
90 content.push('\n');
91 }
92 Err(e) => tracing::warn!(error = %e, "context message serialize failed; skipping"),
93 }
94 }
95 if let Err(e) = tokio::fs::write(path, &content).await {
96 tracing::error!(path = %path.display(), error = %e, "context rewrite failed");
97 }
98}