use std::fs::File;
use std::os::unix::fs::FileExt;
use std::process::Stdio;
pub struct Capture {
out: File,
err: File,
}
fn anonymous_file() -> std::io::Result<File> {
#[cfg(target_os = "linux")]
{
use std::os::unix::io::FromRawFd;
let descriptor: libc::c_int = unsafe { libc::memfd_create(c"cli-git-output".as_ptr(), libc::MFD_CLOEXEC) };
if descriptor < 0 {
return Err(std::io::Error::last_os_error());
}
return Ok(unsafe { File::from_raw_fd(descriptor) });
}
#[cfg(not(target_os = "linux"))]
{
return Err(std::io::Error::new(std::io::ErrorKind::Unsupported, "anonymous output files need memfd_create, which this system lacks"));
}
}
impl Capture {
pub fn new() -> std::io::Result<Self> {
return Ok(Self { out: anonymous_file()?, err: anonymous_file()? });
}
pub fn stdio(&self) -> std::io::Result<(Stdio, Stdio)> {
return Ok((Stdio::from(self.out.try_clone()?), Stdio::from(self.err.try_clone()?)));
}
pub fn read(&self) -> (Vec<u8>, Vec<u8>) {
return (read_all(&self.out), read_all(&self.err));
}
}
fn read_all(file: &File) -> Vec<u8> {
let mut collected: Vec<u8> = Vec::new();
let mut buffer: [u8; 8192] = [0; 8192];
let mut position: u64 = 0;
loop {
match file.read_at(&mut buffer, position) {
Ok(0) | Err(_) => return collected,
Ok(read) => {
collected.extend_from_slice(&buffer[..read]);
position += read as u64;
}
}
}
}
#[cfg(test)]
#[path = "output_capture_tests.rs"]
mod tests;