Skip to main content

omena_query/
sass_unsupported.rs

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}