use super::session::OrcaBackend;
use crate::backend::*;
impl OrcaBackend {
pub(super) fn json(&self, args: Vec<String>) -> Result<Value> {
run_json(
self.runner.as_ref(),
&self.command,
&args,
None,
&BTreeMap::new(),
)
}
pub(super) fn show(&self, handle: &str) -> Result<Value> {
self.json(vec![
"terminal".into(),
"show".into(),
"--terminal".into(),
handle.into(),
"--json".into(),
])
}
fn list(&self, selector: Option<&str>) -> Result<Vec<Value>> {
let mut args = vec!["terminal".into(), "list".into()];
if let Some(selector) = selector {
args.push("--worktree".into());
args.push(selector.into());
}
args.push("--json".into());
let value = self.json(args)?;
Ok(value
.pointer("/terminals")
.and_then(Value::as_array)
.cloned()
.unwrap_or_default())
}
pub(super) fn rows(&self, selector: Option<&str>) -> Result<Vec<Value>> {
match self.list(selector) {
Ok(rows) => Ok(rows),
Err(error) if selector.is_some() && is_selector_not_found(&error) => self.list(None),
Err(error) => Err(error),
}
}
pub(super) fn close_handle(&self, handle: &str) -> Result<()> {
self.json(vec![
"terminal".into(),
"close".into(),
"--terminal".into(),
handle.into(),
"--json".into(),
])
.map(|_| ())
}
}
pub(super) const DEAD_STATUS: [&str; 3] = ["exited", "closed", "dead"];
const STALE_HANDLE_CODES: [&str; 2] = ["terminal_handle_stale", "terminal_gone"];
pub(super) fn is_stale(error: &anyhow::Error) -> bool {
failure_code(error).is_some_and(|code| STALE_HANDLE_CODES.contains(&code))
}
pub(super) fn is_gone(error: &anyhow::Error) -> bool {
is_stale(error) || failure_code(error) == Some("terminal_not_found")
}
fn is_selector_not_found(error: &anyhow::Error) -> bool {
failure_code(error) == Some("selector_not_found")
}
#[cfg(unix)]
fn shell_quote(value: &str) -> String {
format!("'{}'", value.replace('\'', "'\\''"))
}
#[cfg(windows)]
fn cmd_quote(value: &str) -> String {
format!("\"{}\"", value.replace('"', "\"\""))
}
#[cfg(unix)]
pub(super) fn spawn_command_posix(spec: &SpawnSpec) -> Result<String> {
if spec.command.is_empty() {
anyhow::bail!("orca spawn requires a command");
}
let mut command = format!("cd {} &&", shell_quote(&spec.cwd.to_string_lossy()));
if !spec.env.is_empty() {
command.push_str(" env");
for (key, value) in &spec.env {
command.push(' ');
command.push_str(&shell_quote(&format!("{key}={value}")));
}
}
for arg in &spec.command {
command.push(' ');
command.push_str(&shell_quote(arg));
}
command.push_str("; exit");
Ok(command)
}
#[cfg(windows)]
pub(super) fn spawn_command_cmd(spec: &SpawnSpec) -> Result<String> {
if spec.command.is_empty() {
anyhow::bail!("orca spawn requires a command");
}
let mut command = format!("cd /d {} &&", cmd_quote(&spec.cwd.to_string_lossy()));
for (key, value) in &spec.env {
command.push_str(" set ");
command.push_str(&format!("\"{key}={}\"", value.replace('"', "\"\"")));
command.push_str(" &&");
}
for arg in &spec.command {
command.push(' ');
command.push_str(&cmd_quote(arg));
}
command.push_str(" & exit");
Ok(command)
}
pub(super) fn spawn_command(spec: &SpawnSpec) -> Result<String> {
#[cfg(unix)]
{
spawn_command_posix(spec)
}
#[cfg(windows)]
{
spawn_command_cmd(spec)
}
}