1use std::collections::BTreeMap;
2
3use crate::emission_policy::{EmissionClass, EmissionClassKind, EmissionOrigin};
4
5#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
7pub struct FactCounts {
8 pub lowered: usize,
10 pub selected: usize,
12 pub dropped: usize,
14}
15
16#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
18pub struct MandatoryOutcomes {
19 pub emitted: usize,
21 pub equivalent: usize,
23 pub redundant: usize,
25 pub fallback: usize,
27}
28
29impl MandatoryOutcomes {
30 #[must_use]
32 pub const fn total(self) -> usize {
33 self.emitted + self.equivalent + self.redundant + self.fallback
34 }
35}
36
37#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
39pub struct CarrierCounts {
40 pub root: usize,
42 pub local: usize,
44 pub condition_nodes: usize,
46 pub grouping_fan_in: usize,
48}
49
50#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
52pub struct CanonicalizationCounts {
53 pub applied: usize,
55 pub redundant: usize,
57 pub fallback: usize,
59 pub default_backfill_abstentions: usize,
61}
62
63#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
65pub struct InsertionAbstentionCounts {
66 pub base_document: usize,
68 pub conditional_member_projection: usize,
70 pub requirement_target: usize,
72}
73
74#[derive(Debug, Default, Clone, PartialEq, Eq)]
76pub struct EmissionReport {
77 pub facts: FactCounts,
79 facts_by_class_and_origin: BTreeMap<(EmissionClassKind, EmissionOrigin), FactCounts>,
80 pub mandatory_outcomes: MandatoryOutcomes,
82 pub carriers: CarrierCounts,
84 pub canonicalization: CanonicalizationCounts,
86 pub insertion_abstentions: InsertionAbstentionCounts,
88}
89
90#[derive(Clone, Copy)]
91pub(crate) struct FactRecord<'a> {
92 pub(crate) class: &'a EmissionClass,
93 pub(crate) origin: EmissionOrigin,
94 pub(crate) selected: bool,
95}
96
97impl EmissionReport {
98 pub(crate) fn record_fact(&mut self, fact: FactRecord<'_>) {
99 Self::record_counts(
100 &mut self.facts,
101 &mut self.facts_by_class_and_origin,
102 fact.class.kind(),
103 fact.origin,
104 fact.selected,
105 );
106 }
107
108 fn record_counts(
109 totals: &mut FactCounts,
110 by_class_and_origin: &mut BTreeMap<(EmissionClassKind, EmissionOrigin), FactCounts>,
111 class: EmissionClassKind,
112 origin: EmissionOrigin,
113 selected: bool,
114 ) {
115 totals.lowered += 1;
116 let counts = by_class_and_origin.entry((class, origin)).or_default();
117 counts.lowered += 1;
118 if selected {
119 totals.selected += 1;
120 counts.selected += 1;
121 } else {
122 totals.dropped += 1;
123 counts.dropped += 1;
124 }
125 }
126
127 #[must_use]
129 pub fn counts_for_class(&self, class: EmissionClassKind) -> FactCounts {
130 Self::counts_for(&self.facts_by_class_and_origin, class)
131 }
132
133 #[must_use]
135 pub fn counts_for_class_and_origin(
136 &self,
137 class: EmissionClassKind,
138 origin: EmissionOrigin,
139 ) -> FactCounts {
140 self.facts_by_class_and_origin
141 .get(&(class, origin))
142 .copied()
143 .unwrap_or_default()
144 }
145
146 fn counts_for(
147 counts: &BTreeMap<(EmissionClassKind, EmissionOrigin), FactCounts>,
148 class: EmissionClassKind,
149 ) -> FactCounts {
150 counts
151 .iter()
152 .filter(|((candidate, _), _)| *candidate == class)
153 .fold(FactCounts::default(), |mut total, (_, counts)| {
154 total.lowered += counts.lowered;
155 total.selected += counts.selected;
156 total.dropped += counts.dropped;
157 total
158 })
159 }
160}