From b54ac0efef1fc7cc3b7d6e5747df733bfc0b6bd2 Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Fri, 23 Oct 2020 20:40:32 -0500 Subject: [PATCH] Code cleanup + Split up main.rs into multiple modules + Used discord-hook-rs for webhooks + General code cleanup (clippy+fmt) + Bumped version number --- Cargo.lock | 172 +++++++++++++--- Cargo.toml | 6 +- src/backup.rs | 191 ++++++++++++++++++ src/{albatross_config.rs => config.rs} | 1 + src/discord.rs | 16 ++ src/main.rs | 261 +++---------------------- src/region.rs | 42 ++++ 7 files changed, 418 insertions(+), 271 deletions(-) create mode 100644 src/backup.rs rename src/{albatross_config.rs => config.rs} (98%) create mode 100644 src/discord.rs create mode 100644 src/region.rs diff --git a/Cargo.lock b/Cargo.lock index bfe8758..de083ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,20 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "addr2line" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" + [[package]] name = "adler32" version = "1.0.4" @@ -17,17 +32,17 @@ dependencies = [ [[package]] name = "albatross" -version = "0.1.0" +version = "0.2.0" dependencies = [ "chrono", "clap", "config", + "discord-hooks-rs", "flate2", "log", "regex", "reqwest", - "serde 1.0.111", - "serde_derive", + "serde 1.0.117", "tar", ] @@ -57,6 +72,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +[[package]] +name = "backtrace" +version = "0.3.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707b586e0e2f247cbde68cdd2c3ce69ea7b7be43e1c5b426e37c9319c4b9838e" +dependencies = [ + "addr2line", + "cfg-if 1.0.0", + "libc", + "miniz_oxide 0.4.3", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.12.1" @@ -93,6 +122,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "chrono" version = "0.4.11" @@ -128,7 +163,7 @@ dependencies = [ "lazy_static 1.4.0", "nom", "rust-ini", - "serde 1.0.111", + "serde 1.0.117", "serde-hjson", "serde_json", "toml", @@ -157,7 +192,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", +] + +[[package]] +name = "discord-hooks-rs" +version = "0.1.0" +source = "git+https://github.com/joeyahines/discord-hooks-rs#70307a7a5d00b8c48c0dc10402c7d8325836539a" +dependencies = [ + "failure", + "serde 1.0.117", + "serde_json", ] [[package]] @@ -172,7 +217,29 @@ version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", +] + +[[package]] +name = "failure" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] @@ -181,7 +248,7 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "redox_syscall", "winapi 0.3.8", @@ -193,10 +260,10 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "crc32fast", "libc", - "miniz_oxide", + "miniz_oxide 0.3.6", ] [[package]] @@ -293,11 +360,17 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "wasi", ] +[[package]] +name = "gimli" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" + [[package]] name = "h2" version = "0.2.5" @@ -484,7 +557,7 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", ] [[package]] @@ -524,13 +597,23 @@ dependencies = [ "adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "mio" version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "fuchsia-zircon", "fuchsia-zircon-sys", "iovec", @@ -579,7 +662,7 @@ version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "winapi 0.3.8", ] @@ -632,6 +715,12 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693" + [[package]] name = "once_cell" version = "1.4.0" @@ -645,7 +734,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" dependencies = [ "bitflags", - "cfg-if", + "cfg-if 0.1.10", "foreign-types", "lazy_static 1.4.0", "libc", @@ -723,9 +812,9 @@ checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "proc-macro2" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] @@ -836,7 +925,7 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", - "serde 1.0.111", + "serde 1.0.117", "serde_json", "serde_urlencoded", "tokio", @@ -854,6 +943,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" +[[package]] +name = "rustc-demangle" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" + [[package]] name = "ryu" version = "1.0.5" @@ -901,9 +996,12 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" [[package]] name = "serde" -version = "1.0.111" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +dependencies = [ + "serde_derive", +] [[package]] name = "serde-hjson" @@ -920,9 +1018,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.111" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" dependencies = [ "proc-macro2", "quote", @@ -937,7 +1035,7 @@ checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" dependencies = [ "itoa", "ryu", - "serde 1.0.111", + "serde 1.0.117", ] [[package]] @@ -957,7 +1055,7 @@ checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" dependencies = [ "dtoa", "itoa", - "serde 1.0.111", + "serde 1.0.117", "url", ] @@ -979,7 +1077,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "redox_syscall", "winapi 0.3.8", @@ -993,15 +1091,27 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.30" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" +checksum = "5ad5de3220ea04da322618ded2c42233d02baca219d6f160a3e9c87cda16c942" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + [[package]] name = "tar" version = "0.4.28" @@ -1020,7 +1130,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "rand", "redox_syscall", @@ -1104,7 +1214,7 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" dependencies = [ - "serde 1.0.111", + "serde 1.0.117", ] [[package]] @@ -1215,8 +1325,8 @@ version = "0.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c2dc4aa152834bc334f506c1a06b866416a8b6697d5c9f75b9a689c8486def0" dependencies = [ - "cfg-if", - "serde 1.0.111", + "cfg-if 0.1.10", + "serde 1.0.117", "serde_json", "wasm-bindgen-macro", ] @@ -1242,7 +1352,7 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64487204d863f109eb77e8462189d111f27cb5712cc9fdb3461297a76963a2f6" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "js-sys", "wasm-bindgen", "web-sys", diff --git a/Cargo.toml b/Cargo.toml index c597df5..941c13a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "albatross" -version = "0.1.0" +version = "0.2.0" authors = ["Joey Hines "] edition = "2018" @@ -8,8 +8,7 @@ edition = "2018" [dependencies] clap = "2.33.0" -serde = "1.0.106" -serde_derive = "1.0.104" +serde = { version="1.0.116", features=["derive"] } config = "0.9" log = "0.4.8" chrono = "0.4" @@ -17,3 +16,4 @@ regex = "1.3.9" flate2 = "1.0.14" tar = "0.4.28" reqwest = { version = "0.10", features = ["blocking", "json"] } +discord-hooks-rs = { git = "https://github.com/joeyahines/discord-hooks-rs" } diff --git a/src/backup.rs b/src/backup.rs new file mode 100644 index 0000000..ac90faf --- /dev/null +++ b/src/backup.rs @@ -0,0 +1,191 @@ +use crate::config::WorldConfig; +use crate::region::Region; +use flate2::write::GzEncoder; +use flate2::Compression; +use std::convert::TryFrom; +use std::fs::{copy, create_dir, File}; +use std::path::PathBuf; + +/// Backup a file +/// +/// # Param +/// * `file_name` - file name +/// * `world_path` - path to the world folder +/// * `backup_path` - path to the backup folder +pub fn backup_file( + file_name: &str, + mut world_path: PathBuf, + mut backup_path: PathBuf, +) -> Result { + world_path.push(file_name); + backup_path.push(file_name); + + copy(world_path, backup_path) +} + +/// Backup a directory +/// +/// # Param +/// * `dir_name` - directory name +/// * `world_path` - path to the world folder +/// * `backup_path` - path to the backup folder +pub fn backup_dir( + dir_name: &str, + world_path: &PathBuf, + backup_path: &PathBuf, +) -> Result { + let mut src_dir = world_path.clone(); + src_dir.push(dir_name); + let mut backup_dir = backup_path.clone(); + backup_dir.push(dir_name); + create_dir(&backup_dir)?; + + let mut file_count = 0; + for entry in src_dir.read_dir()? { + let entry = entry?; + let mut target = backup_dir.clone(); + target.push(entry.file_name()); + + copy(entry.path(), target)?; + file_count += 1; + } + + Ok(file_count) +} + +/// Backup the regions +/// +/// # Param +/// * `dir_name` - name of the backup folder +/// * `save_radius` - block radius to save +/// * `world_path` - path to the world folder +/// * `backup_path` - path to the backup folder +pub fn backup_region( + dir_name: &str, + save_radius: u64, + world_path: &PathBuf, + backup_path: &PathBuf, +) -> Result { + let mut count: u64 = 0; + let mut src_dir = world_path.clone(); + src_dir.push(dir_name); + let mut backup_dir = backup_path.clone(); + backup_dir.push(dir_name); + create_dir(&backup_dir)?; + + let save_radius = (save_radius as f64 / 512.0).ceil() as i64; + + for entry in src_dir.read_dir()? { + let entry = entry?; + let file_name = entry.file_name().to_str().unwrap().to_string(); + + if let Ok(region) = Region::try_from(file_name) { + if region.x.abs() <= save_radius && region.y.abs() <= save_radius { + let mut target = backup_dir.clone(); + target.push(entry.file_name()); + + copy(entry.path(), target)?; + count += 1; + } + } + } + + Ok(count) +} + +/// Backup a world +/// +/// # Param +/// * `world_path` - path to the world folder +/// * `backup_path` - path to the backup folder +/// * `world_config` - world config options +pub fn backup_world( + world_path: PathBuf, + mut backup_path: PathBuf, + world_config: &WorldConfig, +) -> Result { + let region_count; + backup_path.push(&world_config.world_name); + create_dir(backup_path.as_path())?; + + backup_region("poi", world_config.save_radius, &world_path, &backup_path)?; + region_count = backup_region( + "region", + world_config.save_radius, + &world_path, + &backup_path, + )?; + Ok(region_count) +} + +/// Backup the overworld +/// +/// # Param +/// * `world_path` - path to the world folder +/// * `backup_path` - path to the backup folder +/// * `world_config` - world config options +pub fn backup_overworld( + world_path: PathBuf, + backup_path: PathBuf, + world_config: &WorldConfig, +) -> Result<(u64, u64), std::io::Error> { + backup_dir("data", &world_path, &backup_path)?; + backup_dir("stats", &world_path, &backup_path)?; + + backup_file("level.dat", world_path.clone(), backup_path.clone())?; + backup_file("level.dat_old", world_path.clone(), backup_path.clone())?; + backup_file("session.lock", world_path.clone(), backup_path.clone())?; + backup_file("uid.dat", world_path.clone(), backup_path.clone())?; + + let player_count = backup_dir("playerdata", &world_path, &backup_path)?; + let region_count = backup_world(world_path, backup_path, world_config)?; + + Ok((region_count, player_count)) +} + +/// Backup the nether +/// +/// # Param +/// * `world_path` - path to the world folder +/// * `backup_path` - path to the backup folder +/// * `world_config` - world config options +pub fn backup_nether( + world_path: PathBuf, + backup_path: PathBuf, + world_config: &WorldConfig, +) -> Result { + let mut nether_path = world_path; + nether_path.push("DIM-1"); + + backup_world(nether_path, backup_path, world_config) +} + +/// Backup the end +/// +/// # Param +/// * `world_path` - path to the world folder +/// * `backup_path` - path to the backup folder +/// * `world_config` - world config options +pub fn backup_end( + world_path: PathBuf, + backup_path: PathBuf, + world_config: &WorldConfig, +) -> Result { + let mut end_path = world_path; + end_path.push("DIM1"); + + backup_world(end_path, backup_path, world_config) +} + +/// Compress the backup after the files have been copied +/// +/// # Param +/// * `tmp_dir`: tmp directory with the backed up files +/// * `output_file`: output archive +pub fn compress_backup(tmp_dir: &PathBuf, output_file: &PathBuf) -> Result<(), std::io::Error> { + let archive = File::create(output_file)?; + let enc = GzEncoder::new(archive, Compression::default()); + let mut tar_builder = tar::Builder::new(enc); + tar_builder.append_dir_all(".", tmp_dir)?; + Ok(()) +} diff --git a/src/albatross_config.rs b/src/config.rs similarity index 98% rename from src/albatross_config.rs rename to src/config.rs index a792595..fe078a0 100644 --- a/src/albatross_config.rs +++ b/src/config.rs @@ -1,4 +1,5 @@ use config::{Config, ConfigError, File}; +use serde::Deserialize; use std::path::PathBuf; /// World types supported diff --git a/src/discord.rs b/src/discord.rs new file mode 100644 index 0000000..5c1fe17 --- /dev/null +++ b/src/discord.rs @@ -0,0 +1,16 @@ +use crate::config::AlbatrossConfig; +use discord_hooks_rs::DiscordWebhook; + +/// Sends a webhook to Discord if its configured +/// +/// # Params +/// * `msg` - Message to send to discord +/// * `cfg` - Albatross config +pub fn send_webhook(msg: &str, cfg: &AlbatrossConfig) { + if let Some(webhook) = &cfg.backup.discord_webhook { + let json = DiscordWebhook::new().content(msg); + + let client = reqwest::blocking::Client::new(); + client.post(webhook).json(&json).send().ok(); + } +} diff --git a/src/main.rs b/src/main.rs index 0076537..f967fb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,203 +1,17 @@ -extern crate serde; - -#[macro_use] -extern crate serde_derive; - use chrono::{NaiveDateTime, Utc}; use clap::{App, Arg}; -use flate2::write::GzEncoder; -use flate2::Compression; -use regex::Regex; -use std::fs::{copy, create_dir, create_dir_all, remove_dir_all, remove_file, DirEntry, File}; +use std::fs::{create_dir_all, remove_dir_all, remove_file, DirEntry}; use std::path::PathBuf; use std::time::Instant; -mod albatross_config; +mod backup; +mod config; +mod discord; +mod region; -use albatross_config::{AlbatrossConfig, WorldConfig, WorldType}; -use std::collections::HashMap; - -/// Struct to store information about the region -struct Region { - /// x position of the region - x: i64, - /// y position of the region - y: i64, -} - -impl Region { - fn from_string(string: String) -> Option { - let re = Regex::new(r"r\.(?P-?[0-9]*)+\.(?P-?[0-9]*)").unwrap(); - if re.is_match(string.as_str()) { - let captures = re.captures(string.as_str()).unwrap(); - - return Some(Region { - x: captures["x"].parse::().unwrap(), - y: captures["y"].parse::().unwrap(), - }); - } - - None - } -} - -/// Backup a directory -/// -/// # Param -/// * `world_path` - path to the world folder -/// * `backup_path` - path to the backup folder -fn backup_dir( - dir_name: &str, - world_path: &PathBuf, - backup_path: &PathBuf, -) -> Result { - let mut src_dir = world_path.clone(); - src_dir.push(dir_name); - let mut backup_dir = backup_path.clone(); - backup_dir.push(dir_name); - create_dir(&backup_dir)?; - - let mut file_count = 0; - for entry in src_dir.read_dir()? { - let entry = entry?; - let mut target = backup_dir.clone(); - target.push(entry.file_name()); - - copy(entry.path(), target)?; - file_count += 1; - } - - Ok(file_count) -} - -/// Backup the regions -/// -/// # Param -/// * `dir_name` - name of the backup folder -/// * `save_radius` - block radius to save -/// * `world_path` - path to the world folder -/// * `backup_path` - path to the backup folder -fn backup_region( - dir_name: &str, - save_radius: u64, - world_path: &PathBuf, - backup_path: &PathBuf, -) -> Result { - let mut count: u64 = 0; - let mut src_dir = world_path.clone(); - src_dir.push(dir_name); - let mut backup_dir = backup_path.clone(); - backup_dir.push(dir_name); - create_dir(&backup_dir)?; - - let save_radius = (save_radius as f64 / 512.0).ceil() as i64; - - for entry in src_dir.read_dir()? { - let entry = entry?; - let file_name = entry.file_name().to_str().unwrap().to_string(); - - if let Some(region) = Region::from_string(file_name) { - if region.x.abs() <= save_radius && region.y.abs() <= save_radius { - let mut target = backup_dir.clone(); - target.push(entry.file_name()); - - copy(entry.path(), target)?; - count += 1; - } - } - } - - Ok(count) -} - -/// Backup a world -/// -/// # Param -/// * `world_path` - path to the world folder -/// * `backup_path` - path to the backup folder -/// * `world_config` - world config options -fn backup_world( - world_path: PathBuf, - backup_path: PathBuf, - world_config: &WorldConfig, -) -> Result { - let mut backup_path = backup_path.clone(); - let region_count; - backup_path.push(&world_config.world_name); - create_dir(backup_path.as_path())?; - - backup_region("poi", world_config.save_radius, &world_path, &backup_path)?; - region_count = backup_region( - "region", - world_config.save_radius, - &world_path, - &backup_path, - )?; - backup_dir("data", &world_path, &backup_path)?; - - Ok(region_count) -} - -/// Backup the overworld -/// -/// # Param -/// * `world_path` - path to the world folder -/// * `backup_path` - path to the backup folder -/// * `world_config` - world config options -fn backup_overworld( - world_path: PathBuf, - backup_path: PathBuf, - world_config: &WorldConfig, -) -> Result { - backup_world(world_path, backup_path, world_config) -} - -/// Backup the nether -/// -/// # Param -/// * `world_path` - path to the world folder -/// * `backup_path` - path to the backup folder -/// * `world_config` - world config options -fn backup_nether( - world_path: PathBuf, - backup_path: PathBuf, - world_config: &WorldConfig, -) -> Result { - let mut nether_path = world_path.clone(); - nether_path.push("DIM-1"); - - backup_world(nether_path, backup_path, world_config) -} - -/// Backup the end -/// -/// # Param -/// * `world_path` - path to the world folder -/// * `backup_path` - path to the backup folder -/// * `world_config` - world config options -fn backup_end( - world_path: PathBuf, - backup_path: PathBuf, - world_config: &WorldConfig, -) -> Result { - let mut end_path = world_path.clone(); - end_path.push("DIM1"); - - backup_world(end_path, backup_path, world_config) -} - -/// Compress the backup after the files have been copied -/// -/// # Param -/// * `tmp_dir`: tmp directory with the backed up files -/// * `output_file`: output archive -fn compress_backup(tmp_dir: &PathBuf, output_file: &PathBuf) -> Result<(), std::io::Error> { - let archive = File::create(output_file)?; - let enc = GzEncoder::new(archive, Compression::default()); - let mut tar_builder = tar::Builder::new(enc); - tar_builder.append_dir_all(".", tmp_dir)?; - Ok(()) -} +use crate::config::{AlbatrossConfig, WorldType}; +use backup::{backup_end, backup_nether, backup_overworld}; +use discord::send_webhook; /// Get the time of the backup from a file name /// @@ -207,9 +21,9 @@ fn get_time_from_file_name( archive_entry: &DirEntry, ) -> Result, std::io::Error> { let file_name = archive_entry.file_name().to_str().unwrap().to_string(); - let name: Vec<&str> = file_name.split("_").collect(); + let name: Vec<&str> = file_name.split('_').collect(); - Ok(chrono::NaiveDateTime::parse_from_str(name[0], "%d-%m-%y-%H:%M:%S").ok()) + Ok(chrono::NaiveDateTime::parse_from_str(name[0], "%d-%m-%y-%H.%M.%S").ok()) } /// Removes the old backups from the ouput directory @@ -250,22 +64,6 @@ fn remove_old_backups(output_dir: &PathBuf, keep: u64) -> Result Result<(), std::io::Error> { let server_base_dir = cfg.backup.minecraft_dir.clone(); let worlds = cfg.world_config.clone().expect("No worlds configured"); - let time_str = Utc::now().format("%d-%m-%y-%H:%M:%S").to_string(); + let time_str = Utc::now().format("%d-%m-%y-%H.%M.%S").to_string(); let backup_name = format!("{}_backup.tar.gz", time_str); let mut output_archive = cfg.backup.output_dir.clone(); output_archive.push(backup_name); @@ -299,42 +97,33 @@ fn do_backup(cfg: AlbatrossConfig) -> Result<(), std::io::Error> { format!("Starting backup of **{}**", world_name).as_str(), &cfg, ); - match world_type { + let webhook_msg = match world_type { WorldType::OVERWORLD => { - let region_count = + let (region_count, player_count) = backup_overworld(world_dir.clone(), tmp_dir.clone(), &world)?; - let player_count = backup_dir("playerdata", &world_dir, &tmp_dir)?; - send_webhook( - format!( - "{} regions and {} player files backed up.", - region_count, player_count - ) - .as_str(), - &cfg, - ); + format!( + "{} regions and {} player files backed up.", + region_count, player_count + ) } WorldType::NETHER => { let region_count = backup_nether(world_dir, tmp_dir.clone(), &world)?; - send_webhook( - format!("{} regions backed up.", region_count).as_str(), - &cfg, - ); + format!("{} regions backed up.", region_count) } WorldType::END => { let region_count = backup_end(world_dir, tmp_dir.clone(), &world)?; - send_webhook( - format!("{} regions backed up.", region_count).as_str(), - &cfg, - ); + format!("{} regions backed up.", region_count) } }; + + send_webhook(&webhook_msg, &cfg); } else { send_webhook(format!("Error: {} not found.", world_name).as_str(), &cfg); println!("World \"{}\" not found", world_name.clone()); } } - compress_backup(&tmp_dir, &output_archive)?; + backup::compress_backup(&tmp_dir, &output_archive)?; remove_dir_all(&tmp_dir)?; @@ -377,11 +166,9 @@ fn main() { if cfg.world_config.is_some() { println!("Starting backup"); match do_backup(cfg) { - Err(e) => println!("Error doing backup: {}", e), - _ => {} + Ok(_) => println!("Backup complete"), + Err(e) => println!("Error doing backup: {:?}", e), } - - println!("Backup complete"); } else { println!("No worlds specified to backed up!") } diff --git a/src/region.rs b/src/region.rs new file mode 100644 index 0000000..7f9f2bf --- /dev/null +++ b/src/region.rs @@ -0,0 +1,42 @@ +use regex::Regex; +use std::convert::TryFrom; +use std::error::Error; +use std::fmt; + +#[derive(Debug, Clone)] +pub struct RegionParseError; + +impl fmt::Display for RegionParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Unable to parse region file name") + } +} + +impl Error for RegionParseError {} + +/// Struct to store information about the region +pub struct Region { + /// x position of the region + pub x: i64, + /// y position of the region + pub y: i64, +} + +impl TryFrom for Region { + type Error = RegionParseError; + + /// Try from string + fn try_from(value: String) -> Result { + let re = Regex::new(r"r\.(?P-?[0-9]*)+\.(?P-?[0-9]*)").unwrap(); + if re.is_match(&value) { + let captures = re.captures(value.as_str()).unwrap(); + + return Ok(Region { + x: captures["x"].parse::().unwrap(), + y: captures["y"].parse::().unwrap(), + }); + } + + Err(RegionParseError) + } +}