use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ObservabilityStatus {
EvidenceObserved,
ProbedNoEvidence,
BlockedBeforeOracleLayer,
PartiallyBlocked,
Underpowered,
SurfaceMismatch,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BlockFamily {
Authorization,
Method,
Parser,
TechniqueLocal,
Surface,
}
impl std::fmt::Display for ObservabilityStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::EvidenceObserved => write!(f, "EvidenceObserved"),
Self::ProbedNoEvidence => write!(f, "ProbedNoEvidence"),
Self::BlockedBeforeOracleLayer => write!(f, "BlockedBeforeOracleLayer"),
Self::PartiallyBlocked => write!(f, "PartiallyBlocked"),
Self::Underpowered => write!(f, "Underpowered"),
Self::SurfaceMismatch => write!(f, "SurfaceMismatch"),
}
}
}
impl std::fmt::Display for BlockFamily {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Authorization => write!(f, "Authorization"),
Self::Method => write!(f, "Method"),
Self::Parser => write!(f, "Parser"),
Self::TechniqueLocal => write!(f, "TechniqueLocal"),
Self::Surface => write!(f, "Surface"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockSummary {
pub expected_observation_opportunities: usize,
pub blocked_before_oracle_layer: usize,
pub blocked_fraction: f64,
pub dominant_block_family: BlockFamily,
pub dominant_block_reasons: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub operator_action: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn observability_status_display() {
assert_eq!(
ObservabilityStatus::EvidenceObserved.to_string(),
"EvidenceObserved"
);
assert_eq!(
ObservabilityStatus::ProbedNoEvidence.to_string(),
"ProbedNoEvidence"
);
assert_eq!(
ObservabilityStatus::BlockedBeforeOracleLayer.to_string(),
"BlockedBeforeOracleLayer"
);
assert_eq!(
ObservabilityStatus::PartiallyBlocked.to_string(),
"PartiallyBlocked"
);
assert_eq!(
ObservabilityStatus::Underpowered.to_string(),
"Underpowered"
);
assert_eq!(
ObservabilityStatus::SurfaceMismatch.to_string(),
"SurfaceMismatch"
);
}
#[test]
fn block_family_display() {
assert_eq!(BlockFamily::Authorization.to_string(), "Authorization");
assert_eq!(BlockFamily::Method.to_string(), "Method");
assert_eq!(BlockFamily::Parser.to_string(), "Parser");
assert_eq!(BlockFamily::TechniqueLocal.to_string(), "TechniqueLocal");
assert_eq!(BlockFamily::Surface.to_string(), "Surface");
}
#[test]
fn observability_status_roundtrip() {
for status in [
ObservabilityStatus::EvidenceObserved,
ObservabilityStatus::ProbedNoEvidence,
ObservabilityStatus::BlockedBeforeOracleLayer,
ObservabilityStatus::PartiallyBlocked,
ObservabilityStatus::Underpowered,
ObservabilityStatus::SurfaceMismatch,
] {
let json = serde_json::to_string(&status).expect("serialize");
let back: ObservabilityStatus = serde_json::from_str(&json).expect("deserialize");
assert_eq!(status, back);
}
}
#[test]
fn block_family_roundtrip() {
for family in [
BlockFamily::Authorization,
BlockFamily::Method,
BlockFamily::Parser,
BlockFamily::TechniqueLocal,
BlockFamily::Surface,
] {
let json = serde_json::to_string(&family).expect("serialize");
let back: BlockFamily = serde_json::from_str(&json).expect("deserialize");
assert_eq!(family, back);
}
}
}