use log::debug;
use std::path::PathBuf;
use std::process::Stdio;
#[derive(Debug, Clone)]
pub struct AppServerBuilder {
command: PathBuf,
working_directory: Option<PathBuf>,
}
impl Default for AppServerBuilder {
fn default() -> Self {
Self::new()
}
}
impl AppServerBuilder {
pub fn new() -> Self {
Self {
command: PathBuf::from("codex"),
working_directory: None,
}
}
pub fn command<P: Into<PathBuf>>(mut self, path: P) -> Self {
self.command = path.into();
self
}
pub fn working_directory<P: Into<PathBuf>>(mut self, dir: P) -> Self {
self.working_directory = Some(dir.into());
self
}
fn resolve_command(&self) -> crate::error::Result<PathBuf> {
if self.command.is_absolute() {
return Ok(self.command.clone());
}
which::which(&self.command).map_err(|_| crate::error::Error::BinaryNotFound {
name: self.command.display().to_string(),
})
}
fn build_args(&self) -> Vec<String> {
vec![
"app-server".to_string(),
"--listen".to_string(),
"stdio://".to_string(),
]
}
#[cfg(feature = "async-client")]
pub async fn spawn(self) -> crate::error::Result<tokio::process::Child> {
let resolved = self.resolve_command()?;
let args = self.build_args();
debug!(
"[CLI] Spawning async app-server: {} {}",
resolved.display(),
args.join(" ")
);
let mut cmd = tokio::process::Command::new(&resolved);
cmd.args(&args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
if let Some(ref dir) = self.working_directory {
cmd.current_dir(dir);
}
cmd.spawn().map_err(crate::error::Error::Io)
}
pub fn spawn_sync(self) -> crate::error::Result<std::process::Child> {
let resolved = self.resolve_command()?;
let args = self.build_args();
debug!(
"[CLI] Spawning sync app-server: {} {}",
resolved.display(),
args.join(" ")
);
let mut cmd = std::process::Command::new(&resolved);
cmd.args(&args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
if let Some(ref dir) = self.working_directory {
cmd.current_dir(dir);
}
cmd.spawn().map_err(crate::error::Error::Io)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_args() {
let builder = AppServerBuilder::new();
let args = builder.build_args();
assert_eq!(args, vec!["app-server", "--listen", "stdio://"]);
}
#[test]
fn test_custom_command() {
let builder = AppServerBuilder::new().command("/usr/local/bin/codex");
assert_eq!(builder.command, PathBuf::from("/usr/local/bin/codex"));
}
#[test]
fn test_working_directory() {
let builder = AppServerBuilder::new().working_directory("/tmp/work");
assert_eq!(builder.working_directory, Some(PathBuf::from("/tmp/work")));
}
}