use std::path::PathBuf;
use futures::future::BoxFuture;
use thiserror::Error;
use crate::error::BoxError;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TerminalId(String);
impl TerminalId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<TerminalId> for String {
fn from(value: TerminalId) -> Self {
value.0
}
}
#[derive(Debug, Clone)]
pub struct ShellOutput {
pub text: String,
pub truncated: bool,
pub exit_status: Option<TerminalExitStatus>,
}
#[derive(Debug, Clone)]
pub struct TerminalExitStatus {
pub exit_code: Option<i32>,
pub signal: Option<String>,
}
pub trait ShellBackend: Send + Sync {
fn create(
&self,
command: String,
cwd: PathBuf,
) -> BoxFuture<'_, Result<TerminalId, ShellError>>;
fn output(&self, id: &TerminalId) -> BoxFuture<'_, Result<ShellOutput, ShellError>>;
fn wait_for_exit(
&self,
id: &TerminalId,
) -> BoxFuture<'_, Result<TerminalExitStatus, ShellError>>;
fn release(&self, id: &TerminalId) -> BoxFuture<'_, Result<(), ShellError>>;
fn kill(&self, id: &TerminalId) -> BoxFuture<'_, Result<(), ShellError>>;
}
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum ShellError {
#[error("terminal not found: {0:?}")]
NotFound(TerminalId),
#[error("shell backend failure: {0}")]
Backend(#[source] BoxError),
#[error("operation not permitted: {0}")]
NotPermitted(String),
}
pub struct NoopShellBackend;
impl ShellBackend for NoopShellBackend {
fn create(
&self,
_command: String,
_cwd: PathBuf,
) -> BoxFuture<'_, Result<TerminalId, ShellError>> {
Box::pin(async {
Err(ShellError::NotPermitted(
"NoopShellBackend cannot spawn".to_string(),
))
})
}
fn output(&self, id: &TerminalId) -> BoxFuture<'_, Result<ShellOutput, ShellError>> {
let id = id.clone();
Box::pin(async move { Err(ShellError::NotFound(id)) })
}
fn wait_for_exit(
&self,
id: &TerminalId,
) -> BoxFuture<'_, Result<TerminalExitStatus, ShellError>> {
let id = id.clone();
Box::pin(async move { Err(ShellError::NotFound(id)) })
}
fn release(&self, _id: &TerminalId) -> BoxFuture<'_, Result<(), ShellError>> {
Box::pin(async { Ok(()) })
}
fn kill(&self, id: &TerminalId) -> BoxFuture<'_, Result<(), ShellError>> {
let id = id.clone();
Box::pin(async move { Err(ShellError::NotFound(id)) })
}
}