///|
pub(all) enum TriageBand {
Critical
Attention
Healthy
} derive(Debug, Eq, ToJson)
///|
pub(all) struct TriageItem {
rule : RuleKind
count : Int
errors : Int
warnings : Int
infos : Int
priority : Int
band : TriageBand
action : String
} derive(Debug, Eq, ToJson)
///|
pub(all) struct AnalysisTriage {
band : TriageBand
items : Array[TriageItem]
total_findings : Int
repair_score : Int
} derive(Debug, Eq, ToJson)
///|
/// Convert raw findings into a stable repair queue for editors and teaching tools.
pub fn triage_analysis(analysis : Analysis) -> AnalysisTriage {
let items : Array[TriageItem] = []
for rule in triage_rules() {
let findings = findings_for_rule(analysis, rule)
if findings.length() > 0 {
let mut errors = 0
let mut warnings = 0
let mut infos = 0
for finding in findings {
match finding.severity {
Error => errors += 1
Warning => warnings += 1
Info => infos += 1
}
}
let band = triage_band_for_counts(errors, warnings)
items.push({
rule,
count: findings.length(),
errors,
warnings,
infos,
priority: errors * 100 + warnings * 10 + infos,
band,
action: rule_remedy(rule),
})
}
}
let mut total_findings = 0
let mut repair_score = 0
for item in items {
total_findings += item.count
repair_score += item.priority
}
{ band: triage_band(analysis), items, total_findings, repair_score }
}
///|
fn triage_rules() -> Array[RuleKind] {
[
ParallelPerfect,
HiddenPerfect,
VoiceCrossing,
DissonancePlacement,
MelodyRange,
LargeLeap,
LeapRecovery,
SpeciesRhythm,
CadenceShape,
]
}
///|
fn triage_band_for_counts(errors : Int, warnings : Int) -> TriageBand {
if errors > 0 {
Critical
} else if warnings > 0 {
Attention
} else {
Healthy
}
}
///|
pub fn triage_band(analysis : Analysis) -> TriageBand {
let summary = summarize(analysis)
triage_band_for_counts(summary.errors, summary.warnings)
}
///|
pub fn triage_band_name(band : TriageBand) -> String {
match band {
Critical => "critical"
Attention => "attention"
Healthy => "healthy"
}
}
///|
pub fn triage_item_count(triage : AnalysisTriage) -> Int {
triage.items.length()
}
///|
pub fn triage_rule_count(triage : AnalysisTriage, rule : RuleKind) -> Int {
for item in triage.items {
if item.rule == rule {
return item.count
}
}
0
}
///|
pub fn triage_item(triage : AnalysisTriage, index : Int) -> TriageItem? {
if index < 0 || index >= triage.items.length() {
None
} else {
Some(triage.items[index])
}
}
///|
pub fn triage_is_actionable(triage : AnalysisTriage) -> Bool {
triage.total_findings > 0 && triage.items.length() > 0
}
///|
pub fn triage_priority_for_rule(
triage : AnalysisTriage,
rule : RuleKind,
) -> Int {
for item in triage.items {
if item.rule == rule {
return item.priority
}
}
0
}
///|
pub fn triage_actions(triage : AnalysisTriage) -> Array[String] {
let result : Array[String] = []
for item in triage.items {
result.push(item.action)
}
result
}
///|
pub fn triage_summary(triage : AnalysisTriage) -> String {
"band=\{triage_band_name(triage.band)} findings=\{triage.total_findings} repair-score=\{triage.repair_score}"
}
///|
pub fn triage_markdown(triage : AnalysisTriage) -> String {
let lines : Array[String] = [
"## Repair priority",
"",
"- \{triage_summary(triage)}",
"",
"| Priority | Rule | Band | Findings | Action |",
"| ---: | --- | --- | ---: | --- |",
]
for item in triage.items {
lines.push(
"| \{item.priority} | \{rule_family(item.rule)} | \{triage_band_name(item.band)} | \{item.count} | \{item.action} |",
)
}
lines.join("\n")
}
///|
pub fn triage_compare(left : AnalysisTriage, right : AnalysisTriage) -> Int {
right.repair_score - left.repair_score
}
///|
pub fn triage_rule_order(triage : AnalysisTriage) -> Array[RuleKind] {
let result : Array[RuleKind] = []
for item in triage.items {
result.push(item.rule)
}
result
}
///|
pub fn triage_item_summary(item : TriageItem) -> String {
"rule=\{rule_family(item.rule)} band=\{triage_band_name(item.band)} count=\{item.count} errors=\{item.errors} warnings=\{item.warnings} priority=\{item.priority}"
}
///|
pub fn triage_csv(triage : AnalysisTriage) -> String {
let lines : Array[String] = [
"rule,band,count,errors,warnings,infos,priority,action",
]
for item in triage.items {
lines.push(
"\{rule_family(item.rule)},\{triage_band_name(item.band)},\{item.count},\{item.errors},\{item.warnings},\{item.infos},\{item.priority},\{item.action}",
)
}
lines.join("\n")
}