sim_incremental_core/projection/
assay_repair.rs1use std::collections::BTreeSet;
2
3use sim_kernel::{ContentId, Datum, Symbol};
4
5use super::{
6 ConclusionId, FactId,
7 assay::{AssayContract, AssayError},
8};
9
10#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
12pub enum ProjectionRepairItem {
13 Declaration(ConclusionId),
15 GraphEdge {
17 conclusion: ConclusionId,
19 fact: FactId,
21 },
22 Contract(AssayContract),
24}
25
26#[derive(Clone, Debug, Eq, PartialEq)]
28pub struct ProjectionRepairSet {
29 pub delta: String,
31 pub items: BTreeSet<ProjectionRepairItem>,
33 id: ContentId,
34}
35
36impl ProjectionRepairSet {
37 pub(super) fn new(
38 delta: &str,
39 items: BTreeSet<ProjectionRepairItem>,
40 ) -> Result<Self, AssayError> {
41 let datum = Datum::Node {
42 tag: Symbol::qualified("projection", "repair-set-v1"),
43 fields: vec![
44 (Symbol::new("delta"), Datum::String(delta.to_owned())),
45 (
46 Symbol::new("items"),
47 Datum::Vector(items.iter().map(repair_item_datum).collect()),
48 ),
49 ],
50 };
51 let id = datum
52 .content_id()
53 .map_err(|error| AssayError::Canonical(error.to_string()))?;
54 Ok(Self {
55 delta: delta.to_owned(),
56 items,
57 id,
58 })
59 }
60
61 #[must_use]
63 pub fn id(&self) -> &ContentId {
64 &self.id
65 }
66}
67
68#[derive(Clone, Debug, Eq, PartialEq)]
70pub struct ArchitectureFaultReview {
71 pub finding: ContentId,
73 pub epochs: u8,
75}
76
77#[derive(Clone, Debug, Eq, PartialEq)]
79pub enum RepairDisposition {
80 RepairEpoch(u8),
82 NeedDirection(ArchitectureFaultReview),
84}
85
86#[derive(Clone, Debug, Default)]
88pub struct ProjectionRepairTracker {
89 finding: Option<ContentId>,
90 epochs: u8,
91}
92
93impl ProjectionRepairTracker {
94 #[must_use]
96 pub fn record(&mut self, repairs: &ProjectionRepairSet) -> RepairDisposition {
97 if self.finding.as_ref() == Some(repairs.id()) {
98 self.epochs = self.epochs.saturating_add(1).min(3);
99 } else {
100 self.finding = Some(repairs.id().clone());
101 self.epochs = 1;
102 }
103 if self.epochs >= 3 {
104 RepairDisposition::NeedDirection(ArchitectureFaultReview {
105 finding: repairs.id().clone(),
106 epochs: self.epochs,
107 })
108 } else {
109 RepairDisposition::RepairEpoch(self.epochs)
110 }
111 }
112
113 pub fn clear(&mut self) {
115 self.finding = None;
116 self.epochs = 0;
117 }
118}
119
120fn repair_item_datum(item: &ProjectionRepairItem) -> Datum {
121 match item {
122 ProjectionRepairItem::Declaration(conclusion) => Datum::Node {
123 tag: Symbol::qualified("projection", "repair-declaration-v1"),
124 fields: vec![(
125 Symbol::new("conclusion"),
126 Datum::String(conclusion.as_str().to_owned()),
127 )],
128 },
129 ProjectionRepairItem::GraphEdge { conclusion, fact } => Datum::Node {
130 tag: Symbol::qualified("projection", "repair-edge-v1"),
131 fields: vec![
132 (
133 Symbol::new("conclusion"),
134 Datum::String(conclusion.as_str().to_owned()),
135 ),
136 (Symbol::new("fact"), Datum::String(fact.as_str().to_owned())),
137 ],
138 },
139 ProjectionRepairItem::Contract(contract) => Datum::Node {
140 tag: Symbol::qualified("projection", "repair-contract-v1"),
141 fields: vec![(
142 Symbol::new("name"),
143 Datum::String(assay_contract_name(*contract).into()),
144 )],
145 },
146 }
147}
148
149const fn assay_contract_name(contract: AssayContract) -> &'static str {
150 match contract {
151 AssayContract::DeltaClass => "delta-class",
152 AssayContract::UnknownChangedFact => "unknown-changed-fact",
153 AssayContract::Denominator => "denominator",
154 AssayContract::AffectedCeiling => "affected-ceiling",
155 AssayContract::RevisionDelta => "revision-delta",
156 AssayContract::CarriedState => "carried-state",
157 AssayContract::D1UnaffectedFloor => "d1-unaffected-floor",
158 AssayContract::D2UnaffectedFloor => "d2-unaffected-floor",
159 AssayContract::D4UnaffectedFloor => "d4-unaffected-floor",
160 AssayContract::D6CarriedState => "d6-carried-state",
161 AssayContract::SemanticNoOpWork => "semantic-no-op-work",
162 }
163}