use std::collections::BTreeSet;
use sim_kernel::{ContentId, Datum, Symbol};
use super::{
ConclusionId, FactId,
assay::{AssayContract, AssayError},
};
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum ProjectionRepairItem {
Declaration(ConclusionId),
GraphEdge {
conclusion: ConclusionId,
fact: FactId,
},
Contract(AssayContract),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProjectionRepairSet {
pub delta: String,
pub items: BTreeSet<ProjectionRepairItem>,
id: ContentId,
}
impl ProjectionRepairSet {
pub(super) fn new(
delta: &str,
items: BTreeSet<ProjectionRepairItem>,
) -> Result<Self, AssayError> {
let datum = Datum::Node {
tag: Symbol::qualified("projection", "repair-set-v1"),
fields: vec![
(Symbol::new("delta"), Datum::String(delta.to_owned())),
(
Symbol::new("items"),
Datum::Vector(items.iter().map(repair_item_datum).collect()),
),
],
};
let id = datum
.content_id()
.map_err(|error| AssayError::Canonical(error.to_string()))?;
Ok(Self {
delta: delta.to_owned(),
items,
id,
})
}
#[must_use]
pub fn id(&self) -> &ContentId {
&self.id
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ArchitectureFaultReview {
pub finding: ContentId,
pub epochs: u8,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RepairDisposition {
RepairEpoch(u8),
NeedDirection(ArchitectureFaultReview),
}
#[derive(Clone, Debug, Default)]
pub struct ProjectionRepairTracker {
finding: Option<ContentId>,
epochs: u8,
}
impl ProjectionRepairTracker {
#[must_use]
pub fn record(&mut self, repairs: &ProjectionRepairSet) -> RepairDisposition {
if self.finding.as_ref() == Some(repairs.id()) {
self.epochs = self.epochs.saturating_add(1).min(3);
} else {
self.finding = Some(repairs.id().clone());
self.epochs = 1;
}
if self.epochs >= 3 {
RepairDisposition::NeedDirection(ArchitectureFaultReview {
finding: repairs.id().clone(),
epochs: self.epochs,
})
} else {
RepairDisposition::RepairEpoch(self.epochs)
}
}
pub fn clear(&mut self) {
self.finding = None;
self.epochs = 0;
}
}
fn repair_item_datum(item: &ProjectionRepairItem) -> Datum {
match item {
ProjectionRepairItem::Declaration(conclusion) => Datum::Node {
tag: Symbol::qualified("projection", "repair-declaration-v1"),
fields: vec![(
Symbol::new("conclusion"),
Datum::String(conclusion.as_str().to_owned()),
)],
},
ProjectionRepairItem::GraphEdge { conclusion, fact } => Datum::Node {
tag: Symbol::qualified("projection", "repair-edge-v1"),
fields: vec![
(
Symbol::new("conclusion"),
Datum::String(conclusion.as_str().to_owned()),
),
(Symbol::new("fact"), Datum::String(fact.as_str().to_owned())),
],
},
ProjectionRepairItem::Contract(contract) => Datum::Node {
tag: Symbol::qualified("projection", "repair-contract-v1"),
fields: vec![(
Symbol::new("name"),
Datum::String(assay_contract_name(*contract).into()),
)],
},
}
}
const fn assay_contract_name(contract: AssayContract) -> &'static str {
match contract {
AssayContract::DeltaClass => "delta-class",
AssayContract::UnknownChangedFact => "unknown-changed-fact",
AssayContract::Denominator => "denominator",
AssayContract::AffectedCeiling => "affected-ceiling",
AssayContract::RevisionDelta => "revision-delta",
AssayContract::CarriedState => "carried-state",
AssayContract::D1UnaffectedFloor => "d1-unaffected-floor",
AssayContract::D2UnaffectedFloor => "d2-unaffected-floor",
AssayContract::D4UnaffectedFloor => "d4-unaffected-floor",
AssayContract::D6CarriedState => "d6-carried-state",
AssayContract::SemanticNoOpWork => "semantic-no-op-work",
}
}