Skip to main content

pcf_debug/model/
diag.rs

1//! Diagnostics produced while walking and modelling a PCF file.
2//!
3//! A debugger must describe broken files, not reject them. Every structural
4//! anomaly we can detect is captured here as a [`Diagnostic`] rather than an
5//! error that aborts the walk.
6
7/// The class of an anomaly found in a file.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum DiagKind {
10    /// Two physical regions cover overlapping bytes.
11    Overlap { start: u64, len: u64 },
12    /// A run of bytes covered by no region (dead space / padding).
13    Gap { start: u64, len: u64 },
14    /// A declared region runs past the end of the file.
15    Truncated { start: u64, want: u64, have: u64 },
16    /// The block chain links back to a block already visited.
17    ChainCycle { at_offset: u64 },
18    /// A `next_table_offset` points to an earlier offset (legal under the
19    /// PFS-MS append model, surfaced for information only).
20    BackwardChainLink { from: u64, to: u64 },
21    /// The header holds the trailer sentinel; the partition-table head was
22    /// resolved from a file trailer (surfaced for information only).
23    TrailerResolved {
24        /// Offset of the resolved trailer.
25        trailer_offset: u64,
26        /// Partition-table head recorded in the trailer (0 = empty).
27        head: u64,
28        /// Whether the trailer flags the chain as backward-linked.
29        backward: bool,
30    },
31    /// A partition's stored `data_hash` does not match its bytes.
32    DataHashMismatch { uid: [u8; 16] },
33    /// A table block's stored `table_hash` does not match its bytes.
34    TableHashMismatch { block_index: usize },
35    /// A live entry failed the PCF conformance checks.
36    EntryInvalid { uid: [u8; 16], reason: String },
37    /// The file header could not be parsed.
38    BadHeader { reason: String },
39    /// A table block header could not be parsed.
40    BadBlock { offset: u64, reason: String },
41}
42
43/// How serious a [`Diagnostic`] is.
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum Severity {
46    Info,
47    Warning,
48    Error,
49}
50
51impl Severity {
52    /// Short uppercase tag used in text output.
53    pub fn tag(self) -> &'static str {
54        match self {
55            Severity::Info => "INFO",
56            Severity::Warning => "WARN",
57            Severity::Error => "ERROR",
58        }
59    }
60}
61
62/// One finding about a file.
63#[derive(Debug, Clone)]
64pub struct Diagnostic {
65    pub severity: Severity,
66    pub kind: DiagKind,
67    pub message: String,
68}
69
70impl Diagnostic {
71    pub fn info(kind: DiagKind, message: impl Into<String>) -> Self {
72        Self {
73            severity: Severity::Info,
74            kind,
75            message: message.into(),
76        }
77    }
78    pub fn warn(kind: DiagKind, message: impl Into<String>) -> Self {
79        Self {
80            severity: Severity::Warning,
81            kind,
82            message: message.into(),
83        }
84    }
85    pub fn error(kind: DiagKind, message: impl Into<String>) -> Self {
86        Self {
87            severity: Severity::Error,
88            kind,
89            message: message.into(),
90        }
91    }
92}