use std::sync::{Arc, Mutex};
use cortex_store::repo::DecayJobRepo;
use cortex_store::Pool;
use serde_json::{json, Value};
use crate::tool_handler::{GateId, ToolError, ToolHandler};
const VALID_STATES: &[&str] = &["pending", "in_progress", "completed", "failed", "cancelled"];
#[derive(Debug)]
pub struct CortexDecayStatusTool {
pool: Arc<Mutex<Pool>>,
}
impl CortexDecayStatusTool {
#[must_use]
pub fn new(pool: Arc<Mutex<Pool>>) -> Self {
Self { pool }
}
}
impl ToolHandler for CortexDecayStatusTool {
fn name(&self) -> &'static str {
"cortex_decay_status"
}
fn gate_set(&self) -> &'static [GateId] {
&[GateId::HealthRead]
}
fn call(&self, params: Value) -> Result<Value, ToolError> {
let state_wire_owned: Option<String> = match params["state"].as_str() {
Some("") | None => None,
Some(s) => {
let wire = if s == "in-progress" { "in_progress" } else { s };
if !VALID_STATES.contains(&wire) {
return Err(ToolError::InvalidParams(format!(
"state must be one of {VALID_STATES:?}, got `{s}`"
)));
}
Some(wire.to_owned())
}
};
let pool = self
.pool
.lock()
.map_err(|err| ToolError::Internal(format!("pool lock poisoned: {err}")))?;
let repo = DecayJobRepo::new(&pool);
let mut jobs: Vec<Value> = Vec::new();
let states_to_query: Vec<&str> = match state_wire_owned.as_deref() {
Some(s) => vec![s],
None => VALID_STATES.to_vec(),
};
for wire in &states_to_query {
let records = repo.list_by_state(wire).map_err(|err| {
ToolError::Internal(format!("failed to read decay jobs (state={wire}): {err}"))
})?;
for record in records {
jobs.push(json!({
"id": record.id.to_string(),
"kind": record.kind_wire,
"state": record.state_wire,
"scheduled_for": record.scheduled_for.to_rfc3339(),
"created_at": record.created_at.to_rfc3339(),
}));
}
}
let total = jobs.len();
Ok(json!({ "jobs": jobs, "total": total }))
}
}