use std::sync::Arc;
use sacp::schema::{TerminalExitStatus, TerminalId, TerminalOutputResponse};
use super::TerminalClient;
use crate::types::AgentError;
#[derive(Debug)]
pub struct TerminalHandle {
terminal_id: TerminalId,
client: Arc<TerminalClient>,
released: bool,
}
impl TerminalHandle {
pub fn new(terminal_id: TerminalId, client: Arc<TerminalClient>) -> Self {
Self {
terminal_id,
client,
released: false,
}
}
pub fn id(&self) -> &TerminalId {
&self.terminal_id
}
pub fn id_str(&self) -> &str {
self.terminal_id.0.as_ref()
}
pub async fn output(&self) -> Result<TerminalOutputResponse, AgentError> {
self.client.output(self.terminal_id.clone()).await
}
pub async fn wait_for_exit(&self) -> Result<TerminalExitStatus, AgentError> {
let response = self.client.wait_for_exit(self.terminal_id.clone()).await?;
Ok(response.exit_status)
}
pub async fn kill(&self) -> Result<(), AgentError> {
self.client.kill(self.terminal_id.clone()).await?;
Ok(())
}
pub async fn release(mut self) -> Result<(), AgentError> {
self.released = true;
self.client.release(self.terminal_id.clone()).await?;
Ok(())
}
pub async fn execute_and_wait(&self) -> Result<(String, TerminalExitStatus), AgentError> {
let exit_status = self.wait_for_exit().await?;
let output_response = self.output().await?;
Ok((output_response.output, exit_status))
}
pub fn is_released(&self) -> bool {
self.released
}
}
impl Drop for TerminalHandle {
fn drop(&mut self) {
if !self.released {
tracing::warn!(
terminal_id = %self.id_str(),
"TerminalHandle dropped without explicit release, \
terminal will be cleaned up by client timeout"
);
}
}
}
#[derive(Debug)]
#[allow(dead_code)] pub struct TerminalBuilder {
client: Arc<TerminalClient>,
command: String,
args: Vec<String>,
cwd: Option<std::path::PathBuf>,
output_byte_limit: Option<u64>,
}
#[allow(dead_code)] impl TerminalBuilder {
pub fn new(client: Arc<TerminalClient>, command: impl Into<String>) -> Self {
Self {
client,
command: command.into(),
args: Vec::new(),
cwd: None,
output_byte_limit: None,
}
}
pub fn args(mut self, args: Vec<String>) -> Self {
self.args = args;
self
}
pub fn arg(mut self, arg: impl Into<String>) -> Self {
self.args.push(arg.into());
self
}
pub fn cwd(mut self, cwd: impl Into<std::path::PathBuf>) -> Self {
self.cwd = Some(cwd.into());
self
}
pub fn output_byte_limit(mut self, limit: u64) -> Self {
self.output_byte_limit = Some(limit);
self
}
pub async fn create(self) -> Result<TerminalHandle, AgentError> {
let terminal_id = self
.client
.create(self.command, self.args, self.cwd, self.output_byte_limit)
.await?;
Ok(TerminalHandle::new(terminal_id, self.client))
}
}
#[cfg(test)]
mod tests {
}