///|
/// A stable count associated with one diagnostic code.
pub struct CodeCount {
code : String
count : Int
} derive(Eq, Debug)
///|
pub fn CodeCount::code(self : CodeCount) -> String {
self.code
}
///|
pub fn CodeCount::count(self : CodeCount) -> Int {
self.count
}
///|
/// A stable count associated with one source.
pub struct SourceCount {
source : SourceId
diagnostics : Int
labels : Int
} derive(Eq, Debug)
///|
pub fn SourceCount::source(self : SourceCount) -> SourceId {
self.source
}
///|
pub fn SourceCount::diagnostics(self : SourceCount) -> Int {
self.diagnostics
}
///|
pub fn SourceCount::labels(self : SourceCount) -> Int {
self.labels
}
///|
/// Aggregate information for dashboards, CI summaries, and batch reports.
///
/// Code and source groups preserve first-seen order, which makes output stable
/// without imposing a sorting policy on callers.
pub struct DiagnosticMetrics {
summary : Summary
coded : Int
labels : Int
primary_labels : Int
secondary_labels : Int
code_counts : Array[CodeCount]
source_counts : Array[SourceCount]
} derive(Eq, Debug)
///|
pub fn DiagnosticMetrics::summary(self : DiagnosticMetrics) -> Summary {
self.summary
}
///|
pub fn DiagnosticMetrics::coded(self : DiagnosticMetrics) -> Int {
self.coded
}
///|
pub fn DiagnosticMetrics::uncoded(self : DiagnosticMetrics) -> Int {
self.summary.total() - self.coded
}
///|
pub fn DiagnosticMetrics::labels(self : DiagnosticMetrics) -> Int {
self.labels
}
///|
pub fn DiagnosticMetrics::primary_labels(self : DiagnosticMetrics) -> Int {
self.primary_labels
}
///|
pub fn DiagnosticMetrics::secondary_labels(self : DiagnosticMetrics) -> Int {
self.secondary_labels
}
///|
pub fn DiagnosticMetrics::code_counts(
self : DiagnosticMetrics,
) -> Array[CodeCount] {
self.code_counts.copy()
}
///|
pub fn DiagnosticMetrics::source_counts(
self : DiagnosticMetrics,
) -> Array[SourceCount] {
self.source_counts.copy()
}
///|
pub fn DiagnosticMetrics::unique_codes(self : DiagnosticMetrics) -> Int {
self.code_counts.length()
}
///|
pub fn DiagnosticMetrics::unique_sources(self : DiagnosticMetrics) -> Int {
self.source_counts.length()
}
///|
pub fn DiagnosticMetrics::count_for_code(
self : DiagnosticMetrics,
code : String,
) -> Int {
for item in self.code_counts {
if item.code() == code {
return item.count()
}
}
0
}
///|
pub fn DiagnosticMetrics::diagnostics_for_source(
self : DiagnosticMetrics,
source : SourceId,
) -> Int {
for item in self.source_counts {
if item.source() == source {
return item.diagnostics()
}
}
0
}
///|
pub fn DiagnosticMetrics::labels_for_source(
self : DiagnosticMetrics,
source : SourceId,
) -> Int {
for item in self.source_counts {
if item.source() == source {
return item.labels()
}
}
0
}
///|
fn increment_code(counts : Array[CodeCount], code : String) -> Unit {
for index, item in counts {
if item.code() == code {
counts[index] = { code, count: item.count() + 1 }
return
}
}
counts.push({ code, count: 1 })
}
///|
fn find_source(counts : Array[SourceCount], source : SourceId) -> Int? {
for index, item in counts {
if item.source() == source {
return Some(index)
}
}
None
}
///|
fn record_source_diagnostic(
counts : Array[SourceCount],
source : SourceId,
) -> Unit {
match find_source(counts, source) {
Some(index) => {
let current = counts[index]
counts[index] = {
source,
diagnostics: current.diagnostics() + 1,
labels: current.labels(),
}
}
None => counts.push({ source, diagnostics: 1, labels: 0 })
}
}
///|
fn record_source_label(counts : Array[SourceCount], source : SourceId) -> Unit {
match find_source(counts, source) {
Some(index) => {
let current = counts[index]
counts[index] = {
source,
diagnostics: current.diagnostics(),
labels: current.labels() + 1,
}
}
None => counts.push({ source, diagnostics: 0, labels: 1 })
}
}
///|
/// Computes metrics without changing the bag or its diagnostics.
///
/// A diagnostic contributes at most once to a source's diagnostic count, even
/// when it has several labels in that source. Every label still contributes to
/// the corresponding label count.
pub fn DiagnosticBag::metrics(self : DiagnosticBag) -> DiagnosticMetrics {
let code_counts : Array[CodeCount] = []
let source_counts : Array[SourceCount] = []
let mut coded = 0
let mut labels = 0
let mut primary_labels = 0
let mut secondary_labels = 0
for diagnostic in self.to_array() {
match diagnostic.code() {
Some(code) => {
coded += 1
increment_code(code_counts, code)
}
None => ()
}
let seen_sources : Array[SourceId] = []
for label in diagnostic.labels() {
labels += 1
if label.is_primary() {
primary_labels += 1
} else {
secondary_labels += 1
}
record_source_label(source_counts, label.source())
if !seen_sources.any(fn(source) { source == label.source() }) {
seen_sources.push(label.source())
record_source_diagnostic(source_counts, label.source())
}
}
}
{
summary: self.summary(),
coded,
labels,
primary_labels,
secondary_labels,
code_counts,
source_counts,
}
}