///|
fn report_json_string(value : String) -> String {
@json.to_json(value).stringify()
}
///|
fn relation_label(relation : VrpRelation) -> String {
match relation {
Authorizes => "authorizes"
AsnMismatch => "asnMismatch"
LengthExceeded => "lengthExceeded"
AsnAndLengthMismatch => "asnAndLengthMismatch"
}
}
///|
fn write_decision_json(
buffer : StringBuilder,
decision : ValidationDecision,
) -> Unit {
buffer.write_string("{\"status\":")
buffer.write_string(report_json_string(validity_label(decision.validity)))
buffer.write_string(",\"coveringVrps\":[")
for index, evidence in decision.matches {
if index > 0 {
buffer.write_char(',')
}
buffer.write_string("{\"asn\":")
buffer.write_string(evidence.vrp.asn().to_string())
buffer.write_string(",\"prefix\":")
buffer.write_string(report_json_string(evidence.vrp.prefix().to_string()))
buffer.write_string(",\"maxLength\":")
buffer.write_string(evidence.vrp.max_length().to_string())
buffer.write_string(",\"relation\":")
buffer.write_string(report_json_string(relation_label(evidence.relation)))
buffer.write_char('}')
}
buffer.write_string("]}")
}
///|
fn write_slurm_apply_json(
buffer : StringBuilder,
applied : SlurmApplyResult,
) -> Unit {
buffer.write_string("{\"effectiveVrps\":")
buffer.write_string(applied.vrps.length().to_string())
buffer.write_string(",\"filtered\":")
buffer.write_string(applied.filtered.to_string())
buffer.write_string(",\"duplicateAssertions\":")
buffer.write_string(applied.duplicate_assertions.to_string())
buffer.write_char('}')
}
///|
fn write_optional_slurm_apply_json(
buffer : StringBuilder,
applied : SlurmApplyResult?,
) -> Unit {
match applied {
Some(result) => write_slurm_apply_json(buffer, result)
None => buffer.write_string("null")
}
}
///|
fn write_ipv6_slurm_apply_json(
buffer : StringBuilder,
applied : Ipv6SlurmApplyResult,
) -> Unit {
buffer.write_string("{\"effectiveVrps\":")
buffer.write_string(applied.vrps.length().to_string())
buffer.write_string(",\"filtered\":")
buffer.write_string(applied.filtered.to_string())
buffer.write_string(",\"duplicateAssertions\":")
buffer.write_string(applied.duplicate_assertions.to_string())
buffer.write_char('}')
}
///|
fn write_optional_ipv6_slurm_apply_json(
buffer : StringBuilder,
applied : Ipv6SlurmApplyResult?,
) -> Unit {
match applied {
Some(result) => write_ipv6_slurm_apply_json(buffer, result)
None => buffer.write_string("null")
}
}
///|
fn write_vrp_record_json(buffer : StringBuilder, record : VrpRecord) -> Unit {
buffer.write_string("{\"asn\":")
buffer.write_string(record.vrp.asn().to_string())
buffer.write_string(",\"prefix\":")
buffer.write_string(report_json_string(record.vrp.prefix().to_string()))
buffer.write_string(",\"maxLength\":")
buffer.write_string(record.vrp.max_length().to_string())
buffer.write_string(",\"trustAnchor\":")
buffer.write_string(report_json_string(record.trust_anchor))
buffer.write_char('}')
}
///|
fn write_ipv6_vrp_record_json(
buffer : StringBuilder,
record : Ipv6VrpRecord,
) -> Unit {
buffer.write_string("{\"asn\":")
buffer.write_string(record.vrp.asn().to_string())
buffer.write_string(",\"prefix\":")
buffer.write_string(report_json_string(record.vrp.prefix().to_string()))
buffer.write_string(",\"maxLength\":")
buffer.write_string(record.vrp.max_length().to_string())
buffer.write_string(",\"trustAnchor\":")
buffer.write_string(report_json_string(record.trust_anchor))
buffer.write_char('}')
}
///|
fn write_ipv6_decision_json(
buffer : StringBuilder,
decision : Ipv6ValidationDecision,
) -> Unit {
buffer.write_string("{\"status\":")
buffer.write_string(report_json_string(validity_label(decision.validity)))
buffer.write_string(",\"coveringVrps\":[")
for index, evidence in decision.matches {
if index > 0 {
buffer.write_char(',')
}
buffer.write_string("{\"asn\":")
buffer.write_string(evidence.vrp.asn().to_string())
buffer.write_string(",\"prefix\":")
buffer.write_string(report_json_string(evidence.vrp.prefix().to_string()))
buffer.write_string(",\"maxLength\":")
buffer.write_string(evidence.vrp.max_length().to_string())
buffer.write_string(",\"relation\":")
buffer.write_string(report_json_string(relation_label(evidence.relation)))
buffer.write_char('}')
}
buffer.write_string("]}")
}
///|
fn write_ipv6_assessment_json(
buffer : StringBuilder,
diff : Ipv6SnapshotDiff,
impact : Ipv6SnapshotImpact,
) -> Unit {
let mut newly_invalid = 0
let mut valid_losses = 0
for change in impact.changed {
if change.previous.validity == Valid {
valid_losses = valid_losses + 1
}
if change.current.validity == Invalid {
newly_invalid = newly_invalid + 1
}
}
buffer.write_string(",\"ipv6Snapshot\":{\"added\":")
buffer.write_string(diff.added.length().to_string())
buffer.write_string(",\"removed\":")
buffer.write_string(diff.removed.length().to_string())
buffer.write_string(",\"unchanged\":")
buffer.write_string(diff.unchanged.to_string())
buffer.write_string(",\"addedRecords\":[")
for index, record in diff.added {
if index > 0 {
buffer.write_char(',')
}
write_ipv6_vrp_record_json(buffer, record)
}
buffer.write_string("],\"removedRecords\":[")
for index, record in diff.removed {
if index > 0 {
buffer.write_char(',')
}
write_ipv6_vrp_record_json(buffer, record)
}
buffer.write_string("]},\"ipv6RouteImpact\":{\"checked\":")
buffer.write_string((impact.changed.length() + impact.unchanged).to_string())
buffer.write_string(",\"changed\":")
buffer.write_string(impact.changed.length().to_string())
buffer.write_string(",\"unchanged\":")
buffer.write_string(impact.unchanged.to_string())
buffer.write_string(",\"newlyInvalid\":")
buffer.write_string(newly_invalid.to_string())
buffer.write_string(",\"validLosses\":")
buffer.write_string(valid_losses.to_string())
buffer.write_string(",\"changes\":[")
for index, change in impact.changed {
if index > 0 {
buffer.write_char(',')
}
buffer.write_string("{\"route\":{\"asn\":")
buffer.write_string(change.current.route.origin_asn().to_string())
buffer.write_string(",\"prefix\":")
buffer.write_string(
report_json_string(change.current.route.prefix().to_string()),
)
buffer.write_string("},\"before\":")
write_ipv6_decision_json(buffer, change.previous)
buffer.write_string(",\"after\":")
write_ipv6_decision_json(buffer, change.current)
buffer.write_char('}')
}
buffer.write_string("]}")
}
///|
/// Serialize a snapshot comparison and its route impact as deterministic JSON.
///
/// Only routes whose validity state changes appear in the changes array; the
/// total number of other checked routes appears in unchanged. Covering VRPs
/// explain each old and new decision. Exact added and removed VRP records
/// retain their trust-anchor labels. Per-decision covering VRPs do not carry
/// labels because the validation decision model does not retain their source.
/// IPv4 and IPv6 SLURM application statistics occupy separate optional fields.
/// Each route-impact section counts transitions away from Valid as validLosses.
pub fn write_assessment_json(
diff : SnapshotDiff,
impact : SnapshotImpact,
slurm? : (SlurmApplyResult?, SlurmApplyResult?),
ipv6_slurm? : (Ipv6SlurmApplyResult?, Ipv6SlurmApplyResult?),
ipv6? : (Ipv6SnapshotDiff, Ipv6SnapshotImpact),
) -> String {
let buffer = StringBuilder()
let mut newly_invalid = 0
let mut valid_losses = 0
for change in impact.changed {
if change.previous.validity == Valid {
valid_losses = valid_losses + 1
}
if change.current.validity == Invalid {
newly_invalid = newly_invalid + 1
}
}
buffer.write_string("{\"schemaVersion\":1")
match slurm {
Some((before, after)) => {
buffer.write_string(",\"slurm\":{\"before\":")
write_optional_slurm_apply_json(buffer, before)
buffer.write_string(",\"after\":")
write_optional_slurm_apply_json(buffer, after)
buffer.write_char('}')
}
None => ()
}
match ipv6_slurm {
Some((before, after)) => {
buffer.write_string(",\"ipv6Slurm\":{\"before\":")
write_optional_ipv6_slurm_apply_json(buffer, before)
buffer.write_string(",\"after\":")
write_optional_ipv6_slurm_apply_json(buffer, after)
buffer.write_char('}')
}
None => ()
}
buffer.write_string(",\"snapshot\":{\"added\":")
buffer.write_string(diff.added.length().to_string())
buffer.write_string(",\"removed\":")
buffer.write_string(diff.removed.length().to_string())
buffer.write_string(",\"unchanged\":")
buffer.write_string(diff.unchanged.to_string())
buffer.write_string(",\"addedRecords\":[")
for index, record in diff.added {
if index > 0 {
buffer.write_char(',')
}
write_vrp_record_json(buffer, record)
}
buffer.write_string("],\"removedRecords\":[")
for index, record in diff.removed {
if index > 0 {
buffer.write_char(',')
}
write_vrp_record_json(buffer, record)
}
buffer.write_string("]},\"routeImpact\":{\"checked\":")
buffer.write_string((impact.changed.length() + impact.unchanged).to_string())
buffer.write_string(",\"changed\":")
buffer.write_string(impact.changed.length().to_string())
buffer.write_string(",\"unchanged\":")
buffer.write_string(impact.unchanged.to_string())
buffer.write_string(",\"newlyInvalid\":")
buffer.write_string(newly_invalid.to_string())
buffer.write_string(",\"validLosses\":")
buffer.write_string(valid_losses.to_string())
buffer.write_string(",\"changes\":[")
for index, change in impact.changed {
if index > 0 {
buffer.write_char(',')
}
buffer.write_string("{\"route\":{\"asn\":")
buffer.write_string(change.current.route.origin_asn().to_string())
buffer.write_string(",\"prefix\":")
buffer.write_string(
report_json_string(change.current.route.prefix().to_string()),
)
buffer.write_string("},\"before\":")
write_decision_json(buffer, change.previous)
buffer.write_string(",\"after\":")
write_decision_json(buffer, change.current)
buffer.write_char('}')
}
buffer.write_string("]}")
match ipv6 {
Some((ipv6_diff, ipv6_impact)) =>
write_ipv6_assessment_json(buffer, ipv6_diff, ipv6_impact)
None => ()
}
buffer.write_char('}')
buffer.to_string()
}