parlov_core/
observability.rs1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12pub enum ObservabilityStatus {
13 EvidenceObserved,
15 ProbedNoEvidence,
17 BlockedBeforeOracleLayer,
20 PartiallyBlocked,
22 Underpowered,
24 SurfaceMismatch,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
31pub enum BlockFamily {
32 Authorization,
34 Method,
36 Parser,
38 TechniqueLocal,
40 Surface,
42}
43
44impl std::fmt::Display for ObservabilityStatus {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match self {
47 Self::EvidenceObserved => write!(f, "EvidenceObserved"),
48 Self::ProbedNoEvidence => write!(f, "ProbedNoEvidence"),
49 Self::BlockedBeforeOracleLayer => write!(f, "BlockedBeforeOracleLayer"),
50 Self::PartiallyBlocked => write!(f, "PartiallyBlocked"),
51 Self::Underpowered => write!(f, "Underpowered"),
52 Self::SurfaceMismatch => write!(f, "SurfaceMismatch"),
53 }
54 }
55}
56
57impl std::fmt::Display for BlockFamily {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 match self {
60 Self::Authorization => write!(f, "Authorization"),
61 Self::Method => write!(f, "Method"),
62 Self::Parser => write!(f, "Parser"),
63 Self::TechniqueLocal => write!(f, "TechniqueLocal"),
64 Self::Surface => write!(f, "Surface"),
65 }
66 }
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct BlockSummary {
72 pub expected_observation_opportunities: usize,
74 pub blocked_before_oracle_layer: usize,
76 pub blocked_fraction: f64,
78 pub dominant_block_family: BlockFamily,
80 pub dominant_block_reasons: Vec<String>,
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub operator_action: Option<String>,
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn observability_status_display() {
93 assert_eq!(
94 ObservabilityStatus::EvidenceObserved.to_string(),
95 "EvidenceObserved"
96 );
97 assert_eq!(
98 ObservabilityStatus::ProbedNoEvidence.to_string(),
99 "ProbedNoEvidence"
100 );
101 assert_eq!(
102 ObservabilityStatus::BlockedBeforeOracleLayer.to_string(),
103 "BlockedBeforeOracleLayer"
104 );
105 assert_eq!(
106 ObservabilityStatus::PartiallyBlocked.to_string(),
107 "PartiallyBlocked"
108 );
109 assert_eq!(
110 ObservabilityStatus::Underpowered.to_string(),
111 "Underpowered"
112 );
113 assert_eq!(
114 ObservabilityStatus::SurfaceMismatch.to_string(),
115 "SurfaceMismatch"
116 );
117 }
118
119 #[test]
120 fn block_family_display() {
121 assert_eq!(BlockFamily::Authorization.to_string(), "Authorization");
122 assert_eq!(BlockFamily::Method.to_string(), "Method");
123 assert_eq!(BlockFamily::Parser.to_string(), "Parser");
124 assert_eq!(BlockFamily::TechniqueLocal.to_string(), "TechniqueLocal");
125 assert_eq!(BlockFamily::Surface.to_string(), "Surface");
126 }
127
128 #[test]
129 fn observability_status_roundtrip() {
130 for status in [
131 ObservabilityStatus::EvidenceObserved,
132 ObservabilityStatus::ProbedNoEvidence,
133 ObservabilityStatus::BlockedBeforeOracleLayer,
134 ObservabilityStatus::PartiallyBlocked,
135 ObservabilityStatus::Underpowered,
136 ObservabilityStatus::SurfaceMismatch,
137 ] {
138 let json = serde_json::to_string(&status).expect("serialize");
139 let back: ObservabilityStatus = serde_json::from_str(&json).expect("deserialize");
140 assert_eq!(status, back);
141 }
142 }
143
144 #[test]
145 fn block_family_roundtrip() {
146 for family in [
147 BlockFamily::Authorization,
148 BlockFamily::Method,
149 BlockFamily::Parser,
150 BlockFamily::TechniqueLocal,
151 BlockFamily::Surface,
152 ] {
153 let json = serde_json::to_string(&family).expect("serialize");
154 let back: BlockFamily = serde_json::from_str(&json).expect("deserialize");
155 assert_eq!(family, back);
156 }
157 }
158}