#![allow(
clippy::disallowed_types,
reason = "dev/verification tooling over JSON artifacts (the catalogue, results, wire \
exchanges) — not the application (#1694); the carriers here are cfg(test)-only, so \
#[expect] would be unfulfilled in the non-test build"
)]
use serde::{Deserialize, Serialize};
use crate::ids::{AmbiguityId, CaseId, SmOperationRef};
use crate::vocab::{FormatName, HttpMethod, OutcomeKind};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SurfaceReason {
OffWire,
VariantOf,
Accessor,
CoverageGap,
StatementDeclared,
}
impl SurfaceReason {
pub const ALL: &[SurfaceReason] = &[
SurfaceReason::OffWire,
SurfaceReason::VariantOf,
SurfaceReason::Accessor,
SurfaceReason::CoverageGap,
SurfaceReason::StatementDeclared,
];
#[must_use]
pub fn token(self) -> &'static str {
match self {
SurfaceReason::OffWire => "off_wire",
SurfaceReason::VariantOf => "variant_of",
SurfaceReason::Accessor => "accessor",
SurfaceReason::CoverageGap => "coverage_gap",
SurfaceReason::StatementDeclared => "statement_declared",
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SmOperationException {
pub operation: SmOperationRef,
pub reason: SurfaceReason,
pub source: String,
#[serde(default)]
pub note: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BranchException {
pub binding: SmOperationRef,
#[serde(default)]
pub variant: Option<String>,
#[serde(default)]
pub outcome: Option<OutcomeKind>,
#[serde(default)]
pub format: Option<FormatName>,
pub reason: SurfaceReason,
pub source: String,
#[serde(default)]
pub note: Option<String>,
}
impl BranchException {
pub fn check_invariants(&self) -> Result<(), String> {
match (self.outcome, self.format) {
(Some(_), None) | (None, Some(_)) => Ok(()),
_ => Err(format!(
"branch exception for {} must carry exactly one of outcome | format",
self.binding
)),
}
}
#[must_use]
pub fn scopes(&self, operation: &SmOperationRef, variant: Option<&str>) -> bool {
&self.binding == operation && (self.variant.is_none() || self.variant.as_deref() == variant)
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ElementException {
pub reason: SurfaceReason,
#[serde(default)]
pub register: Option<AmbiguityId>,
#[serde(default)]
pub note: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WireElement {
pub id: String,
pub description: String,
pub source: String,
#[serde(default)]
pub covered_by: Vec<CaseId>,
#[serde(default)]
pub exception: Option<ElementException>,
}
impl WireElement {
pub fn check_invariants(&self) -> Result<(), String> {
match (self.covered_by.is_empty(), &self.exception) {
(false, None) | (true, Some(_)) => Ok(()),
(false, Some(_)) => Err(format!(
"wire-surface element {} carries both covered_by and an exception",
self.id
)),
(true, None) => Err(format!(
"wire-surface element {} has neither a covering case nor an exception",
self.id
)),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ServedExtension {
pub family: String,
pub routes: Vec<String>,
pub config_gate: String,
pub spec_silence: String,
pub never_gates: bool,
}
impl ServedExtension {
#[must_use]
pub fn route_path(route: &str) -> Option<&str> {
let (method, path) = route.split_once(' ')?;
Self::method(method)?;
path.starts_with('/').then_some(path)
}
fn method(token: &str) -> Option<HttpMethod> {
serde_json::from_value::<HttpMethod>(serde_json::Value::String(token.to_owned())).ok()
}
#[must_use]
pub fn check_invariants(&self) -> Vec<String> {
let mut findings = Vec::new();
let label = if self.family.trim().is_empty() {
findings.push("served_extensions entry has an empty family name".to_owned());
"<unnamed>"
} else {
self.family.as_str()
};
if !self.never_gates {
findings.push(format!(
"served extension {label} sets never_gates: false — this axis is a declaration \
and can never carry a coverage obligation"
));
}
if self.routes.is_empty() {
findings.push(format!("served extension {label} declares no routes"));
}
for route in &self.routes {
if Self::route_path(route).is_none() {
findings.push(format!(
"served extension {label} route {route:?} is outside the grammar \
(\"<METHOD> /<path>\", method from the closed HTTP-method vocabulary)"
));
}
}
if self.config_gate.trim().is_empty() {
findings.push(format!("served extension {label} states no config gate"));
}
if self.spec_silence.trim().is_empty() {
findings.push(format!(
"served extension {label} states no spec-silence citation"
));
}
findings
}
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WireSurface {
#[serde(default)]
pub sm_operations: Vec<SmOperationException>,
#[serde(default)]
pub branches: Vec<BranchException>,
#[serde(default)]
pub elements: Vec<WireElement>,
#[serde(default)]
pub served_extensions: Vec<ServedExtension>,
}
impl WireSurface {
#[must_use]
pub fn sm_exception(&self, operation: &SmOperationRef) -> Option<&SmOperationException> {
self.sm_operations
.iter()
.find(|e| &e.operation == operation)
}
#[must_use]
pub fn outcome_exception(
&self,
operation: &SmOperationRef,
variant: Option<&str>,
outcome: OutcomeKind,
) -> Option<&BranchException> {
self.branches
.iter()
.find(|b| b.outcome == Some(outcome) && b.scopes(operation, variant))
}
#[must_use]
pub fn format_exception(
&self,
operation: &SmOperationRef,
variant: Option<&str>,
format: FormatName,
) -> Option<&BranchException> {
self.branches
.iter()
.find(|b| b.format == Some(format) && b.scopes(operation, variant))
}
pub fn check_invariants(&self) -> Result<(), Vec<String>> {
let mut findings = Vec::new();
for branch in &self.branches {
if let Err(message) = branch.check_invariants() {
findings.push(message);
}
}
let mut seen = std::collections::BTreeSet::new();
for element in &self.elements {
if let Err(message) = element.check_invariants() {
findings.push(message);
}
if !seen.insert(element.id.as_str()) {
findings.push(format!(
"wire-surface element id {} is not unique",
element.id
));
}
}
let mut families = std::collections::BTreeSet::new();
let mut routes = std::collections::BTreeSet::new();
for extension in &self.served_extensions {
findings.extend(extension.check_invariants());
if !families.insert(extension.family.as_str()) {
findings.push(format!(
"served_extensions family {} is declared twice",
extension.family
));
}
for route in &extension.routes {
if !routes.insert(route.as_str()) {
findings.push(format!(
"served_extensions route {route:?} is declared by more than one family"
));
}
}
}
if findings.is_empty() {
Ok(())
} else {
Err(findings)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn branch_exactly_one_of_outcome_format() {
let both: BranchException = serde_json::from_value(serde_json::json!({
"binding": "I_EHR_SERVICE.create_ehr",
"outcome": "created", "format": "canonical-xml",
"reason": "coverage_gap", "source": "s"
}))
.unwrap();
assert!(both.check_invariants().is_err());
let one: BranchException = serde_json::from_value(serde_json::json!({
"binding": "I_EHR_SERVICE.create_ehr",
"format": "canonical-xml", "reason": "coverage_gap", "source": "s"
}))
.unwrap();
assert!(one.check_invariants().is_ok());
}
#[test]
fn element_xor_covered_by_exception() {
let neither: WireElement = serde_json::from_value(serde_json::json!({
"id": "x", "description": "d", "source": "s"
}))
.unwrap();
assert!(neither.check_invariants().is_err());
let covered: WireElement = serde_json::from_value(serde_json::json!({
"id": "x", "description": "d", "source": "s", "covered_by": ["CASE-1"]
}))
.unwrap();
assert!(covered.check_invariants().is_ok());
}
fn served(never_gates: bool, routes: &[&str]) -> ServedExtension {
serde_json::from_value(serde_json::json!({
"family": "management",
"routes": routes,
"config_gate": "management.enabled",
"spec_silence": "no released clause governs the URI space beyond the resource set",
"never_gates": never_gates
}))
.unwrap()
}
#[test]
fn served_extension_requires_never_gates_and_a_route_grammar() {
let ok = served(true, &["GET /management/info"]);
assert!(ok.check_invariants().is_empty());
let gating = served(false, &["GET /management/info"]);
assert!(
gating
.check_invariants()
.iter()
.any(|m| m.contains("never_gates")),
"{:?}",
gating.check_invariants()
);
for bad in ["/management/info", "FETCH /management/info", "GET info"] {
let e = served(true, &[bad]);
assert!(
e.check_invariants().iter().any(|m| m.contains("grammar")),
"{bad} should be rejected"
);
}
assert_eq!(
ServedExtension::route_path("DELETE /admin/tenant/{tenant_id}"),
Some("/admin/tenant/{tenant_id}")
);
}
#[test]
fn served_extension_families_and_routes_are_unique() {
let surface: WireSurface = serde_json::from_value(serde_json::json!({
"served_extensions": [
{ "family": "health", "routes": ["GET /health"], "config_gate": "always on",
"spec_silence": "s", "never_gates": true },
{ "family": "health", "routes": ["GET /health"], "config_gate": "always on",
"spec_silence": "s", "never_gates": true }
]
}))
.unwrap();
let findings = surface.check_invariants().unwrap_err();
assert!(findings.iter().any(|m| m.contains("declared twice")));
assert!(findings.iter().any(|m| m.contains("more than one family")));
}
#[test]
fn branch_scopes_variant() {
let ex: BranchException = serde_json::from_value(serde_json::json!({
"binding": "I_EHR_SERVICE.create_ehr", "variant": "with_ehr_id",
"outcome": "already_exists", "reason": "coverage_gap", "source": "s"
}))
.unwrap();
let op = SmOperationRef::parse("I_EHR_SERVICE.create_ehr").unwrap();
assert!(ex.scopes(&op, Some("with_ehr_id")));
assert!(!ex.scopes(&op, None));
assert!(!ex.scopes(&op, Some("other")));
}
}