use std::time::SystemTime;
use crate::error::MemoryError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MessageRow {
pub id: String,
pub conversation_id: String,
pub role: String,
pub content: String,
pub timestamp: SystemTime,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HistoryRow {
pub conversation_id: String,
pub summary: String,
pub updated_at: SystemTime,
}
pub(crate) fn parse_sqlite_timestamp(s: &str) -> Result<SystemTime, MemoryError> {
let naive = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S")
.map_err(|e| MemoryError::logic(format!("parse timestamp {s:?}: {e}")))?;
Ok(naive.and_utc().into())
}
pub(crate) fn format_sqlite_timestamp(t: SystemTime) -> String {
let dt: chrono::DateTime<chrono::Utc> = t.into();
dt.format("%Y-%m-%d %H:%M:%S").to_string()
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn parse_roundtrips_through_format() {
let original = SystemTime::UNIX_EPOCH + Duration::from_secs(1_600_000_000);
let formatted = format_sqlite_timestamp(original);
let parsed = parse_sqlite_timestamp(&formatted).unwrap();
assert_eq!(parsed, original);
}
#[test]
fn parse_rejects_garbage() {
let err = parse_sqlite_timestamp("not a timestamp").unwrap_err();
assert!(matches!(err, MemoryError::Logic(_)));
}
#[test]
fn parse_accepts_schema_default_shape() {
let parsed = parse_sqlite_timestamp("2026-05-11 18:00:00").unwrap();
let formatted = format_sqlite_timestamp(parsed);
assert_eq!(formatted, "2026-05-11 18:00:00");
}
}