use sqlx::SqlitePool;
use std::{future::Future, sync::Arc, time::Instant};
use crate::{
config::BranchConfig,
error::{BranchError, BranchResult},
sandbox::environment::{SandboxStatus, SimulationEnvironment},
types::SimulationOutcome,
};
pub struct SandboxRunner {
pub env: SimulationEnvironment,
pub config: Arc<BranchConfig>,
}
impl SandboxRunner {
pub fn new(env: SimulationEnvironment, config: Arc<BranchConfig>) -> Self {
Self { env, config }
}
pub async fn run<F, Fut>(&mut self, agent_fn: F) -> BranchResult<SimulationOutcome>
where
F: FnOnce(SqlitePool) -> Fut,
Fut: Future<Output = BranchResult<serde_json::Value>>,
{
let started = Instant::now();
let pool = self.env.branch_pool().await?;
let agent_result = agent_fn(pool).await;
let duration_ms = started.elapsed().as_millis() as u64;
match agent_result {
Ok(agent_output) => {
let outcome = SimulationOutcome {
agent_output,
ops_executed: 1, duration_ms,
};
self.env.status = SandboxStatus::Completed {
outcome: outcome.clone(),
};
Ok(outcome)
}
Err(error) => {
self.env.status = SandboxStatus::Failed(error.to_string());
Err(error)
}
}
}
pub async fn run_with_timeout<F, Fut>(
&mut self,
agent_fn: F,
timeout_secs: u64,
) -> BranchResult<SimulationOutcome>
where
F: FnOnce(SqlitePool) -> Fut,
Fut: Future<Output = BranchResult<serde_json::Value>>,
{
let pool = self.env.branch_pool().await?;
let duration = std::time::Duration::from_secs(timeout_secs);
let started = Instant::now();
let agent_result = tokio::time::timeout(duration, agent_fn(pool)).await;
let elapsed_ms = started.elapsed().as_millis() as u64;
match agent_result {
Ok(Ok(agent_output)) => {
let outcome = SimulationOutcome {
agent_output,
ops_executed: 1,
duration_ms: elapsed_ms,
};
self.env.status = SandboxStatus::Completed {
outcome: outcome.clone(),
};
Ok(outcome)
}
Ok(Err(error)) => {
self.env.status = SandboxStatus::Failed(error.to_string());
Err(error)
}
Err(_elapsed) => {
let msg = format!("sandbox timed out after {timeout_secs}s");
self.env.status = SandboxStatus::Failed(msg.clone());
Err(BranchError::SandboxError(msg))
}
}
}
pub fn into_env(self) -> SimulationEnvironment {
self.env
}
}