Skip to main content

rust_par2/
types.rs

1//! Data types for PAR2 file sets.
2
3use std::collections::HashMap;
4use std::fmt;
5
6/// 16-byte MD5 hash.
7pub type Md5Hash = [u8; 16];
8
9/// 16-byte identifier (Recovery Set ID, File ID, etc.).
10pub type Id16 = [u8; 16];
11
12/// A parsed PAR2 file set containing all metadata needed for verification.
13#[derive(Debug, Clone)]
14pub struct Par2FileSet {
15    /// Recovery Set ID — all packets in a set share this.
16    pub recovery_set_id: Id16,
17    /// Slice (block) size in bytes.
18    pub slice_size: u64,
19    /// Files described in this PAR2 set, keyed by File ID.
20    pub files: HashMap<Id16, Par2File>,
21    /// Number of recovery slices available (counted from RecoverySlice packets).
22    pub recovery_block_count: u32,
23    /// Creator software string, if present.
24    pub creator: Option<String>,
25}
26
27/// Metadata for a single file in the PAR2 set.
28#[derive(Debug, Clone)]
29pub struct Par2File {
30    /// File ID (MD5 of hash16k + hash + file_id internal data).
31    pub file_id: Id16,
32    /// Full file MD5 hash.
33    pub hash: Md5Hash,
34    /// MD5 hash of the first 16 KiB of the file.
35    pub hash_16k: Md5Hash,
36    /// File size in bytes.
37    pub size: u64,
38    /// Filename (from the PAR2 packet, UTF-8 or best-effort decoded).
39    pub filename: String,
40    /// Per-slice checksums (MD5 + CRC32), in order. From IFSC packets.
41    pub slices: Vec<SliceChecksum>,
42}
43
44/// Checksum data for a single slice (block) of a file.
45#[derive(Debug, Clone, Copy)]
46pub struct SliceChecksum {
47    /// MD5 hash of this slice.
48    pub md5: Md5Hash,
49    /// CRC32 of this slice (the full slice, zero-padded if it's the last partial slice).
50    pub crc32: u32,
51}
52
53/// Result of verifying a PAR2 file set against actual files on disk.
54#[derive(Debug)]
55pub struct VerifyResult {
56    /// Files that are intact (MD5 matches).
57    pub intact: Vec<VerifiedFile>,
58    /// Files that are damaged (exist but MD5 doesn't match).
59    pub damaged: Vec<DamagedFile>,
60    /// Files that are missing entirely.
61    pub missing: Vec<MissingFile>,
62    /// Total number of recovery blocks available in the PAR2 set.
63    pub recovery_blocks_available: u32,
64    /// Whether repair is theoretically possible (enough recovery blocks).
65    pub repair_possible: bool,
66}
67
68impl VerifyResult {
69    /// Returns true if all files are intact.
70    pub fn all_correct(&self) -> bool {
71        self.damaged.is_empty() && self.missing.is_empty()
72    }
73
74    /// Total number of damaged/missing blocks that need repair.
75    pub fn blocks_needed(&self) -> u32 {
76        let damaged_blocks: u32 = self.damaged.iter().map(|d| d.damaged_block_count).sum();
77        let missing_blocks: u32 = self.missing.iter().map(|m| m.block_count).sum();
78        damaged_blocks + missing_blocks
79    }
80}
81
82/// A file that passed verification.
83#[derive(Debug)]
84pub struct VerifiedFile {
85    pub filename: String,
86    pub size: u64,
87}
88
89/// A file that exists but has damage.
90#[derive(Debug)]
91pub struct DamagedFile {
92    pub filename: String,
93    pub size: u64,
94    /// Number of blocks in this file that are damaged.
95    pub damaged_block_count: u32,
96    /// Total blocks in this file.
97    pub total_block_count: u32,
98    /// Indices of the specific damaged blocks within this file (0-based).
99    pub damaged_block_indices: Vec<u32>,
100}
101
102/// A file that is missing entirely.
103#[derive(Debug)]
104pub struct MissingFile {
105    pub filename: String,
106    pub expected_size: u64,
107    /// Number of blocks this file contributes to the repair requirement.
108    pub block_count: u32,
109}
110
111impl fmt::Display for VerifyResult {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        if self.all_correct() {
114            write!(f, "All {} files correct", self.intact.len())
115        } else {
116            write!(
117                f,
118                "{} intact, {} damaged, {} missing — {} blocks needed, {} available",
119                self.intact.len(),
120                self.damaged.len(),
121                self.missing.len(),
122                self.blocks_needed(),
123                self.recovery_blocks_available,
124            )
125        }
126    }
127}