diff options
author | Omer BenAmram <omerbenamram@gmail.com> | 2019-06-06 12:52:42 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-06-06 12:52:42 +0300 |
commit | b44ee90251f6cda76a26efaba719930cd715b082 (patch) | |
tree | 5d2ae030f99af191821dca2bb3dc91dad55a95cc | |
parent | b328f064536fd3ad5da247371ff4184aefcf8b25 (diff) | |
parent | 89476f2c07d65c409bc0539764a6c750bf2679c1 (diff) |
Merge pull request #9 from omerbenamram/feature/entry-ranges
implement ability to select entry ranges
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | azure-pipelines.yml | 2 | ||||
-rw-r--r-- | src/bin/mft_dump.rs | 120 |
3 files changed, 121 insertions, 5 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6af8d86..487e952 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.4.3] - UNRELEASED +### Added +- `mft_dump` can now dump only a specific range of entries with `-r`. + ### Fixed - Fixed an issue with debug-logs - ## [0.4.2] - 2019-06-04 ### Fixed diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d3feff9..cca8c73 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -37,7 +37,7 @@ steps: - script: cargo build --release displayName: Cargo build - script: cargo test - displayName: Cargo test + displayName: Cargo test --all - bash: | MY_TAG="$(Build.SourceBranch)" MY_TAG=${MY_TAG#refs/tags/} diff --git a/src/bin/mft_dump.rs b/src/bin/mft_dump.rs index ea272de..75f4a8e 100644 --- a/src/bin/mft_dump.rs +++ b/src/bin/mft_dump.rs @@ -12,13 +12,14 @@ use mft::csv::FlatMftEntryWithName; use snafu::ErrorCompat; use std::error::Error; use std::fs::File; -use std::io::Read; use std::io::Write; use std::path::{Path, PathBuf}; use std::process::exit; use mft::entry::ZERO_HEADER; use std::fmt::Write as FmtWrite; +use std::ops::RangeInclusive; +use std::str::FromStr; use std::{fs, io, path}; /// Simple error macro for use inside of internal errors in `MftDump` @@ -46,6 +47,95 @@ impl OutputFormat { } } +struct Ranges(Vec<RangeInclusive<usize>>); + +impl Ranges { + pub fn chain(&self) -> impl Iterator<Item = usize> + '_ { + self.0.iter().cloned().flatten() + } +} + +impl FromStr for Ranges { + type Err = StdErr; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let mut ranges = vec![]; + for x in s.split(',') { + // range + if x.contains('-') { + let range: Vec<&str> = x.split('-').collect(); + if range.len() != 2 { + return err!( + "Failed to parse ranges: Range should contain exactly one `-`, found {}", + x + ); + } + + ranges.push(range[0].parse()?..=range[1].parse()?); + } else { + let n = x.parse()?; + ranges.push(n..=n); + } + } + + Ok(Ranges(ranges)) + } +} + +#[cfg(test)] +mod tests { + use super::Ranges; + use std::str::FromStr; + + #[test] + fn it_works_with_single_number() { + let ranges = Ranges::from_str("1").unwrap(); + assert_eq!(ranges.0, vec![1..=1]); + } + + #[test] + fn it_works_with_a_range() { + let ranges = Ranges::from_str("1-5").unwrap(); + assert_eq!(ranges.0, vec![1..=5]); + } + + #[test] + fn it_works_with_a_range_and_a_number() { + let ranges = Ranges::from_str("1-5,8").unwrap(); + assert_eq!(ranges.0, vec![1..=5, 8..=8]); + } + + #[test] + fn it_works_with_a_number_and_a_range() { + let ranges = Ranges::from_str("1-5,8").unwrap(); + assert_eq!(ranges.0, vec![1..=5, 8..=8]); + } + + #[test] + fn it_works_with_more_than_2_number_and_a_range() { + let ranges = Ranges::from_str("1-5,8,10-19").unwrap(); + assert_eq!(ranges.0, vec![1..=5, 8..=8, 10..=19]); + } + + #[test] + fn it_works_with_two_ranges() { + let ranges = Ranges::from_str("1-10,20-25").unwrap(); + assert_eq!(ranges.0, vec![1..=10, 20..=25]); + } + + #[test] + fn it_errors_on_a_random_string() { + let ranges = Ranges::from_str("hello"); + assert!(ranges.is_err()) + } + + #[test] + fn it_errors_on_a_range_with_too_many_dashes() { + let ranges = Ranges::from_str("1-5-8"); + assert!(ranges.is_err()) + } +} + struct MftDump { filepath: PathBuf, // We use an option here to be able to move the output out of mftdump from a mutable reference. @@ -53,6 +143,7 @@ struct MftDump { data_streams_output: Option<PathBuf>, verbosity_level: Option<Level>, output_format: OutputFormat, + ranges: Option<Ranges>, backtraces: bool, } @@ -98,12 +189,18 @@ impl MftDump { } }; + let ranges = match matches.value_of("entry-range") { + Some(range) => Some(Ranges::from_str(range)?), + None => None, + }; + Ok(MftDump { filepath: PathBuf::from(matches.value_of("INPUT").expect("Required argument")), output, data_streams_output, verbosity_level, output_format, + ranges, backtraces, }) } @@ -203,8 +300,18 @@ impl MftDump { }; let number_of_entries = parser.get_entry_count(); - for i in 0..number_of_entries { - let entry = parser.get_entry(i); + + // Move ranges out of self here to avoid immutably locking self during + // the `for i in entries` loop. + let take_ranges = self.ranges.take(); + + let entries = match take_ranges { + Some(ref ranges) => Box::new(ranges.chain()), + None => Box::new(0..number_of_entries as usize) as Box<dyn Iterator<Item = usize>>, + }; + + for i in entries { + let entry = parser.get_entry(i as u64); let entry = match entry { Ok(entry) => match &entry.header.signature { @@ -379,6 +486,13 @@ fn main() { .help("Output format."), ) .arg( + Arg::with_name("entry-range") + .long("--ranges") + .short("-r") + .takes_value(true) + .help(indoc!("Dumps only the given entry range(s), for example, `1-15,30` will dump entries 1-15, and 30")), + ) + .arg( Arg::with_name("output-target") .long("--output") .short("-f") |