use super::{
MemoryProjection,
state::{Retraction, RetractionHistory},
};
use crate::{
domain::Evidence,
error::{MemoryError, Result},
id::FactId,
time::Timestamp,
};
impl MemoryProjection {
pub(crate) fn known_retraction(
&self,
fact: &FactId,
known_at: Timestamp,
) -> Option<&Retraction> {
self.retractions.get(fact).and_then(|history| {
history
.iter()
.filter(|change| change.recorded_at <= known_at)
.max_by_key(|change| (change.recorded_at, change.position))
})
}
pub(super) fn apply_retraction(
&mut self,
recorded_at: Timestamp,
position: u64,
fact_id: &FactId,
valid_until: Timestamp,
evidence: &[Evidence],
) -> Result<()> {
let fact = self.fact(fact_id).ok_or_else(|| MemoryError::MissingFact {
id: fact_id.to_string(),
})?;
if valid_until <= fact.valid_from {
return Err(MemoryError::InvalidValue {
field: "retraction.valid_until",
reason: "must be later than the fact valid_from",
});
}
if evidence.is_empty() {
return Err(MemoryError::InvalidValue {
field: "retraction.evidence",
reason: "at least one evidence item is required",
});
}
evidence.iter().try_for_each(Evidence::validate)?;
self.retractions
.entry(fact_id.clone())
.or_default()
.push(Retraction {
valid_until,
recorded_at,
position,
evidence: evidence.to_vec(),
});
Ok(())
}
}
#[derive(serde::Deserialize)]
#[serde(untagged)]
pub(super) enum RetractionField {
One(Retraction),
Many(RetractionHistory),
}
impl RetractionField {
pub(super) fn into_history(self) -> RetractionHistory {
match self {
Self::One(change) => vec![change],
Self::Many(changes) => changes,
}
}
}