This commit is contained in:
JMARyA 2025-01-05 00:37:20 +01:00
commit b416507e4a
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
8 changed files with 326 additions and 0 deletions

66
src/backup.rs Normal file
View file

@ -0,0 +1,66 @@
use yansi::{Color, Paint};
use crate::{
config::{Config, RsyncConfig},
run_command,
};
pub fn ensure_exists(dir: &str) {
let exists = std::fs::exists(dir).unwrap_or_default();
let entries = std::fs::read_dir(dir)
.unwrap()
.flatten()
.collect::<Vec<_>>();
if !exists || entries.len() == 0 {
println!(
"{} {}",
"Error:".paint(Color::Red),
"Directory {dir} does not exists"
);
std::process::exit(1);
}
}
pub fn run_backup_rsync(conf: &RsyncConfig) {
println!("Running backup for {} -> {}", conf.src, conf.dest);
if let Some(dir) = &conf.ensure_exists {
ensure_exists(&dir);
}
let mut cmd = vec!["rsync", "-avzhruP"];
if conf.delete.unwrap_or_default() {
cmd.push("--delete");
}
if let Some(exclude) = &conf.exclude {
for e in exclude {
cmd.extend(&["--exclude", e.as_str()]);
}
}
cmd.push(&conf.src);
cmd.push(&conf.dest);
run_command(&cmd);
}
pub fn run_backup(conf: Config) {
if let Some(script) = &conf.start_script {
run_command(&["sh", script.as_str()]);
}
for rsync in &conf.rsync.unwrap_or_default() {
run_backup_rsync(rsync);
}
for borg in &conf.borg.unwrap_or_default() {
// TODO : Implement
}
if let Some(script) = &conf.end_script {
run_command(&["sh", script.as_str()]);
}
}

29
src/config.rs Normal file
View file

@ -0,0 +1,29 @@
use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
pub struct Config {
/// Run a script before backup
pub start_script: Option<String>,
// Run a script after backup
pub end_script: Option<String>,
pub rsync: Option<Vec<RsyncConfig>>,
pub borg: Option<Vec<BorgConfig>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct RsyncConfig {
pub src: String,
pub dest: String,
pub exclude: Option<Vec<String>>,
pub delete: Option<bool>,
pub ensure_exists: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BorgConfig {
pub repo: String,
pub passphrase: Option<String>,
pub src: Vec<String>,
}

40
src/main.rs Normal file
View file

@ -0,0 +1,40 @@
use backup::run_backup;
mod backup;
mod config;
fn main() {
let args = std::env::args().collect::<Vec<_>>();
if let Some(conf) = args.get(1) {
let conf = toml::from_str(&std::fs::read_to_string(conf).unwrap()).unwrap();
run_backup(conf);
} else {
println!("Usage: bk <config>");
}
}
pub fn run_command(cmd: &[&str]) -> (String, String) {
println!("--> {} ", cmd.join(" "));
let mut cmd_setup = std::process::Command::new(cmd[0]);
let mut cmd_setup = cmd_setup.args(cmd.iter().skip(1).collect::<Vec<_>>());
cmd_setup = cmd_setup
.stdout(std::process::Stdio::inherit())
.stdin(std::process::Stdio::inherit());
let child = cmd_setup.spawn().unwrap();
let status = child.wait_with_output().unwrap();
assert!(status.status.success());
let output = String::from_utf8(status.stdout).unwrap();
let stderr = String::from_utf8(status.stderr).unwrap();
if !stderr.trim().is_empty() {
eprintln!("{stderr}");
}
(output, stderr)
}