use std::ffi::OsString; use std::process::Command; use anyhow::{Context, Result, bail}; use crate::package_installer::{PackageInstaller, PackageRequest}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Invocation { program: OsString, arguments: Vec, } impl Invocation { pub fn new( program: impl Into, arguments: impl IntoIterator>, ) -> Self { Self { program: program.into(), arguments: arguments.into_iter().map(Into::into).collect(), } } pub fn program(&self) -> &OsString { &self.program } pub fn arguments(&self) -> &[OsString] { &self.arguments } } pub trait CommandRunner { fn run(&self, invocation: Invocation) -> Result<()>; } #[derive(Debug, Default, Clone, Copy)] pub struct ProcessRunner; impl CommandRunner for ProcessRunner { fn run(&self, invocation: Invocation) -> Result<()> { let status = Command::new(&invocation.program) .args(&invocation.arguments) .status() .with_context(|| format!("run {}", invocation.program.to_string_lossy()))?; if !status.success() { bail!( "{} exited with {status}", invocation.program.to_string_lossy() ); } Ok(()) } } #[derive(Debug)] pub struct HasherInstaller { runner: R, } impl HasherInstaller { pub fn new(runner: R) -> Self { Self { runner } } pub fn runner(&self) -> &R { &self.runner } } impl PackageInstaller for HasherInstaller { fn install(&self, request: &PackageRequest) -> Result<()> { self.runner.run(Invocation::new( "hsh", [ "--mountpoints=/proc".into(), "--initroot-only".into(), "--apt-config".into(), request.apt_config().as_path().as_os_str().to_owned(), "--workdir".into(), request.workdir().as_os_str().to_owned(), ], ))?; let mut arguments = vec![ OsString::from("--mountpoints=/proc"), OsString::from("--workdir"), request.workdir().as_os_str().to_owned(), ]; arguments.extend(request.selectors().iter().map(OsString::from)); arguments.extend(request.packages().iter().map(OsString::from)); self.runner.run(Invocation::new("hsh-install", arguments)) } }