Skip to main content

helm_schema_gen/
emission_report.rs

1use std::collections::BTreeMap;
2
3use crate::emission_policy::{EmissionClass, EmissionClassKind, EmissionOrigin};
4
5/// Fact totals at one emission-selection boundary.
6#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
7pub struct FactCounts {
8    /// Facts produced by lowering.
9    pub lowered: usize,
10    /// Facts retained by the selector.
11    pub selected: usize,
12    /// Facts removed by the selector.
13    pub dropped: usize,
14}
15
16/// How selected mandatory facts reached the generated document.
17#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
18pub struct MandatoryOutcomes {
19    /// Facts emitted as distinct constraints.
20    pub emitted: usize,
21    /// Facts folded into validation-equivalent base structure.
22    pub equivalent: usize,
23    /// Facts already implied by emitted structure.
24    pub redundant: usize,
25    /// Facts preserved through the fallback emitter.
26    pub fallback: usize,
27}
28
29impl MandatoryOutcomes {
30    /// Returns the total number of accounted mandatory facts.
31    #[must_use]
32    pub const fn total(self) -> usize {
33        self.emitted + self.equivalent + self.redundant + self.fallback
34    }
35}
36
37/// Counts of conditional carriers in the completed generated schema.
38#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
39pub struct CarrierCounts {
40    /// Conditional carriers anchored at the document root.
41    pub root: usize,
42    /// Conditional carriers anchored below the document root.
43    pub local: usize,
44    /// JSON Schema `if` nodes in the completed document.
45    pub condition_nodes: usize,
46    /// Largest number of lowered facts grouped into one emitted carrier.
47    pub grouping_fan_in: usize,
48}
49
50/// Outcomes reserved for canonical mandatory emission.
51#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
52pub struct CanonicalizationCounts {
53    /// Facts handled by canonical emission.
54    pub applied: usize,
55    /// Facts already represented by canonical structure.
56    pub redundant: usize,
57    /// Facts handled by the general fallback.
58    pub fallback: usize,
59    /// Default backfills skipped because object-union arms cannot expose an equivalent descendant.
60    pub default_backfill_abstentions: usize,
61}
62
63/// Ambiguous-union insertion abstentions grouped by the phase that requested them.
64#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
65pub struct InsertionAbstentionCounts {
66    /// Base path insertions skipped while materializing a projected document.
67    pub base_document: usize,
68    /// Member-descendant projections skipped while lowering conditional overlays.
69    pub conditional_member_projection: usize,
70    /// Nested requirement targets skipped while lowering requirement implications.
71    pub requirement_target: usize,
72}
73
74/// Fact and carrier accounting produced alongside a generated schema.
75#[derive(Debug, Default, Clone, PartialEq, Eq)]
76pub struct EmissionReport {
77    /// Accounting for the selector that produced the current document.
78    pub facts: FactCounts,
79    facts_by_class_and_origin: BTreeMap<(EmissionClassKind, EmissionOrigin), FactCounts>,
80    /// Outcomes for mandatory facts selected by the operative selector.
81    pub mandatory_outcomes: MandatoryOutcomes,
82    /// Completed-document carrier accounting.
83    pub carriers: CarrierCounts,
84    /// Canonical-emission accounting.
85    pub canonicalization: CanonicalizationCounts,
86    /// Ambiguous-union insertions that deliberately retained their original schema.
87    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    /// Returns operative-selector accounting for one policy class.
128    #[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    /// Returns operative-selector accounting for one class and producer pair.
134    #[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}