use serde::{Deserialize, Serialize};
use tocat_api::StderrMode;
use crate::{
child,
endpoint::{
Connection, EndpointStream,
parse::{Opt, ParseEndpointError},
},
};
#[derive(Debug, Deserialize, Serialize)]
pub struct Exec {
pub argv: Vec<String>,
#[serde(default)]
pub name: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct System {
pub command: String,
#[serde(default)]
pub name: Option<String>,
}
async fn spawn(
program: &str,
args: &[String],
shell: bool,
buffer: usize,
) -> anyhow::Result<Connection> {
let parts = child::spawn(program, args, shell, StderrMode::Inherit, buffer)?;
child::reap_in_background(parts.child);
Ok(EndpointStream::Split(Box::new(parts.stdout), Box::new(parts.stdin)).into_connection())
}
impl Exec {
const SCHEME: &'static str = "exec";
pub(super) fn parse<'a>(
body: &str,
opts: impl Iterator<Item = Opt<'a>>,
) -> Result<Self, ParseEndpointError> {
let argv: Vec<String> = body.split_whitespace().map(String::from).collect();
if argv.is_empty() {
return Err(ParseEndpointError::Empty);
}
let mut name = None;
for opt in opts {
match opt.key {
"name" => name = Some(opt.string()?),
_ => return Err(opt.unsupported(Self::SCHEME)),
}
}
Ok(Self { argv, name })
}
pub(super) fn label(&self) -> String {
format!("EXEC({})", self.argv.join(" "))
}
pub(super) async fn connect(&self, buffer: usize) -> anyhow::Result<Connection> {
let Some((program, args)) = self.argv.split_first() else {
anyhow::bail!("exec: empty argv");
};
spawn(program, args, false, buffer).await
}
}
impl System {
const SCHEME: &'static str = "system";
pub(super) fn parse<'a>(
body: &str,
opts: impl Iterator<Item = Opt<'a>>,
) -> Result<Self, ParseEndpointError> {
let mut name = None;
for opt in opts {
match opt.key {
"name" => name = Some(opt.string()?),
_ => return Err(opt.unsupported(Self::SCHEME)),
}
}
Ok(Self {
command: body.to_string(),
name,
})
}
pub(super) fn label(&self) -> String {
format!("SYSTEM({})", self.command)
}
pub(super) async fn connect(&self, buffer: usize) -> anyhow::Result<Connection> {
spawn(&self.command, &[], true, buffer).await
}
}