use super::protocol::{HistoryEntry, HistoryParams, HistoryResult};
use crate::history::{HistoryQuery, HistoryStore, parse_since};
use std::sync::OnceLock;
static HISTORY_STORE: OnceLock<HistoryStore> = OnceLock::new();
pub fn set_history_store(history: HistoryStore) {
if HISTORY_STORE.set(history).is_err() {
log::warn!("History store already initialized; ignoring duplicate set_history_store call");
}
}
pub(crate) enum HistoryQueryError {
InvalidParams(String),
Internal(String),
}
pub(crate) fn query_history(params: &HistoryParams) -> Result<HistoryResult, HistoryQueryError> {
if params.limit < 1 {
return Err(HistoryQueryError::InvalidParams(format!(
"limit must be positive, got {}",
params.limit
)));
}
let store = HISTORY_STORE.get().ok_or_else(|| {
HistoryQueryError::Internal(
"History is not available (no history store configured)".to_string(),
)
})?;
let session_id = store.session();
if !params.all_sessions && session_id.is_none() {
return Err(HistoryQueryError::Internal(
"Session ID is not available; use all_sessions=true to query without session scope"
.to_string(),
));
}
let since = params
.since
.as_deref()
.map(|since| {
parse_since(since).map_err(|_| {
HistoryQueryError::InvalidParams(format!(
"Invalid 'since' format: {since}. \
Use RFC 3339 (e.g. '2026-03-29T00:00:00Z') or date (e.g. '2026-03-29')"
))
})
})
.transpose()?;
let rows = store
.query(&HistoryQuery {
limit: params.limit,
all_sessions: params.all_sessions,
cwd: params.cwd.clone(),
grep: params.grep.clone(),
since,
})
.map_err(|e| {
log::error!("Strict history query failed: {e}");
HistoryQueryError::Internal(format!("History query failed: {e}"))
})?;
let entries = rows
.into_iter()
.map(|row| HistoryEntry {
command: row.command_line,
timestamp: row.start_timestamp.map(|time| time.to_rfc3339()),
cwd: row.cwd,
exit_status: row.exit_status,
session_id: row.session_id.map(i64::from),
})
.collect();
Ok(HistoryResult {
entries,
session_id: session_id.map(i64::from),
})
}