use crate::error::RunLogError;
use crate::types::{RunLog, RunStatus};
use async_trait::async_trait;
use klieo_core::ids::RunId;
use std::collections::HashMap;
use tokio::sync::Mutex;
#[derive(Debug, Clone, Default)]
pub struct RunLogQuery {
pub agent: Option<String>,
pub status: Option<RunStatus>,
pub limit: Option<usize>,
pub offset: usize,
}
impl RunLogQuery {
pub fn recent(limit: usize) -> Self {
Self {
limit: Some(limit),
..Self::default()
}
}
}
#[async_trait]
pub trait RunLogStore: Send + Sync {
async fn put(&self, run_log: &RunLog) -> Result<(), RunLogError>;
async fn get(&self, run_id: RunId) -> Result<Option<RunLog>, RunLogError>;
async fn list(&self, query: &RunLogQuery) -> Result<Vec<RunLog>, RunLogError>;
async fn delete(&self, run_id: RunId) -> Result<(), RunLogError>;
}
#[derive(Default)]
pub struct InMemoryRunLogStore {
inner: Mutex<HashMap<RunId, RunLog>>,
}
impl InMemoryRunLogStore {
pub fn new() -> Self {
Self::default()
}
}
#[async_trait]
impl RunLogStore for InMemoryRunLogStore {
async fn put(&self, run_log: &RunLog) -> Result<(), RunLogError> {
let mut g = self.inner.lock().await;
g.insert(run_log.run_id, run_log.clone());
Ok(())
}
async fn get(&self, run_id: RunId) -> Result<Option<RunLog>, RunLogError> {
let g = self.inner.lock().await;
Ok(g.get(&run_id).cloned())
}
async fn list(&self, query: &RunLogQuery) -> Result<Vec<RunLog>, RunLogError> {
let g = self.inner.lock().await;
let mut rows: Vec<RunLog> = g
.values()
.filter(|r| query.agent.as_deref().is_none_or(|a| r.agent == a))
.filter(|r| query.status.is_none_or(|s| r.status == s))
.cloned()
.collect();
rows.sort_by_key(|row| std::cmp::Reverse(row.started_at));
Ok(rows
.into_iter()
.skip(query.offset)
.take(query.limit.unwrap_or(usize::MAX))
.collect())
}
async fn delete(&self, run_id: RunId) -> Result<(), RunLogError> {
let mut g = self.inner.lock().await;
g.remove(&run_id);
Ok(())
}
}