use crate::model::{project, session::ChatItem};
use serde::{Deserialize, Serialize};
use std::{
cmp::Reverse,
path::{Path, PathBuf},
time::{Duration, SystemTime, UNIX_EPOCH},
};
#[derive(Serialize, Deserialize)]
pub struct Record {
pub agent: String,
#[serde(default)]
pub session: Option<String>,
pub title: String,
pub name: Option<String>,
pub updated: u64,
#[serde(default)]
pub closed: bool,
pub items: Vec<ChatItem>,
}
impl Record {
pub fn at(&self) -> SystemTime {
UNIX_EPOCH + Duration::from_secs(self.updated)
}
}
const SESSIONS: &str = "sessions";
fn dir(project: &Path) -> PathBuf {
project::dir(project).join(SESSIONS)
}
pub fn list(project: &Path) -> Vec<(PathBuf, Record)> {
let Ok(entries) = std::fs::read_dir(dir(project)) else {
return Vec::new();
};
let mut found: Vec<(PathBuf, Record)> = entries
.flatten()
.map(|entry| entry.path())
.filter(|path| path.extension().is_some_and(|ext| ext == "json"))
.filter_map(|path| {
let body = std::fs::read_to_string(&path).ok()?;
Some((path, serde_json::from_str(&body).ok()?))
})
.collect();
found.sort_by_key(|(_, record)| Reverse(record.updated));
found
}
pub fn create(project: &Path) -> Option<PathBuf> {
let dir = project::init(project).ok()?.join(SESSIONS);
std::fs::create_dir_all(&dir).ok()?;
let stamp = project::stamp();
let mut path = dir.join(format!("{stamp}.json"));
for n in 2.. {
if !path.exists() {
break;
}
path = dir.join(format!("{stamp}-{n}.json"));
}
Some(path)
}
pub fn write(file: &Path, record: &Record) {
if let Ok(body) = serde_json::to_string_pretty(record) {
let _ = std::fs::write(file, body);
}
}
pub fn remove(file: &Path) {
let _ = std::fs::remove_file(file);
}