ref:main
//! Pluggable backend system for terminal sessions.
//!
//! Backends provide the I/O pipe that `TerminalState` reads from and writes to.
//! The terminal emulation (VTE parsing, grid, renderer) stays in `TerminalState`;
//! a backend is just a raw byte transport — it knows nothing about escape codes.
pub mod docker;
pub mod plain;
use std::collections::HashMap;
/// Configuration passed to a backend when spawning a session.
pub struct BackendConfig {
pub cols: u16,
pub rows: u16,
pub font_size: f32,
pub shell: String,
pub working_dir: String,
// Docker-specific (ignored by PlainShellBackend)
pub image: Option<String>,
pub memory_limit: Option<String>,
pub cpu_limit: Option<f64>,
pub network_enabled: Option<bool>,
}
/// A backend knows how to spawn terminal sessions of a particular kind.
pub trait TerminalBackend: Send + Sync {
/// Spawn a new session with the given configuration.
fn spawn(&self, config: &BackendConfig) -> Result<Box<dyn TerminalSession>, String>;
/// Human-readable name (e.g. "plain", "docker").
fn name(&self) -> &'static str;
/// Check whether this backend can run on the current system.
fn is_available(&self) -> Result<bool, String>;
}
/// A running terminal session — a raw byte pipe plus lifecycle controls.
///
/// `read` / `write` use `std::io::Error` to match standard I/O conventions.
/// `resize` / `kill` use `String` for simple error messages.
pub trait TerminalSession: Send {
/// Read output from the session.
/// Returns `Ok(0)` or `Err(WouldBlock)` when nothing is available.
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error>;
/// Write input to the session.
fn write(&mut self, data: &[u8]) -> Result<usize, std::io::Error>;
/// Resize the terminal dimensions.
fn resize(&mut self, cols: u16, rows: u16) -> Result<(), String>;
/// Kill the session and its child process.
fn kill(&mut self) -> Result<(), String>;
/// Check if the session is still alive.
fn is_alive(&self) -> bool;
}
/// Registry of available backends, keyed by name.
pub struct BackendRegistry {
backends: HashMap<String, Box<dyn TerminalBackend>>,
}
impl Default for BackendRegistry {
fn default() -> Self {
Self::new()
}
}
impl BackendRegistry {
pub fn new() -> Self {
let mut backends: HashMap<String, Box<dyn TerminalBackend>> = HashMap::new();
backends.insert("plain".to_string(), Box::new(plain::PlainShellBackend));
backends.insert("docker".to_string(), Box::new(docker::DockerBackend));
Self { backends }
}
/// Look up a backend by name.
pub fn get(&self, name: &str) -> Option<&dyn TerminalBackend> {
self.backends.get(name).map(|b| b.as_ref())
}
/// Return names of backends that report themselves as available.
pub fn available_backends(&self) -> Vec<&str> {
self.backends
.values()
.filter(|b| b.is_available().unwrap_or(false))
.map(|b| b.name())
.collect()
}
}