use log::debug; use serde::Serialize; use std::path::{Path, PathBuf}; use std::process::Command; use tempfile::{tempdir, TempDir}; #[derive(Debug, thiserror::Error)] pub enum ObnamClientError { #[error("failed to create temporary directory for server: {0}")] TempDir(std::io::Error), #[error("failed to write client configuration to {0}: {1}")] WriteConfig(PathBuf, std::io::Error), #[error("failed to run obnam: {0}")] Run(std::io::Error), } #[derive(Debug)] pub struct ObnamClient { binary: PathBuf, #[allow(dead_code)] tempdir: TempDir, #[allow(dead_code)] config: PathBuf, } impl ObnamClient { pub fn new( client_binary: &Path, server_url: String, root: PathBuf, ) -> Result { debug!("creating ObnamClient"); let tempdir = tempdir().map_err(ObnamClientError::TempDir)?; let config_filename = tempdir.path().join("client.yaml"); let config = ClientConfig::new(server_url, root); config.write(&config_filename)?; Ok(Self { binary: client_binary.to_path_buf(), tempdir, config: config_filename, }) } pub fn run(&self, args: &[&str]) -> Result { let output = Command::new(&self.binary) .arg("--config") .arg(&self.config) .args(args) .output() .map_err(ObnamClientError::Run)?; if output.status.code() != Some(0) { eprintln!("{}", String::from_utf8_lossy(&output.stdout)); eprintln!("{}", String::from_utf8_lossy(&output.stderr)); std::process::exit(1); } Ok(String::from_utf8_lossy(&output.stdout).to_string()) } } #[derive(Debug, Serialize)] pub struct ClientConfig { server_url: String, verify_tls_cert: bool, roots: Vec, log: PathBuf, } impl ClientConfig { fn new(server_url: String, root: PathBuf) -> Self { Self { server_url, verify_tls_cert: false, roots: vec![root], log: PathBuf::from("obnam.log"), } } fn write(&self, filename: &Path) -> Result<(), ObnamClientError> { std::fs::write(filename, serde_yaml::to_string(self).unwrap()) .map_err(|err| ObnamClientError::WriteConfig(filename.to_path_buf(), err))?; Ok(()) } }