1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5const SASS_UNSUPPORTED_LEDGER_PRODUCT_VIEW_JSON: &str =
6 include_str!("../data/sass-unsupported-ledger.json");
7
8#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
9#[serde(rename_all = "camelCase")]
10struct CanonicalSassUnsupportedLedgerReportV0 {
11 schema_version: String,
12 product: String,
13 semantic_site_count: usize,
14 ledger_semantic_site_count: usize,
15 raw_pattern_hit_count: usize,
16 non_semantic_pattern_hit_count: usize,
17 linked_site_count: usize,
18 named_gap_site_count: usize,
19 linked_case_count: usize,
20 ledger_metadata_valid: bool,
21 all_semantic_sites_match_ledger: bool,
22 all_sites_linked_or_named_gap: bool,
23 all_linked_cases_match_reason_class: bool,
24 all_linked_cases_are_imported_sound_bail_cases: bool,
25 all_bail_site_ledger_checks_hold: bool,
26 records: Vec<OmenaQuerySassUnsupportedLedgerRecordV0>,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
30#[serde(rename_all = "camelCase")]
31pub struct OmenaQuerySassUnsupportedLedgerRecordV0 {
32 pub file: String,
33 pub ordinal: usize,
34 pub reason: String,
35 pub current_line: Option<usize>,
36 pub ledger_line_hint: usize,
37 pub present_in_current_sources: bool,
38 pub linked_fixture_ids: Vec<String>,
39 pub gap: Option<String>,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
43#[serde(rename_all = "camelCase")]
44pub struct OmenaQuerySassUnsupportedCountV0 {
45 pub key: String,
46 pub count: usize,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
50#[serde(rename_all = "camelCase")]
51pub struct OmenaQuerySassUnsupportedLedgerViewV0 {
52 pub schema_version: &'static str,
53 pub product: &'static str,
54 pub source_product: String,
55 pub semantic_site_count: usize,
56 pub ledger_semantic_site_count: usize,
57 pub surface_record_count: usize,
58 pub raw_pattern_hit_count: usize,
59 pub non_semantic_pattern_hit_count: usize,
60 pub linked_site_count: usize,
61 pub named_gap_site_count: usize,
62 pub linked_case_count: usize,
63 pub ledger_metadata_valid: bool,
64 pub all_semantic_sites_match_ledger: bool,
65 pub all_sites_linked_or_named_gap: bool,
66 pub all_linked_cases_match_reason_class: bool,
67 pub all_linked_cases_are_imported_sound_bail_cases: bool,
68 pub all_bail_site_ledger_checks_hold: bool,
69 pub surface_matches_ledger: bool,
70 pub summary_view_ready: bool,
71 pub file_counts: Vec<OmenaQuerySassUnsupportedCountV0>,
72 pub reason_counts: Vec<OmenaQuerySassUnsupportedCountV0>,
73 pub records: Vec<OmenaQuerySassUnsupportedLedgerRecordV0>,
74}
75
76pub fn summarize_omena_query_sass_unsupported_ledger_view_v0()
77-> Result<OmenaQuerySassUnsupportedLedgerViewV0, serde_json::Error> {
78 let canonical = serde_json::from_str::<CanonicalSassUnsupportedLedgerReportV0>(
79 SASS_UNSUPPORTED_LEDGER_PRODUCT_VIEW_JSON,
80 )?;
81 Ok(project_sass_unsupported_ledger_view(canonical))
82}
83
84fn project_sass_unsupported_ledger_view(
85 canonical: CanonicalSassUnsupportedLedgerReportV0,
86) -> OmenaQuerySassUnsupportedLedgerViewV0 {
87 let file_counts = count_records_by(&canonical.records, |record| record.file.as_str());
88 let reason_counts = count_records_by(&canonical.records, |record| record.reason.as_str());
89 let surface_record_count = canonical.records.len();
90 let surface_matches_ledger = canonical.all_semantic_sites_match_ledger
91 && surface_record_count == canonical.semantic_site_count
92 && surface_record_count == canonical.ledger_semantic_site_count;
93 let summary_view_ready = canonical.all_bail_site_ledger_checks_hold && surface_matches_ledger;
94 OmenaQuerySassUnsupportedLedgerViewV0 {
95 schema_version: "0",
96 product: "omena-query.sass-unsupported-ledger-view",
97 source_product: canonical.product,
98 semantic_site_count: canonical.semantic_site_count,
99 ledger_semantic_site_count: canonical.ledger_semantic_site_count,
100 surface_record_count,
101 raw_pattern_hit_count: canonical.raw_pattern_hit_count,
102 non_semantic_pattern_hit_count: canonical.non_semantic_pattern_hit_count,
103 linked_site_count: canonical.linked_site_count,
104 named_gap_site_count: canonical.named_gap_site_count,
105 linked_case_count: canonical.linked_case_count,
106 ledger_metadata_valid: canonical.ledger_metadata_valid,
107 all_semantic_sites_match_ledger: canonical.all_semantic_sites_match_ledger,
108 all_sites_linked_or_named_gap: canonical.all_sites_linked_or_named_gap,
109 all_linked_cases_match_reason_class: canonical.all_linked_cases_match_reason_class,
110 all_linked_cases_are_imported_sound_bail_cases: canonical
111 .all_linked_cases_are_imported_sound_bail_cases,
112 all_bail_site_ledger_checks_hold: canonical.all_bail_site_ledger_checks_hold,
113 surface_matches_ledger,
114 summary_view_ready,
115 file_counts,
116 reason_counts,
117 records: canonical.records,
118 }
119}
120
121fn count_records_by(
122 records: &[OmenaQuerySassUnsupportedLedgerRecordV0],
123 key: impl Fn(&OmenaQuerySassUnsupportedLedgerRecordV0) -> &str,
124) -> Vec<OmenaQuerySassUnsupportedCountV0> {
125 let mut counts = BTreeMap::<String, usize>::new();
126 for record in records {
127 *counts.entry(key(record).to_string()).or_default() += 1;
128 }
129 counts
130 .into_iter()
131 .map(|(key, count)| OmenaQuerySassUnsupportedCountV0 { key, count })
132 .collect()
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn product_view_reuses_the_independent_ledger_projection() -> Result<(), serde_json::Error> {
141 let view = summarize_omena_query_sass_unsupported_ledger_view_v0()?;
142 assert_eq!(
143 view.source_product,
144 "omena-diff-test.sass-spec-bail-site-ledger"
145 );
146 assert_eq!(view.semantic_site_count, 33);
147 assert_eq!(view.surface_record_count, 33);
148 assert!(view.all_semantic_sites_match_ledger);
149 assert!(view.surface_matches_ledger);
150 assert!(view.summary_view_ready);
151 assert_eq!(
152 view.file_counts.iter().map(|row| row.count).sum::<usize>(),
153 view.surface_record_count
154 );
155 assert_eq!(
156 view.reason_counts
157 .iter()
158 .map(|row| row.count)
159 .sum::<usize>(),
160 view.surface_record_count
161 );
162 Ok(())
163 }
164
165 #[test]
166 fn hiding_a_ledger_record_invalidates_the_surface_projection() -> Result<(), serde_json::Error>
167 {
168 let mut canonical = serde_json::from_str::<CanonicalSassUnsupportedLedgerReportV0>(
169 SASS_UNSUPPORTED_LEDGER_PRODUCT_VIEW_JSON,
170 )?;
171 canonical.records.pop();
172 let view = project_sass_unsupported_ledger_view(canonical);
173 assert!(!view.surface_matches_ledger);
174 assert!(!view.summary_view_ready);
175 Ok(())
176 }
177}