use std::{ fs::File, process::{Command, ExitCode}, }; use anyhow::{Context, Result, bail}; use clap::Parser; mod cli; mod config; use cli::Cli; use config::{Source, Repository, Config}; fn main() -> Result { let (repository, source, options) = setup()?; let exit_status = Command::new("sh").arg("-c") .arg("restic cat config || restic init") .env("RESTIC_REPOSITORY", &repository.url) .env("RESTIC_PASSWORD", &repository.password) .spawn()?.wait()?; if !exit_status.success() { bail!("Couldn't ensure repository is initialized!") } let mut backup_command = Command::new("sh"); backup_command .arg("-c") .env("RESTIC_REPOSITORY", &repository.url) .env("RESTIC_PASSWORD", &repository.password); let command = match source { Source::Path { path } => backup_command.arg(format!( "restic backup {} {}", options, path.to_string_lossy() )), Source::Stdin { command, filename } => backup_command.arg(format!( "{} | restic backup {} --stdin --stdin-filename '{}'", command, options, filename.to_string_lossy(), )), }; if let Some(code) = command.spawn()?.wait()?.code() { let code: u8 = code.try_into()?; Ok(ExitCode::from(code)) } else { Ok(ExitCode::SUCCESS) } } fn setup() -> Result<(Repository, Source, String)> { let args = Cli::parse(); let mut config: Config = serde_yaml::from_reader(File::open(&args.config)?)?; let repository = config.repositories.remove(&args.repository).with_context(|| { format!( "Repository {} not contained in config file {}", args.repository, &args.config.to_string_lossy() ) })?; let source = config.sources.remove(&args.source).with_context(|| { format!( "Source {} not contained in config file {}", args.source, &args.config.to_string_lossy() ) })?; let options = match args.options { Some(options) => config.options.remove(&options).with_context(|| { format!( "Options {} not contained in config file {}", &options, &args.config.to_string_lossy() ) })?, None => Default::default(), }; Ok((repository, source, options)) }