use super::RunId;
use super::run_control::RunControlCommand;
use super::run_primitive::RunPrimitive;
use super::run_receipt::RunBoundaryReceipt;
use crate::types::RunResult;
use serde_json::Value;
#[derive(Debug, Clone, thiserror::Error)]
#[non_exhaustive]
pub enum CoreExecutorError {
#[error("Apply failed: {reason}")]
ApplyFailed { reason: String },
#[error("Control failed: {reason}")]
ControlFailed { reason: String },
#[error("Executor is stopped")]
Stopped,
#[error("Internal error: {0}")]
Internal(String),
}
#[derive(Debug, Clone)]
pub enum CoreApplyTerminal {
RunResult(RunResult),
CallbackPending { tool_name: String, args: Value },
}
#[derive(Debug, Clone)]
pub struct CoreApplyOutput {
pub receipt: RunBoundaryReceipt,
pub session_snapshot: Option<Vec<u8>>,
pub run_result: Option<RunResult>,
pub terminal: Option<CoreApplyTerminal>,
}
impl CoreApplyOutput {
pub fn with_run_result(
receipt: RunBoundaryReceipt,
session_snapshot: Option<Vec<u8>>,
run_result: RunResult,
) -> Self {
Self {
receipt,
session_snapshot,
run_result: Some(run_result.clone()),
terminal: Some(CoreApplyTerminal::RunResult(run_result)),
}
}
pub fn with_callback_pending(
receipt: RunBoundaryReceipt,
session_snapshot: Option<Vec<u8>>,
tool_name: impl Into<String>,
args: Value,
) -> Self {
Self {
receipt,
session_snapshot,
run_result: None,
terminal: Some(CoreApplyTerminal::CallbackPending {
tool_name: tool_name.into(),
args,
}),
}
}
pub fn without_terminal(
receipt: RunBoundaryReceipt,
session_snapshot: Option<Vec<u8>>,
) -> Self {
Self {
receipt,
session_snapshot,
run_result: None,
terminal: None,
}
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
pub trait CoreExecutor: Send + Sync {
async fn apply(
&mut self,
run_id: RunId,
primitive: RunPrimitive,
) -> Result<CoreApplyOutput, CoreExecutorError>;
async fn control(&mut self, command: RunControlCommand) -> Result<(), CoreExecutorError>;
}
#[cfg(test)]
mod tests {
use super::*;
fn _assert_object_safe(_: &dyn CoreExecutor) {}
#[test]
fn core_executor_error_display() {
let err = CoreExecutorError::ApplyFailed {
reason: "bad input".into(),
};
assert_eq!(err.to_string(), "Apply failed: bad input");
let err = CoreExecutorError::ControlFailed {
reason: "not running".into(),
};
assert_eq!(err.to_string(), "Control failed: not running");
let err = CoreExecutorError::Stopped;
assert_eq!(err.to_string(), "Executor is stopped");
let err = CoreExecutorError::Internal("oops".into());
assert_eq!(err.to_string(), "Internal error: oops");
}
}