commit 5f3369cf465f4b7a7973cd0d45a5161b660400f6 Author: James McDonald Date: Sat Apr 25 01:33:15 2026 +0200 Add rust zfsbackup diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3e6fb2e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "zfsbackup" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/src/command.rs b/src/command.rs new file mode 100644 index 0000000..a0ba558 --- /dev/null +++ b/src/command.rs @@ -0,0 +1,19 @@ +pub fn exec_command(command: &Vec) -> Result<(), String> { + if command.is_empty() { + return Err("Command is empty".to_string()); + } + let mut cmd = std::process::Command::new(&command[0]); + if command.len() > 1 { + cmd.args(&command[1..]); + } + let output = cmd.output().map_err(|e| e.to_string())?; + if !output.status.success() { + return Err(format!( + "Command {:?} failed with status {}: {}", + command, + output.status, + String::from_utf8_lossy(&output.stderr) + )); + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d9839a4 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,83 @@ +mod command; + +pub struct JobBuilder { + sources: Vec, + target: String, + source_zfs_command: Vec, + target_zfs_command: Vec, + dryrun: bool, +} + +fn parse_command(commandstr: &str) -> Vec { + commandstr + .split_whitespace() + .map(|s| s.to_string()) + .collect() +} + +impl JobBuilder { + pub fn new(sources: Vec, target: String) -> Self { + JobBuilder { + sources, + target, + source_zfs_command: vec!["zfs".to_string()], + target_zfs_command: vec!["zfs".to_string()], + dryrun: false, + } + } + + pub fn source_zfs_command(mut self, commandstr: &str) -> Self { + let command: Vec; + command = parse_command(commandstr); + self.source_zfs_command = command; + self + } + + pub fn target_zfs_command(mut self, commandstr: &str) -> Self { + let command: Vec; + command = parse_command(commandstr); + self.target_zfs_command = command; + self + } + + pub fn zfs_command(mut self, commandstr: &str) -> Self { + let command: Vec; + command = parse_command(commandstr); + self.source_zfs_command = command.clone(); + self.target_zfs_command = command; + self + } + + pub fn dryrun(mut self) -> Self { + self.dryrun = true; + self + } + + pub fn build(self) -> Job { + for source in &self.sources { + let mut cmd = self.source_zfs_command.clone(); + let mut args = vec!["list".to_string(), source.clone()]; + cmd.append(&mut args); + match command::exec_command(&cmd) { + Ok(_) => (), + Err(e) => panic!("Cannot list source dataset {}: {}", source, e), + } + println!("Source: {}", source); + } + Job { + datasets: vec!["dataset1".to_string(), "dataset2".to_string()], + target: self.target, + source_zfs_command: self.source_zfs_command, + target_zfs_command: self.target_zfs_command, + dryrun: self.dryrun, + } + } +} + +pub struct Job { + datasets: Vec, + target: String, + source_zfs_command: Vec, + target_zfs_command: Vec, + dryrun: bool, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e5eeaee --- /dev/null +++ b/src/main.rs @@ -0,0 +1,5 @@ +use zfsbackup::JobBuilder; + +fn main() { + let job = JobBuilder::new(vec!["chonk".to_string()], "backup".to_string()).build(); +}