From 4b5460f75426878aa5db52ea54904197bcaee2f7 Mon Sep 17 00:00:00 2001 From: Cutieguwu Date: Mon, 29 Dec 2025 15:31:01 -0500 Subject: [PATCH] Huge refactor. Introduce `anyhow` for error handling. --- Cargo.lock | 470 +++++------------------------------------ Cargo.toml | 12 +- imports_order.rs | 17 ++ src/cli.rs | 32 +++ src/io.rs | 21 ++ src/main.rs | 121 +++-------- src/mapping/cluster.rs | 7 + src/mapping/domain.rs | 5 + src/mapping/map.rs | 183 +++++++++++++++- src/mapping/mod.rs | 185 +--------------- src/mapping/prelude.rs | 6 + src/mapping/stage.rs | 20 ++ src/recovery.rs | 78 +++---- 13 files changed, 411 insertions(+), 746 deletions(-) create mode 100644 imports_order.rs create mode 100644 src/cli.rs create mode 100644 src/mapping/prelude.rs create mode 100644 src/mapping/stage.rs diff --git a/Cargo.lock b/Cargo.lock index 2a6e76b..68751e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,14 +3,20 @@ version = 4 [[package]] -name = "aho-corasick" -version = "1.1.3" +name = "addr2line" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ - "memchr", + "gimli", ] +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "anstyle" version = "1.0.10" @@ -19,23 +25,26 @@ checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anyhow" -version = "1.0.96" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" - -[[package]] -name = "arc-swap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" - -[[package]] -name = "base62" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10e52a7bcb1d6beebee21fb5053af9e3cbb7a7ed1a4909e534040e676437ab1f" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" dependencies = [ - "rustversion", + "backtrace", +] + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", ] [[package]] @@ -44,12 +53,6 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.8.0" @@ -60,14 +63,10 @@ dependencies = [ ] [[package]] -name = "bstr" -version = "1.11.3" +name = "cfg-if" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" -dependencies = [ - "memchr", - "serde", -] +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clap" @@ -109,77 +108,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] -name = "crossbeam-deque" -version = "0.8.6" +name = "gimli" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "either" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "glob" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" - -[[package]] -name = "globset" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "globwalk" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" -dependencies = [ - "bitflags 1.3.2", - "ignore", - "walkdir", -] - -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "heck" @@ -187,99 +119,45 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "ignore" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - -[[package]] -name = "indexmap" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" - [[package]] name = "kramer" version = "0.1.0" dependencies = [ + "anyhow", "clap", "ron", - "rust-i18n", "serde", ] [[package]] -name = "lazy_static" -version = "1.5.0" +name = "libc" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libyml" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3302702afa434ffa30847a83305f0a69d6abd74293b6554c18ec85c7ef30c980" -dependencies = [ - "anyhow", - "version_check", -] - -[[package]] -name = "log" -version = "0.4.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] -name = "normpath" -version = "1.3.0" +name = "miniz_oxide" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "windows-sys", + "adler2", ] [[package]] -name = "once_cell" -version = "1.20.3" +name = "object" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] [[package]] name = "proc-macro2" @@ -299,35 +177,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - [[package]] name = "ron" version = "0.8.1" @@ -335,85 +184,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64", - "bitflags 2.8.0", + "bitflags", "serde", "serde_derive", ] [[package]] -name = "rust-i18n" -version = "3.1.3" +name = "rustc-demangle" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b3a6e1c6565b77c86d868eea3068b0eb39582510f9c78cfbd5c67bd36fda9b" -dependencies = [ - "globwalk", - "once_cell", - "regex", - "rust-i18n-macro", - "rust-i18n-support", - "smallvec", -] - -[[package]] -name = "rust-i18n-macro" -version = "3.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6180d8506af2b485ffc1eab7fc6d15678336a694f2b5efac5f2ca78c52928275" -dependencies = [ - "glob", - "once_cell", - "proc-macro2", - "quote", - "rust-i18n-support", - "serde", - "serde_json", - "serde_yml", - "syn", -] - -[[package]] -name = "rust-i18n-support" -version = "3.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938f16094e2b09e893b1f85c9da251739a832d4272a5957217977da3a0713bb6" -dependencies = [ - "arc-swap", - "base62", - "globwalk", - "itertools", - "lazy_static", - "normpath", - "once_cell", - "proc-macro2", - "regex", - "serde", - "serde_json", - "serde_yml", - "siphasher", - "toml", - "triomphe", -] - -[[package]] -name = "rustversion" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" - -[[package]] -name = "ryu" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "serde" @@ -435,60 +215,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_json" -version = "1.0.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_yml" -version = "0.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e2dd588bf1597a252c3b920e0143eb99b0f76e4e082f4c92ce34fbc9e71ddd" -dependencies = [ - "indexmap", - "itoa", - "libyml", - "memchr", - "ryu", - "serde", - "version_check", -] - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "smallvec" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "strsim" version = "0.11.1" @@ -506,91 +232,12 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "toml" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "triomphe" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" -dependencies = [ - "arc-swap", - "serde", - "stable_deref_trait", -] - [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -654,12 +301,3 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" -dependencies = [ - "memchr", -] diff --git a/Cargo.toml b/Cargo.toml index efd9b56..c70301d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,10 +12,16 @@ edition = "2021" # For serde info, see [dependencies.serde] ron = "0.8.1, >=0.8, <0.9" -rust-i18n = "3.1.3, ~3.1.3" +#rust-i18n = "3.1.3" + +[dependencies.anyhow] +version = "1.0" +features = [ + "backtrace", +] [dependencies.clap] -version = "4.5, ~4.5.27" +version = "4.5" default-features = false features = [ # From default features collection @@ -30,5 +36,5 @@ features = [ ] [dependencies.serde] -version = "1.0.219, ~1.0.217" +version = "1.0.219" features = ["derive"] diff --git a/imports_order.rs b/imports_order.rs new file mode 100644 index 0000000..4e9db5f --- /dev/null +++ b/imports_order.rs @@ -0,0 +1,17 @@ +// Acknowledge sister/child +mod module; + +// std +use std::*; + +// sister/child +use module1::*; + +// parent +use super::*; + +// ancestor of parent +use crate::*; + +// external +use external::*; diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..bf8fd6a --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,32 @@ +use std::path::PathBuf; + +use crate::FB_SECTOR_SIZE; + +use clap::Parser; + +#[derive(Parser, Debug)] +pub struct Args { + /// Path to source file or block device + #[arg(short, long, value_hint = clap::ValueHint::DirPath)] + pub input: PathBuf, + + /// Path to output file. Defaults to {input}.iso + #[arg(short, long, value_hint = clap::ValueHint::DirPath)] + pub output: Option, + + /// Path to rescue map. Defaults to {input}.map + #[arg(short, long, value_hint = clap::ValueHint::DirPath)] + pub map: Option, + + /// Max number of consecutive sectors to test as a group + #[arg(short, long, default_value_t = 128)] + pub cluster_length: u16, + + /// Number of brute force read passes + #[arg(short, long, default_value_t = 2)] + pub brute_passes: usize, + + /// Sector size + #[arg(short, long, default_value_t = FB_SECTOR_SIZE)] + pub sector_size: u16, +} diff --git a/src/io.rs b/src/io.rs index cc75e80..a69e402 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,4 +1,5 @@ use std::io::{self, Seek, SeekFrom}; +use std::path::{Path, PathBuf}; /// Get length of data stream. /// Physical length of data stream in bytes @@ -13,3 +14,23 @@ pub fn get_stream_length(stream: &mut S) -> io::Result { len } + +/// Generates a file path if one not provided. +/// source_name for fallback name. +pub fn get_path

(file_path: &Option

, source_name: &P, extension: &str) -> Option +where + P: AsRef, +{ + if let Some(f) = file_path { + return f.as_ref().to_path_buf().into(); + } + + PathBuf::from(format!( + "{:?}.{}", + source_name.as_ref().to_str()?, + extension + )) + .as_path() + .to_owned() + .into() +} diff --git a/src/main.rs b/src/main.rs index 208f96d..299cb69 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,87 +1,49 @@ +mod cli; mod io; mod mapping; mod recovery; use std::fs::{File, OpenOptions}; -use std::path::{Path, PathBuf}; -use clap::Parser; -use mapping::MapFile; +use cli::Args; +use mapping::prelude::*; use recovery::Recover; +use anyhow::{self, Context}; +use clap::Parser; + const FB_SECTOR_SIZE: u16 = 2048; const FB_PAD_VALUE: u8 = 0; -#[derive(Parser, Debug)] -struct Args { - /// Path to source file or block device - #[arg(short, long, value_hint = clap::ValueHint::DirPath)] - input: PathBuf, - - /// Path to output file. Defaults to {input}.iso - #[arg(short, long, value_hint = clap::ValueHint::DirPath)] - output: Option, - - /// Path to rescue map. Defaults to {input}.map - #[arg(short, long, value_hint = clap::ValueHint::DirPath)] - map: Option, - - /// Max number of consecutive sectors to test as a group - #[arg(short, long, default_value_t = 128)] - cluster_length: u16, - - /// Number of brute force read passes - #[arg(short, long, default_value_t = 2)] - brute_passes: usize, - - /// Sector size - #[arg(short, long, default_value_t = FB_SECTOR_SIZE)] - sector_size: u16, -} - -fn main() { +fn main() -> anyhow::Result<()> { let config = Args::parse(); // Live with it, prefer to use expect() here. // I'm lazy and don't want to mess around with comparing error types. // Thus, any error in I/O here should be treated as fatal. - let mut input: File = OpenOptions::new() - .read(true) - .open(&config.input.as_path()) - .expect("Failed to open input file"); + let mut input: File = { + let input_path = &config.input.as_path(); + + OpenOptions::new() + .read(true) + .open(&input_path) + .with_context(move || format!("Failed to open input file: {}", input_path.display()))? + }; let output: File = { // Keep this clean, make a short-lived binding. - let path = get_path(&config.output, &config.input.to_str().unwrap(), "iso"); + let path = crate::io::get_path(&config.output, &config.input, "iso") + .context("Failed to generate output path.")?; OpenOptions::new() .read(true) .write(true) .create(true) - .open(path) - .expect("Failed to open/create output file") + .open(&path) + .with_context(|| format!("Failed to open/create output file at: {}", path.display()))? }; - // Check if output file is shorter than input. - // If so, autoextend the output file. - // - // Is this actually needed? I don't think I need to preallocate the space. - /* - { - let input_len = - get_stream_length(&mut input).expect("Failed to get the length of the input data."); - let output_len = - get_stream_length(&mut output).expect("Failed to get the length of the output file."); - - if output_len < input_len { - output - .set_len(input_len) - .expect("Failed to autofill output file.") - } - } - */ - /* let map: MapFile = { let path = get_path( @@ -107,49 +69,16 @@ fn main() { }; */ - let buf_capacity = - crate::io::get_stream_length(&mut input).expect("Failed to get buffer capacity from input"); + let mut recover_tool = { + let buf_capacity = crate::io::get_stream_length(&mut input) + .context("Failed to get buffer capacity from input")?; - let mut recover_tool = Recover::new(config, input, output, MapFile::default(), buf_capacity); + Recover::new(&config, input, output, MapFile::default(), buf_capacity) + }; recover_tool.run(); - //todo!("Recovery, Map saving, and closure of all files."); - - /* - let mut buf: Vec = Vec::with_capacity( - get_stream_length(&mut input).expect("Failed to get the length of the input data.") - as usize, - ); - println!( - "Read {} bytes", - input - .read_to_end(&mut buf) - .expect("Failed to read complete input stream.") - ); - - println!("Wrote {} bytes", { - output - .write_all(&buf) - .expect("Failed to write complete output stream."); - &buf.len() - }); - */ -} - -/// Generates a file path if one not provided. -/// source_name for fallback name. -fn get_path

(file_path: &Option

, source_name: &str, extension: &str) -> PathBuf -where - P: AsRef, -{ - if let Some(f) = file_path { - f.as_ref().to_path_buf() - } else { - PathBuf::from(format!("{:?}.{}", source_name, extension)) - .as_path() - .to_owned() - } + Ok(()) } #[cfg(test)] diff --git a/src/mapping/cluster.rs b/src/mapping/cluster.rs index 0a68709..46a9562 100644 --- a/src/mapping/cluster.rs +++ b/src/mapping/cluster.rs @@ -54,3 +54,10 @@ impl Cluster { self } } + +#[cfg(test)] +mod tests { + use super::*; + + // Test for Cluster::subdivide() +} diff --git a/src/mapping/domain.rs b/src/mapping/domain.rs index aff5163..778520e 100644 --- a/src/mapping/domain.rs +++ b/src/mapping/domain.rs @@ -20,3 +20,8 @@ impl Domain { self.end - self.start } } + +#[cfg(test)] +mod tests { + use super::*; +} diff --git a/src/mapping/map.rs b/src/mapping/map.rs index 65c3f8f..3b821f6 100644 --- a/src/mapping/map.rs +++ b/src/mapping/map.rs @@ -1,7 +1,9 @@ use std::fs::File; +use std::io::Write; use super::{Cluster, Domain, Stage}; +use anyhow; use ron::de::from_reader; use ron::error::SpannedError; use serde::{Deserialize, Serialize}; @@ -197,11 +199,178 @@ impl MapFile { } } -pub fn write_map_to_file(file: File, map: &MapFile) -> ron::error::Result { - ron::ser::to_string_pretty( - map, - ron::ser::PrettyConfig::new() - .new_line("\n".to_string()) - .struct_names(true), - ) +pub fn write_map_to_file(file: &mut File, map: &MapFile) -> anyhow::Result { + let written_bytes = file.write( + ron::ser::to_string_pretty( + map, + ron::ser::PrettyConfig::new() + .new_line("\n".to_string()) + .struct_names(true), + )? + .as_bytes(), + )?; + + Ok(written_bytes) +} + +#[cfg(test)] +mod tests { + use super::*; + + // Test for MapFile::update() + + // Test for MapFile::get_stage() + #[test] + fn test_get_stage() { + use std::vec; + + let mut mf = MapFile::default(); + let mut mf_stage = mf.get_stage(); + + // If this fails here, there's something SERIOUSLY wrong. + assert!( + mf_stage == Stage::Untested, + "Determined stage to be {:?}, when {:?} was expeccted.", + mf_stage, + Stage::Untested + ); + + let stages = vec![ + Stage::Damaged, + Stage::ForIsolation(1), + Stage::ForIsolation(0), + Stage::Untested, + ]; + + mf.map = vec![]; + + for stage in stages { + mf.map.push(*Cluster::default().set_stage(stage)); + + mf_stage = mf.get_stage(); + + assert!( + stage == mf_stage, + "Expected stage to be {:?}, determined {:?} instead.", + stage, + mf_stage + ) + } + } + + // Test for MapFile::get_clusters() + #[test] + fn test_get_clusters() { + let mut mf = MapFile::default(); + + mf.map = vec![ + *Cluster::default().set_stage(Stage::Damaged), + *Cluster::default().set_stage(Stage::ForIsolation(0)), + *Cluster::default().set_stage(Stage::ForIsolation(1)), + Cluster::default(), + Cluster::default(), + *Cluster::default().set_stage(Stage::ForIsolation(1)), + *Cluster::default().set_stage(Stage::ForIsolation(0)), + *Cluster::default().set_stage(Stage::Damaged), + ]; + + let stages = vec![ + Stage::Damaged, + Stage::ForIsolation(1), + Stage::ForIsolation(0), + Stage::Untested, + ]; + + for stage in stages { + let expected = vec![ + *Cluster::default().set_stage(stage), + *Cluster::default().set_stage(stage), + ]; + let received = mf.get_clusters(stage); + + assert!( + expected == received, + "Expected clusters {:?}, got {:?}.", + expected, + received + ) + } + } + + // Test for MapFile::defrag() + #[test] + fn test_defrag() { + let mut mf = MapFile { + sector_size: 1, + domain: Domain { start: 0, end: 8 }, + map: vec![ + Cluster { + domain: Domain { start: 0, end: 1 }, + stage: Stage::Untested, + }, + Cluster { + domain: Domain { start: 1, end: 2 }, + stage: Stage::Untested, + }, + Cluster { + domain: Domain { start: 2, end: 3 }, + stage: Stage::Untested, + }, + Cluster { + domain: Domain { start: 3, end: 4 }, + stage: Stage::ForIsolation(0), + }, + Cluster { + domain: Domain { start: 4, end: 5 }, + stage: Stage::ForIsolation(0), + }, + Cluster { + domain: Domain { start: 5, end: 6 }, + stage: Stage::ForIsolation(1), + }, + Cluster { + domain: Domain { start: 6, end: 7 }, + stage: Stage::ForIsolation(0), + }, + Cluster { + domain: Domain { start: 7, end: 8 }, + stage: Stage::Damaged, + }, + ], + }; + + let expected = vec![ + Cluster { + domain: Domain { start: 0, end: 3 }, + stage: Stage::Untested, + }, + Cluster { + domain: Domain { start: 3, end: 5 }, + stage: Stage::ForIsolation(0), + }, + Cluster { + domain: Domain { start: 5, end: 6 }, + stage: Stage::ForIsolation(1), + }, + Cluster { + domain: Domain { start: 6, end: 7 }, + stage: Stage::ForIsolation(0), + }, + Cluster { + domain: Domain { start: 7, end: 8 }, + stage: Stage::Damaged, + }, + ]; + + mf.defrag(); + + let received = mf.map; + + assert!( + expected == received, + "Expected {:?} after defragging, got {:?}.", + expected, + received + ) + } } diff --git a/src/mapping/mod.rs b/src/mapping/mod.rs index 1791df0..48ef000 100644 --- a/src/mapping/mod.rs +++ b/src/mapping/mod.rs @@ -1,187 +1,12 @@ +#![allow(unused_imports)] + pub mod cluster; pub mod domain; pub mod map; - -use serde::{Deserialize, Serialize}; +pub mod prelude; +pub mod stage; pub use cluster::Cluster; pub use domain::Domain; pub use map::MapFile; - -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, PartialOrd)] -pub enum Stage { - Intact, - Untested, - ForIsolation(u8), - Damaged, -} - -impl Default for Stage { - fn default() -> Self { - Stage::Untested - } -} - -#[cfg(test)] -mod tests { - use super::*; - - // Test for Cluster::subdivide() - - // Test for MapFile::update() - - // Test for MapFile::get_stage() - #[test] - fn test_get_stage() { - use std::vec; - - let mut mf = MapFile::default(); - let mut mf_stage = mf.get_stage(); - - // If this fails here, there's something SERIOUSLY wrong. - assert!( - mf_stage == Stage::Untested, - "Determined stage to be {:?}, when {:?} was expeccted.", - mf_stage, - Stage::Untested - ); - - let stages = vec![ - Stage::Damaged, - Stage::ForIsolation(1), - Stage::ForIsolation(0), - Stage::Untested, - ]; - - mf.map = vec![]; - - for stage in stages { - mf.map.push(*Cluster::default().set_stage(stage)); - - mf_stage = mf.get_stage(); - - assert!( - stage == mf_stage, - "Expected stage to be {:?}, determined {:?} instead.", - stage, - mf_stage - ) - } - } - - // Test for MapFile::get_clusters() - #[test] - fn test_get_clusters() { - let mut mf = MapFile::default(); - - mf.map = vec![ - *Cluster::default().set_stage(Stage::Damaged), - *Cluster::default().set_stage(Stage::ForIsolation(0)), - *Cluster::default().set_stage(Stage::ForIsolation(1)), - Cluster::default(), - Cluster::default(), - *Cluster::default().set_stage(Stage::ForIsolation(1)), - *Cluster::default().set_stage(Stage::ForIsolation(0)), - *Cluster::default().set_stage(Stage::Damaged), - ]; - - let stages = vec![ - Stage::Damaged, - Stage::ForIsolation(1), - Stage::ForIsolation(0), - Stage::Untested, - ]; - - for stage in stages { - let expected = vec![ - *Cluster::default().set_stage(stage), - *Cluster::default().set_stage(stage), - ]; - let received = mf.get_clusters(stage); - - assert!( - expected == received, - "Expected clusters {:?}, got {:?}.", - expected, - received - ) - } - } - - // Test for MapFile::defrag() - #[test] - fn test_defrag() { - let mut mf = MapFile { - sector_size: 1, - domain: Domain { start: 0, end: 8 }, - map: vec![ - Cluster { - domain: Domain { start: 0, end: 1 }, - stage: Stage::Untested, - }, - Cluster { - domain: Domain { start: 1, end: 2 }, - stage: Stage::Untested, - }, - Cluster { - domain: Domain { start: 2, end: 3 }, - stage: Stage::Untested, - }, - Cluster { - domain: Domain { start: 3, end: 4 }, - stage: Stage::ForIsolation(0), - }, - Cluster { - domain: Domain { start: 4, end: 5 }, - stage: Stage::ForIsolation(0), - }, - Cluster { - domain: Domain { start: 5, end: 6 }, - stage: Stage::ForIsolation(1), - }, - Cluster { - domain: Domain { start: 6, end: 7 }, - stage: Stage::ForIsolation(0), - }, - Cluster { - domain: Domain { start: 7, end: 8 }, - stage: Stage::Damaged, - }, - ], - }; - - let expected = vec![ - Cluster { - domain: Domain { start: 0, end: 3 }, - stage: Stage::Untested, - }, - Cluster { - domain: Domain { start: 3, end: 5 }, - stage: Stage::ForIsolation(0), - }, - Cluster { - domain: Domain { start: 5, end: 6 }, - stage: Stage::ForIsolation(1), - }, - Cluster { - domain: Domain { start: 6, end: 7 }, - stage: Stage::ForIsolation(0), - }, - Cluster { - domain: Domain { start: 7, end: 8 }, - stage: Stage::Damaged, - }, - ]; - - mf.defrag(); - - let received = mf.map; - - assert!( - expected == received, - "Expected {:?} after defragging, got {:?}.", - expected, - received - ) - } -} +pub use stage::Stage; diff --git a/src/mapping/prelude.rs b/src/mapping/prelude.rs new file mode 100644 index 0000000..8dc3f40 --- /dev/null +++ b/src/mapping/prelude.rs @@ -0,0 +1,6 @@ +#![allow(unused_imports)] + +pub use super::cluster::Cluster; +pub use super::domain::Domain; +pub use super::map::{write_map_to_file, MapFile}; +pub use super::stage::Stage; diff --git a/src/mapping/stage.rs b/src/mapping/stage.rs new file mode 100644 index 0000000..9c874c3 --- /dev/null +++ b/src/mapping/stage.rs @@ -0,0 +1,20 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, PartialOrd)] +pub enum Stage { + Intact, + Untested, + ForIsolation(u8), + Damaged, +} + +impl Default for Stage { + fn default() -> Self { + Stage::Untested + } +} + +#[cfg(test)] +mod tests { + use super::*; +} diff --git a/src/recovery.rs b/src/recovery.rs index aae1371..0380b6e 100644 --- a/src/recovery.rs +++ b/src/recovery.rs @@ -1,16 +1,17 @@ use std::fs::{File, OpenOptions}; -use std::io::{BufWriter, Read, Seek, SeekFrom, Write}; -use std::ptr::read; +use std::io::{BufWriter, Read, Seek, Write}; -use crate::mapping::{Cluster, Domain, MapFile, Stage}; -use crate::Args; +use anyhow::Context; + +use crate::cli::Args; +use crate::mapping::prelude::*; #[derive(Debug)] #[allow(dead_code)] -pub struct Recover { +pub struct Recover<'a> { /// Buffer capacity in bytes. buf_capacity: u64, - config: Args, + config: &'a Args, input: File, output: BufWriter, @@ -18,8 +19,14 @@ pub struct Recover { stage: Stage, } -impl Recover { - pub fn new(config: Args, input: File, output: File, map: MapFile, buf_capacity: u64) -> Self { +impl<'a> Recover<'a> { + pub fn new( + config: &'a Args, + input: File, + output: File, + map: MapFile, + buf_capacity: u64, + ) -> Self { let stage = map.get_stage(); let mut r = Recover { @@ -64,27 +71,25 @@ impl Recover { } /// Attempt to copy all untested blocks. - fn copy_untested(&mut self) -> &mut Self { + fn copy_untested(&mut self) -> anyhow::Result<()> { let mut buf = vec![crate::FB_PAD_VALUE; self.buf_capacity as usize]; // Purely caching. let mut read_position = 0_u64; let last_read_position = crate::io::get_stream_length(&mut self.input) - .expect("Failed to get length of input stream") + .context("Failed to get length of input stream")? - self.buf_capacity; while read_position < last_read_position { - dbg!(read_position); - if let Err(err) = self.input.read_exact(&mut buf) { println!("Hit error: {:?}", err); self.input .seek_relative(self.buf_capacity as i64) - .expect("Failed to seek input by buf_capacity to skip previous error"); + .context("Failed to seek input by buf_capacity to skip previous error")?; } else { self.output .write_all(buf.as_slice()) - .expect("Failed to write data to output file"); + .context("Failed to write data to output file")?; self.map.update(Cluster { domain: Domain { @@ -99,41 +104,26 @@ impl Recover { } crate::mapping::map::write_map_to_file( - OpenOptions::new() - .create(true) - .write(true) - .open(crate::get_path( - &self.config.map, - self.config.input.to_str().unwrap(), - "map", - )) - .expect("Failed to open map file"), + { + let map_path = crate::io::get_path(&self.config.map, &self.config.input, "map") + .context("Failed to generate map path.")?; + + &mut OpenOptions::new() + .create(true) + .write(true) + .open(&map_path) + .with_context(|| { + format!("Failed to open map file at: {}", map_path.display()) + })? + }, &self.map, ) - .expect("Failed to write map file"); + .context("Failed to write map file")?; - /* - let mut untested: Vec = vec![]; - - for cluster in self.map.get_clusters(Stage::Untested).iter_mut() { - untested.append(&mut cluster.subdivide(self.map.sector_size as usize)); - } - - todo!("Read and save data."); - - */ - - self + Ok(()) } - /// Attempt to copy blocks via isolation at pass level. - fn copy_isolate(&mut self, level: u8) -> &mut Self { - todo!(); - - self - } - - /// Set buffer capacities as cluster length in bytes. + /// Set buffer capacity as cluster length in bytes. /// Varies depending on the recovery stage. fn set_buf_capacity(&mut self) -> &mut Self { self.buf_capacity = self.config.sector_size as u64 * self.config.cluster_length as u64;