use std::error::Error;
use std::fmt;
use std::path::Path;
use std::process::Command;
use crate::channel::AgentRequestKind;
use crate::model::agent::ReasoningLevel;
use crate::model::turn_prompt::TurnPromptAttachment;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AgentTransport {
AppServer,
Cli,
}
impl AgentTransport {
pub fn uses_app_server(self) -> bool {
matches!(self, Self::AppServer)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum AgentPromptTransport {
Argv,
Stdin,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum AppServerThoughtPolicy {
None,
PhaseLabel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BuildCommandRequest<'a> {
pub attachments: &'a [TurnPromptAttachment],
pub folder: &'a Path,
pub main_checkout_root: Option<&'a Path>,
pub model: &'a str,
pub prompt: &'a str,
pub reasoning_level: ReasoningLevel,
pub request_kind: &'a AgentRequestKind,
pub replay_transcript: Option<&'a str>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AgentBackendError {
Setup(String),
CommandBuild(String),
}
impl fmt::Display for AgentBackendError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Setup(message) | Self::CommandBuild(message) => {
write!(formatter, "{message}")
}
}
}
}
impl Error for AgentBackendError {}
#[cfg_attr(any(test, feature = "test-utils"), mockall::automock)]
pub trait AgentBackend: Send + Sync {
fn setup(&self, folder: &Path) -> Result<(), AgentBackendError>;
fn build_command<'request>(
&'request self,
request: BuildCommandRequest<'request>,
) -> Result<Command, AgentBackendError>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agent_transport_app_server_uses_app_server() {
let transport = AgentTransport::AppServer;
let result = transport.uses_app_server();
assert!(result);
}
#[test]
fn test_agent_transport_cli_does_not_use_app_server() {
let transport = AgentTransport::Cli;
let result = transport.uses_app_server();
assert!(!result);
}
#[test]
fn test_agent_backend_error_setup_displays_message() {
let error = AgentBackendError::Setup("setup failed".to_string());
let display = format!("{error}");
assert_eq!(display, "setup failed");
}
#[test]
fn test_agent_backend_error_command_build_displays_message() {
let error = AgentBackendError::CommandBuild("build failed".to_string());
let display = format!("{error}");
assert_eq!(display, "build failed");
}
#[test]
fn test_agent_backend_error_implements_std_error() {
let error = AgentBackendError::Setup("test error".to_string());
let std_error: &dyn Error = &error;
assert_eq!(std_error.to_string(), "test error");
assert!(std_error.source().is_none());
}
#[test]
fn test_agent_backend_error_setup_and_command_build_are_distinct() {
let setup_error = AgentBackendError::Setup("failure".to_string());
let build_error = AgentBackendError::CommandBuild("failure".to_string());
assert_ne!(setup_error, build_error);
}
}