///|
/// Summary of a validation pass.
pub(all) struct ValidationSummary {
error_count : Int
warning_count : Int
is_valid : Bool
first_error_code : String
}
///|
fn feature_exists(features : Array[Feature], id : String) -> Bool {
let mut result = false
for feature in features {
if feature.id == id {
result = true
}
}
result
}
///|
/// Validate observation identity and sample counts against a feature set.
pub fn validate_observations(
features : Array[Feature],
observations : Array[Observation],
) -> Array[Diagnostic] {
let diagnostics = []
for i, observation in observations {
if !sample_count_is_valid(observation.sample_count) {
diagnostics.push(
error_diagnostic(
code="observation.samples.invalid",
message="observation sample count must be positive",
index=i,
),
)
}
if !feature_exists(features, observation.feature_id) {
diagnostics.push(
error_diagnostic(
code="observation.feature.unknown",
message="observation refers to an unknown feature",
index=i,
),
)
}
}
diagnostics
}
///|
/// Validate that plan steps refer to the supplied features and summary.
pub fn validate_plan(
plan : MeasurementPlan,
features : Array[Feature],
) -> Array[Diagnostic] {
let diagnostics = []
for i, step in plan.steps {
if !feature_exists(features, step.feature_id) {
diagnostics.push(
error_diagnostic(
code="plan.feature.unknown",
message="plan step refers to an unknown feature",
index=i,
),
)
}
if step.device_id.trim().length() == 0 {
diagnostics.push(
error_diagnostic(
code="plan.device.empty",
message="plan step has no device identifier",
index=i,
),
)
}
if step.uncertainty < 0.0 {
diagnostics.push(
error_diagnostic(
code="plan.uncertainty.negative",
message="plan uncertainty must not be negative",
index=i,
),
)
}
}
if plan.summary.steps != plan.steps.length() {
diagnostics.push(
error_diagnostic(
code="plan.summary.steps.mismatch",
message="plan summary step count differs from steps",
index=-1,
),
)
}
if plan.summary.coverage < 0.0 || plan.summary.coverage > 1.0 {
diagnostics.push(
error_diagnostic(
code="plan.coverage.invalid",
message="plan coverage must be between zero and one",
index=-1,
),
)
}
diagnostics
}
///|
/// Validate a batch summary's internal counts and rates.
pub fn validate_batch_summary(summary : BatchSummary) -> Array[Diagnostic] {
let diagnostics = []
if summary.count < 0 ||
summary.pass_count < 0 ||
summary.fail_count < 0 ||
summary.unmeasured_count < 0 {
diagnostics.push(
error_diagnostic(
code="batch.count.negative",
message="batch counts must not be negative",
index=-1,
),
)
}
if summary.pass_count + summary.fail_count + summary.unmeasured_count !=
summary.count {
diagnostics.push(
error_diagnostic(
code="batch.count.mismatch",
message="batch verdict counts do not sum to count",
index=-1,
),
)
}
if summary.defect_rate < 0.0 || summary.defect_rate > 1.0 {
diagnostics.push(
error_diagnostic(
code="batch.defect_rate.invalid",
message="batch defect rate must be between zero and one",
index=-1,
),
)
}
diagnostics
}
///|
/// Summarize severity counts and the first error code.
pub fn validation_summary(diagnostics : Array[Diagnostic]) -> ValidationSummary {
let error_count = diagnostic_count(diagnostics, DiagnosticSeverity::Error)
let warning_count = diagnostic_count(diagnostics, DiagnosticSeverity::Warning)
let mut first_error_code = ""
for diagnostic in diagnostics {
if diagnostic.severity == DiagnosticSeverity::Error &&
first_error_code == "" {
first_error_code = diagnostic.code
}
}
{ error_count, warning_count, is_valid: error_count == 0, first_error_code }
}
///|
/// Return whether diagnostics include a code.
pub fn diagnostics_have_code(
diagnostics : Array[Diagnostic],
code : String,
) -> Bool {
let mut result = false
for diagnostic in diagnostics {
if diagnostic.code == code {
result = true
}
}
result
}
///|
/// Return all diagnostic codes in source order.
pub fn diagnostic_codes(diagnostics : Array[Diagnostic]) -> Array[String] {
let result = []
for diagnostic in diagnostics {
result.push(diagnostic.code)
}
result
}
///|
/// Return whether a validation result can be used for planning.
pub fn validation_can_plan(diagnostics : Array[Diagnostic]) -> Bool {
!has_errors(diagnostics)
}
///|
/// Return a stable validation status label.
pub fn validation_status(diagnostics : Array[Diagnostic]) -> String {
if has_errors(diagnostics) {
"invalid"
} else if diagnostics.length() > 0 {
"review"
} else {
"valid"
}
}