1use std::fmt;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32#[repr(u8)]
33pub enum ProtocolModel {
34 Failure = 1,
49
50 Timing = 2,
61
62 Trust = 3,
74
75 Event = 4,
86
87 Media = 5,
98}
99
100impl ProtocolModel {
101 pub fn code(&self) -> &'static str {
103 match self {
104 ProtocolModel::Failure => "MODEL-1",
105 ProtocolModel::Timing => "MODEL-2",
106 ProtocolModel::Trust => "MODEL-3",
107 ProtocolModel::Event => "MODEL-4",
108 ProtocolModel::Media => "MODEL-5",
109 }
110 }
111
112 pub fn name(&self) -> &'static str {
114 match self {
115 ProtocolModel::Failure => "Failure Model",
116 ProtocolModel::Timing => "Timing Model",
117 ProtocolModel::Trust => "Trust Model",
118 ProtocolModel::Event => "Event Model",
119 ProtocolModel::Media => "Media Model",
120 }
121 }
122
123 pub fn axiom(&self) -> &'static str {
125 match self {
126 ProtocolModel::Failure => "Failure is distortion, not termination",
127 ProtocolModel::Timing => "Time is local. Order is reconstructed",
128 ProtocolModel::Trust => "Identity is cryptographic, not topological",
129 ProtocolModel::Event => "Event is truth. State is a story we tell about it",
130 ProtocolModel::Media => "Media is perception, not data",
131 }
132 }
133
134 pub fn compliance_question(&self) -> &'static str {
136 match self {
137 ProtocolModel::Failure => "What is your failure model?",
138 ProtocolModel::Timing => "Whose clock do you depend on?",
139 ProtocolModel::Trust => "Where does trust come from?",
140 ProtocolModel::Event => "Where is your event-truth?",
141 ProtocolModel::Media => "If media has holes, what still lives?",
142 }
143 }
144
145 pub fn valid_answer(&self) -> &'static str {
147 match self {
148 ProtocolModel::Failure => "Distortion, not termination",
149 ProtocolModel::Timing => "Local only, reconstructed order",
150 ProtocolModel::Trust => "Cryptographic continuity",
151 ProtocolModel::Event => "In signed, immutable events",
152 ProtocolModel::Media => "Perception continues",
153 }
154 }
155
156 pub fn related_invariant(&self) -> crate::Invariant {
158 match self {
159 ProtocolModel::Failure => crate::Invariant::ExperienceDegradesNeverCollapses,
160 ProtocolModel::Timing => crate::Invariant::RealityNeverWaits,
161 ProtocolModel::Trust => crate::Invariant::IdentitySurvivesTransport,
162 ProtocolModel::Event => crate::Invariant::EventIsTruth,
163 ProtocolModel::Media => crate::Invariant::PresenceOverPackets,
164 }
165 }
166
167 pub fn all() -> &'static [ProtocolModel] {
169 &[
170 ProtocolModel::Failure,
171 ProtocolModel::Timing,
172 ProtocolModel::Trust,
173 ProtocolModel::Event,
174 ProtocolModel::Media,
175 ]
176 }
177}
178
179impl fmt::Display for ProtocolModel {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 write!(f, "{}: {}", self.code(), self.name())
182 }
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
189#[repr(u8)]
190pub enum ReconstructabilityClass {
191 MustFeel = 0,
194
195 MayApproximate = 1,
198
199 Reconstructable = 2,
202
203 Ignorable = 3,
206}
207
208impl ReconstructabilityClass {
209 pub fn priority(&self) -> u8 {
211 *self as u8
212 }
213
214 pub fn droppable(&self) -> bool {
216 matches!(self, Self::Ignorable | Self::Reconstructable)
217 }
218
219 pub fn requires_reconstruction(&self) -> bool {
221 matches!(self, Self::MustFeel | Self::MayApproximate)
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq)]
229pub struct PerceptualWeight {
230 pub emotional: f32,
232
233 pub presence: f32,
235
236 pub continuity: f32,
238
239 pub class: ReconstructabilityClass,
241}
242
243impl PerceptualWeight {
244 pub fn new(
246 emotional: f32,
247 presence: f32,
248 continuity: f32,
249 class: ReconstructabilityClass,
250 ) -> Self {
251 Self {
252 emotional: emotional.clamp(0.0, 1.0),
253 presence: presence.clamp(0.0, 1.0),
254 continuity: continuity.clamp(0.0, 1.0),
255 class,
256 }
257 }
258
259 pub fn critical() -> Self {
261 Self::new(1.0, 1.0, 1.0, ReconstructabilityClass::MustFeel)
262 }
263
264 pub fn important() -> Self {
266 Self::new(0.7, 0.7, 0.7, ReconstructabilityClass::MayApproximate)
267 }
268
269 pub fn normal() -> Self {
271 Self::new(0.5, 0.5, 0.5, ReconstructabilityClass::Reconstructable)
272 }
273
274 pub fn cosmetic() -> Self {
276 Self::new(0.2, 0.2, 0.2, ReconstructabilityClass::Ignorable)
277 }
278
279 pub fn importance(&self) -> f32 {
281 (self.emotional + self.presence + self.continuity) / 3.0
282 }
283}
284
285impl Default for PerceptualWeight {
286 fn default() -> Self {
287 Self::normal()
288 }
289}
290
291#[derive(Debug, Clone)]
293pub struct ModelCompliance {
294 pub model: ProtocolModel,
295 pub compliant: bool,
296 pub answer: String,
297}
298
299pub fn check_model_compliance<F>(mut checker: F) -> Vec<ModelCompliance>
303where
304 F: FnMut(ProtocolModel) -> (bool, String),
305{
306 ProtocolModel::all()
307 .iter()
308 .map(|&model| {
309 let (compliant, answer) = checker(model);
310 ModelCompliance {
311 model,
312 compliant,
313 answer,
314 }
315 })
316 .collect()
317}
318
319pub trait ModelCompliant {
321 fn failure_model(&self) -> &'static str {
323 "Distortion field - no termination"
324 }
325
326 fn timing_model(&self) -> &'static str {
328 "Local time only - order reconstructed"
329 }
330
331 fn trust_model(&self) -> &'static str {
333 "Cryptographic continuity"
334 }
335
336 fn event_model(&self) -> &'static str {
338 "Signed immutable events"
339 }
340
341 fn media_model(&self) -> &'static str {
343 "Perception continues"
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350
351 #[test]
352 fn test_model_codes() {
353 assert_eq!(ProtocolModel::Failure.code(), "MODEL-1");
354 assert_eq!(ProtocolModel::Timing.code(), "MODEL-2");
355 assert_eq!(ProtocolModel::Trust.code(), "MODEL-3");
356 assert_eq!(ProtocolModel::Event.code(), "MODEL-4");
357 assert_eq!(ProtocolModel::Media.code(), "MODEL-5");
358 }
359
360 #[test]
361 fn test_all_models() {
362 assert_eq!(ProtocolModel::all().len(), 5);
363 }
364
365 #[test]
366 fn test_model_invariant_relationship() {
367 use crate::Invariant;
368
369 assert_eq!(
370 ProtocolModel::Failure.related_invariant(),
371 Invariant::ExperienceDegradesNeverCollapses
372 );
373 assert_eq!(
374 ProtocolModel::Timing.related_invariant(),
375 Invariant::RealityNeverWaits
376 );
377 assert_eq!(
378 ProtocolModel::Trust.related_invariant(),
379 Invariant::IdentitySurvivesTransport
380 );
381 assert_eq!(
382 ProtocolModel::Event.related_invariant(),
383 Invariant::EventIsTruth
384 );
385 assert_eq!(
386 ProtocolModel::Media.related_invariant(),
387 Invariant::PresenceOverPackets
388 );
389 }
390
391 #[test]
392 fn test_reconstructability_priority() {
393 assert!(
394 ReconstructabilityClass::MustFeel.priority()
395 < ReconstructabilityClass::Ignorable.priority()
396 );
397 }
398
399 #[test]
400 fn test_perceptual_weight() {
401 let critical = PerceptualWeight::critical();
402 let cosmetic = PerceptualWeight::cosmetic();
403
404 assert!(critical.importance() > cosmetic.importance());
405 assert!(!critical.class.droppable());
406 assert!(cosmetic.class.droppable());
407 }
408
409 #[test]
410 fn test_check_model_compliance() {
411 let results =
412 check_model_compliance(|model| (true, format!("Compliant with {}", model.name())));
413
414 assert_eq!(results.len(), 5);
415 assert!(results.iter().all(|r| r.compliant));
416 }
417
418 #[test]
419 fn test_model_display() {
420 let model = ProtocolModel::Failure;
421 let display = format!("{}", model);
422 assert!(display.contains("MODEL-1"));
423 assert!(display.contains("Failure Model"));
424 }
425}