87 lines
2.4 KiB
Rust
87 lines
2.4 KiB
Rust
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<ExitCode> {
|
|
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))
|
|
}
|