///|
/// Evaluate a batch of already computed reviews with the strict default gate.
pub fn evaluate_audit_gate(reviews : Array[HeaderReview]) -> AuditGateResult {
evaluate_audit_gate_with_policy(reviews, AuditGatePolicy::strict())
}
///|
/// Lint headers and evaluate them with the strict default gate.
pub fn evaluate_headers_gate(headers : Array[String]) -> AuditGateResult {
evaluate_audit_gate(lint_headers(headers))
}
///|
/// Lint headers with a filename policy and evaluate them with an audit policy.
pub fn evaluate_headers_gate_with_policy(
headers : Array[String],
filename_policy : FilenamePolicy,
audit_policy : AuditGatePolicy,
) -> AuditGateResult {
evaluate_audit_gate_with_policy(
lint_headers_with_policy(headers, filename_policy),
audit_policy,
)
}
///|
/// Evaluate a batch of reviews against explicit warning, error and repair limits.
pub fn evaluate_audit_gate_with_policy(
reviews : Array[HeaderReview],
policy : AuditGatePolicy,
) -> AuditGateResult {
let summary = summarize_reviews(reviews)
let reasons : Array[String] = []
if !policy.allow_rejected_headers && summary.rejected > 0 {
reasons.push("batch contains rejected headers")
}
if summary.errors > policy.max_errors {
reasons.push("error count exceeds policy limit")
}
if summary.warnings > policy.max_warnings {
reasons.push("warning count exceeds policy limit")
}
if !policy.allow_needs_attention && summary.needs_attention > 0 {
reasons.push("headers need manual attention")
}
if !policy.allow_filename_changes && summary.filenames_changed > 0 {
reasons.push("filenames changed during normalization")
}
let accepted = reasons.length() == 0
let status = if (!policy.allow_rejected_headers && summary.rejected > 0) ||
summary.errors > policy.max_errors {
Rejected
} else if accepted {
Accepted
} else {
NeedsAttention
}
{ accepted, status, summary, reasons }
}
///|
pub fn AuditGateResult::reason_text(self : AuditGateResult) -> String {
if self.reasons.is_empty() {
"ok"
} else {
join_strings(self.reasons, "; ")
}
}
///|
pub fn AuditGateResult::to_text(self : AuditGateResult) -> String {
let buf = StringBuilder(size_hint=192)
buf.write_string("accepted=")
buf.write_string(if self.accepted { "true" } else { "false" })
buf.write_string(", status=")
buf.write_string(self.status.name())
buf.write_string(", ")
buf.write_string(self.summary.to_text())
buf.write_string(", reasons=")
buf.write_string(self.reason_text())
buf.to_string()
}
///|
pub fn AuditSummary::is_clean(self : AuditSummary) -> Bool {
self.rejected == 0 &&
self.needs_attention == 0 &&
self.warnings == 0 &&
self.errors == 0 &&
self.filenames_changed == 0
}
///|
pub fn AuditSummary::has_failures(self : AuditSummary) -> Bool {
self.rejected > 0 || self.errors > 0
}
///|
pub fn AuditSummary::has_attention_items(self : AuditSummary) -> Bool {
self.needs_attention > 0 || self.warnings > 0 || self.filenames_changed > 0
}
///|
pub fn AuditSummary::clean_count(self : AuditSummary) -> Int {
self.accepted
}
///|
pub fn reviews_by_status(
reviews : Array[HeaderReview],
status : ReviewStatus,
) -> Array[HeaderReview] {
let found : Array[HeaderReview] = []
for review in reviews {
if review.status == status {
found.push(review)
}
}
found
}
///|
pub fn reviews_with_filename_changes(
reviews : Array[HeaderReview],
) -> Array[HeaderReview] {
let found : Array[HeaderReview] = []
for review in reviews {
match review.filename {
Some(report) => if report.changed { found.push(review) }
None => ()
}
}
found
}
///|
pub fn reviews_with_recommendation(
reviews : Array[HeaderReview],
code : RecommendationCode,
) -> Array[HeaderReview] {
let found : Array[HeaderReview] = []
for review in reviews {
if review.has_recommendation(code) {
found.push(review)
}
}
found
}
///|
pub fn HeaderReview::has_recommendation(
self : HeaderReview,
code : RecommendationCode,
) -> Bool {
for recommendation in self.recommendations {
if recommendation.code == code {
return true
}
}
false
}
///|
pub fn HeaderReview::recommendations_by_severity(
self : HeaderReview,
severity : Severity,
) -> Array[Recommendation] {
let found : Array[Recommendation] = []
for recommendation in self.recommendations {
if recommendation.severity == severity {
found.push(recommendation)
}
}
found
}
///|
fn join_strings(values : Array[String], separator : String) -> String {
let buf = StringBuilder(size_hint=values.length() * 24)
let mut first = true
for value in values {
if first {
first = false
} else {
buf.write_string(separator)
}
buf.write_string(value)
}
buf.to_string()
}