Skip to main content

tzcompile/compare/
report.rs

1//! The result of an oracle comparison for one zone, plus honest rendering.
2
3use super::semantic::Difference;
4
5/// Which comparison produced this result — so summaries never overclaim what was checked.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum ComparisonKind {
8    /// Decoded both TZif files and diffed the model (footer + transitions + types).
9    DecodedTzif,
10    /// Diffed `zdump -v -c lo,hi` behaviour over an inclusive year horizon.
11    ZdumpBehaviour { lo: i32, hi: i32 },
12}
13
14/// Outcome of comparing our output against reference `zic` for a single zone.
15#[derive(Debug, Clone)]
16pub struct ZoneComparison {
17    pub zone: String,
18    pub kind: ComparisonKind,
19    /// Differences found; empty means the two agree under this comparison kind.
20    pub differences: Vec<Difference>,
21    /// Whether the two files were byte-identical (a stricter, informational signal).
22    pub byte_identical: bool,
23}
24
25impl ZoneComparison {
26    /// Whether the two agree under the comparison that was run. Note this is scoped to the
27    /// `kind`: a `DecodedTzif` match is *not* a claim about `zdump` behaviour, and a
28    /// `ZdumpBehaviour` match is scoped to its declared horizon.
29    pub fn is_match(&self) -> bool {
30        self.differences.is_empty()
31    }
32
33    /// A short, deterministic, honest human summary that names the comparison performed.
34    pub fn summary(&self) -> String {
35        let label = match self.kind {
36            ComparisonKind::DecodedTzif => "decoded TZif match".to_string(),
37            ComparisonKind::ZdumpBehaviour { lo, hi } => {
38                format!("zdump behaviour match over {lo}..{hi}")
39            }
40        };
41        let mut s = String::new();
42        if self.is_match() {
43            s.push_str(&format!(
44                "{}: {label}{}",
45                self.zone,
46                if self.byte_identical {
47                    " (byte-identical)"
48                } else {
49                    ""
50                }
51            ));
52        } else {
53            let what = match self.kind {
54                ComparisonKind::DecodedTzif => "decoded-TZif",
55                ComparisonKind::ZdumpBehaviour { .. } => "zdump-behaviour",
56            };
57            s.push_str(&format!(
58                "{}: {} {what} difference(s):",
59                self.zone,
60                self.differences.len()
61            ));
62            for d in &self.differences {
63                s.push_str(&format!(
64                    "\n  - {}: ours={:?} theirs={:?}",
65                    d.what, d.ours, d.theirs
66                ));
67            }
68        }
69        s
70    }
71}