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}