diff --git a/.cargo/config.toml b/.cargo/config.toml index 37fecab..c5834ea 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,3 @@ [alias] -runs = "run -- --config data/config --backup data/backups --world data/worlds --layers 2min,2,4,4;3min,3,2,2" +runs = "run -- run --config data/config --backup data/backups --world data/worlds --jar paper-1.19.4-550.jar --layers 2min,2,4,4;3min,3,2,2" runrs = "run --release -- paper 1.19.4-545 --config data/config --backup data/backups --world data/worlds --jar data/paper-1.19.4-525.jar" diff --git a/src/backup/manager/meta.rs b/src/backup/manager/meta.rs index ff91310..076bf14 100644 --- a/src/backup/manager/meta.rs +++ b/src/backup/manager/meta.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; /// Manages a collection of backup layers, allowing them to be utilized as a single object. pub struct MetaManager where - T: Clone + Serialize + for<'de> Deserialize<'de> + std::fmt::Debug, + T: Clone + Serialize + for<'de> Deserialize<'de>, { backup_dir: PathBuf, dirs: Vec<(PathBuf, PathBuf)>, @@ -19,7 +19,7 @@ where impl MetaManager where - T: Clone + Serialize + for<'de> Deserialize<'de> + std::fmt::Debug, + T: Clone + Serialize + for<'de> Deserialize<'de>, { pub fn new>( backup_dir: P, @@ -101,30 +101,4 @@ where Ok(()) } } - - /// Create a manual backup for a specific layer - pub fn create_backup(&mut self, layer: &str) -> Option> { - if let Some(manager) = self.managers.get_mut(layer) { - let mut res = manager.create_backup(); - - if res.is_ok() { - res = manager.remove_old_backups(); - } - - Some(res) - } else { - None - } - } - - /// Restore a backup for a specific layer - pub fn restore_backup( - &self, - layer: &str, - start_time: chrono::DateTime, - ) -> Option> { - self.managers - .get(layer) - .map(|manager| manager.restore_backup(start_time)) - } } diff --git a/src/backup/manager/mod.rs b/src/backup/manager/mod.rs index 2b742f7..32c452a 100644 --- a/src/backup/manager/mod.rs +++ b/src/backup/manager/mod.rs @@ -5,8 +5,6 @@ pub use config::ManagerConfig; pub use meta::MetaManager; use super::Backup; -use crate::other; -use chrono::SubsecRound; use chrono::Utc; use serde::Deserialize; use serde::Serialize; @@ -17,7 +15,7 @@ use std::path::PathBuf; /// Manages a single backup layer consisting of one or more chains of backups. pub struct Manager where - T: Clone + Serialize + for<'de> Deserialize<'de> + std::fmt::Debug, + T: Clone + Serialize + for<'de> Deserialize<'de>, { backup_dir: PathBuf, dirs: Vec<(PathBuf, PathBuf)>, @@ -30,7 +28,7 @@ where impl Manager where - T: Clone + Serialize + for<'de> Deserialize<'de> + std::fmt::Debug, + T: Clone + Serialize + for<'de> Deserialize<'de>, { const METADATA_FILE: &str = "alex.json"; @@ -158,27 +156,4 @@ where chrono::offset::Utc::now() } - - /// Restore the backup with the given start time by restoring its chain up to and including the - /// backup, in order. - pub fn restore_backup(&self, start_time: chrono::DateTime) -> io::Result<()> { - // Iterate over each chain, skipping elements until the element with the given start time - // is possibly found. - for chain in &self.chains { - // If we find the element in the chain, restore the entire chain up to and including - // the element - if let Some(index) = chain - .iter() - .position(|b| b.start_time.trunc_subsecs(0) == start_time) - { - for backup in chain.iter().take(index + 1) { - backup.restore(&self.backup_dir, &self.dirs)?; - } - - return Ok(()); - } - } - - Err(other("Unknown backup.")) - } } diff --git a/src/backup/mod.rs b/src/backup/mod.rs index 3bc99df..0168e67 100644 --- a/src/backup/mod.rs +++ b/src/backup/mod.rs @@ -8,7 +8,6 @@ pub use manager::ManagerConfig; pub use manager::MetaManager; use chrono::Utc; -use flate2::read::GzDecoder; use flate2::write::GzEncoder; use flate2::Compression; use path::PathExt; @@ -25,7 +24,7 @@ pub enum BackupType { } /// Represents a successful backup -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize)] pub struct Backup { /// When the backup was started (also corresponds to the name) pub start_time: chrono::DateTime, @@ -37,8 +36,6 @@ pub struct Backup { } impl Backup<()> { - pub const FILENAME_FORMAT: &str = "%Y-%m-%d_%H-%M-%S.tar.gz"; - /// Return the path to a backup file by properly formatting the data. pub fn path>(backup_dir: P, start_time: chrono::DateTime) -> PathBuf { let backup_dir = backup_dir.as_ref(); @@ -49,6 +46,8 @@ impl Backup<()> { } impl Backup { + const FILENAME_FORMAT: &str = "%Y-%m-%d_%H-%M-%S.tar.gz"; + pub fn set_metadata(&mut self, metadata: T) { self.metadata = Some(metadata); } @@ -160,48 +159,4 @@ impl Backup { metadata: None, }) } - - pub fn restore>( - &self, - backup_dir: P, - dirs: &Vec<(PathBuf, PathBuf)>, - ) -> io::Result<()> { - let path = Backup::path(backup_dir, self.start_time); - let tar_gz = File::open(path)?; - let enc = GzDecoder::new(tar_gz); - let mut ar = tar::Archive::new(enc); - - // Unpack each file by matching it with one of the destination directories and extracting - // it to the right path - for entry in ar.entries()? { - let mut entry = entry?; - let entry_path_in_tar = entry.path()?.to_path_buf(); - - for (path_in_tar, dst_dir) in dirs { - if entry_path_in_tar.starts_with(path_in_tar) { - let dst_path = - dst_dir.join(entry_path_in_tar.strip_prefix(path_in_tar).unwrap()); - - // Ensure all parent directories are present - std::fs::create_dir_all(dst_path.parent().unwrap())?; - - entry.unpack(dst_path)?; - - break; - } - } - } - - // Remove any files - for (path_in_tar, dst_dir) in dirs { - if let Some(removed) = self.delta.removed.get(path_in_tar) { - for path in removed { - let dst_path = dst_dir.join(path); - std::fs::remove_file(dst_path)?; - } - } - } - - Ok(()) - } } diff --git a/src/cli/backup.rs b/src/cli/backup.rs index 32d45c4..2eeb4e4 100644 --- a/src/cli/backup.rs +++ b/src/cli/backup.rs @@ -1,19 +1,8 @@ -use crate::backup::Backup; -use crate::cli::Cli; -use crate::other; -use chrono::{TimeZone, Utc}; use clap::{Args, Subcommand}; -use std::io; -use std::path::PathBuf; #[derive(Subcommand)] pub enum BackupCommands { - /// List all tracked backups List, - /// Manually create a new backup - Create(BackupCreateArgs), - /// Restore a backup - Restore(BackupRestoreArgs), } #[derive(Args)] @@ -21,102 +10,3 @@ pub struct BackupArgs { #[command(subcommand)] pub command: BackupCommands, } - -#[derive(Args)] -pub struct BackupCreateArgs { - /// What layer to create a backup in - layer: String, -} - -#[derive(Args)] -pub struct BackupRestoreArgs { - /// Path to the backup inside the backup directory - path: PathBuf, - /// Whether to overwrite the contents of the existing directories - #[arg(short, long, default_value_t = false)] - force: bool, -} - -impl BackupArgs { - pub fn run(&self, cli: &Cli) -> io::Result<()> { - match &self.command { - BackupCommands::Create(args) => args.run(cli), - BackupCommands::List => Ok(()), - BackupCommands::Restore(args) => args.run(cli), - } - } -} - -impl BackupCreateArgs { - pub fn run(&self, cli: &Cli) -> io::Result<()> { - let mut meta = cli.meta()?; - - if let Some(res) = meta.create_backup(&self.layer) { - res - } else { - Err(io::Error::new(io::ErrorKind::Other, "Unknown layer")) - } - } -} - -impl BackupRestoreArgs { - pub fn run(&self, cli: &Cli) -> io::Result<()> { - let backup_dir = cli.backup.canonicalize()?; - - // Parse input path - let path = self.path.canonicalize()?; - - if !path.starts_with(&backup_dir) { - return Err(other("Provided file is not inside the backup directory.")); - } - - let layer = if let Some(parent) = path.parent() { - // Backup files should be stored nested inside a layer's folder - if parent != backup_dir { - parent.file_name().unwrap().to_string_lossy() - } else { - return Err(other("Invalid path.")); - } - } else { - return Err(other("Invalid path.")); - }; - - let timestamp = if let Some(filename) = path.file_name() { - Utc.datetime_from_str(&filename.to_string_lossy(), Backup::FILENAME_FORMAT) - .map_err(|_| other("Invalid filename."))? - } else { - return Err(other("Invalid filename.")); - }; - - let meta = cli.meta()?; - - // Clear previous contents of directories - let mut entries = cli - .config - .canonicalize()? - .read_dir()? - .chain(cli.world.canonicalize()?.read_dir()?) - .peekable(); - - if entries.peek().is_some() && !self.force { - return Err(other("Output directories are not empty. If you wish to overwrite these contents, use the force flag.")); - } - - for entry in entries { - let path = entry?.path(); - - if path.is_dir() { - std::fs::remove_dir_all(path)?; - } else { - std::fs::remove_file(path)?; - } - } - - // Restore the backup - if let Some(res) = meta.restore_backup(&layer, timestamp) { - res - } else { - Err(other("Unknown layer")) - } - } -} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 0594e8e..9ecd9f3 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,8 +1,6 @@ mod backup; mod run; -pub use crate::backup::MetaManager; -pub use crate::server::Metadata; pub use backup::{BackupArgs, BackupCommands}; pub use run::RunArgs; @@ -70,23 +68,7 @@ impl Cli { pub fn run(&self) -> io::Result<()> { match &self.command { Commands::Run(args) => args.run(self), - Commands::Backup(args) => args.run(self), + Commands::Backup(_) => Ok(()), } } - - /// Convenience method to initialize backup manager from the cli arguments - pub fn meta(&self) -> io::Result> { - let metadata = Metadata { - server_type: self.server, - server_version: self.server_version.clone(), - }; - let dirs = vec![ - (PathBuf::from("config"), self.config.canonicalize()?), - (PathBuf::from("worlds"), self.world.canonicalize()?), - ]; - let mut meta = MetaManager::new(self.backup.canonicalize()?, dirs, metadata); - meta.add_all(&self.layers)?; - - Ok(meta) - } } diff --git a/src/main.rs b/src/main.rs index f357d1b..edad9c3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,10 +8,6 @@ use crate::cli::Cli; use clap::Parser; use std::io; -pub fn other(msg: &str) -> io::Error { - io::Error::new(io::ErrorKind::Other, msg) -} - // fn commands_backup(cli: &Cli, args: &BackupArgs) -> io::Result<()> { // let metadata = server::Metadata { // server_type: cli.server, diff --git a/src/server/mod.rs b/src/server/mod.rs index 23a9ff5..09f90a5 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -8,7 +8,7 @@ use clap::ValueEnum; use serde::{Deserialize, Serialize}; use std::fmt; -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)] pub enum ServerType { Unknown, Paper, @@ -29,7 +29,7 @@ impl fmt::Display for ServerType { } } -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize)] pub struct Metadata { pub server_type: ServerType, pub server_version: String,