Skip to main content

acp_cli/session/
history.rs

1use serde::{Deserialize, Serialize};
2use std::fs::{self, OpenOptions};
3use std::io::{self, BufRead, Write};
4
5use super::scoping::session_dir;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ConversationEntry {
9    pub role: String,
10    pub content: String,
11    pub timestamp: u64,
12}
13
14/// Return the path to the conversation log file for a given session key.
15fn log_path(session_key: &str) -> std::path::PathBuf {
16    session_dir().join(format!("{session_key}.log.jsonl"))
17}
18
19/// Append an entry to the conversation log file.
20pub fn append_entry(session_key: &str, entry: &ConversationEntry) -> io::Result<()> {
21    let path = log_path(session_key);
22    if let Some(parent) = path.parent() {
23        fs::create_dir_all(parent)?;
24    }
25    let mut file = OpenOptions::new().create(true).append(true).open(&path)?;
26    let json =
27        serde_json::to_string(entry).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
28    writeln!(file, "{json}")?;
29    Ok(())
30}
31
32/// Load all entries from the conversation log file.
33pub fn load_history(session_key: &str) -> io::Result<Vec<ConversationEntry>> {
34    let path = log_path(session_key);
35    let file = match fs::File::open(&path) {
36        Ok(f) => f,
37        Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(Vec::new()),
38        Err(e) => return Err(e),
39    };
40    let reader = io::BufReader::new(file);
41    let mut entries = Vec::new();
42    for line in reader.lines() {
43        let line = line?;
44        if line.trim().is_empty() {
45            continue;
46        }
47        let entry: ConversationEntry = serde_json::from_str(&line)
48            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
49        entries.push(entry);
50    }
51    Ok(entries)
52}