#![allow(clippy::needless_doctest_main)]
use std::{
fmt::Display,
io::{Read, Write},
path::PathBuf,
process::{Child, ChildStdin, Stdio},
sync::mpsc,
};
use miette::Diagnostic;
#[derive(Debug)]
pub struct Error {
executable: String,
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Failed to launch '{}'", self.executable)
}
}
impl std::error::Error for Error {}
impl Diagnostic for Error {}
#[derive(Debug)]
struct Processor {
rx: mpsc::Receiver<u8>,
stdin: ChildStdin,
child: Child,
}
impl Processor {
pub fn new(child: Child) -> Self {
let mut child = child;
let (tx, rx) = mpsc::channel::<u8>();
let mut stdout = child.stdout.take().unwrap();
let stdin = child.stdin.take().unwrap();
std::thread::spawn(move || {
let mut buffer = [0u8; 1024];
loop {
if let Ok(len) = stdout.read(&mut buffer) {
for b in &buffer[..len] {
if tx.send(*b).is_err() {
break;
}
}
}
}
});
Self { rx, stdin, child }
}
pub fn try_receive(&mut self) -> Vec<u8> {
let mut res = Vec::new();
while let Ok(b) = self.rx.try_recv() {
res.push(b);
}
res
}
pub fn send(&mut self, data: Vec<u8>) {
let _ignored = self.stdin.write(&data).ok();
}
}
impl Drop for Processor {
fn drop(&mut self) {
self.child.kill().unwrap();
}
}
#[derive(Debug)]
pub struct ExternalProcessors {
processors: Vec<Processor>,
}
impl ExternalProcessors {
pub fn new(processors: Option<String>, elf: Option<PathBuf>) -> Result<Self, Error> {
let mut args = Vec::new();
if let Some(elf) = elf {
args.push(elf.as_os_str().to_str().unwrap().to_string());
};
let mut spawned = Vec::new();
if let Some(processors) = processors {
for processor in processors.split(",") {
let processor = std::process::Command::new(processor)
.args(args.clone())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.spawn()
.map_err(|_| Error {
executable: processor.to_string(),
})?;
spawned.push(Processor::new(processor));
}
}
Ok(Self {
processors: spawned,
})
}
pub fn process(&mut self, read: &[u8]) -> Vec<u8> {
let mut buffer = Vec::new();
buffer.extend_from_slice(read);
for processor in &mut self.processors {
processor.send(buffer);
buffer = processor.try_receive();
}
buffer
}
}