use std::collections::BTreeMap;
use openquiver_compile::CompiledPlan;
use openquiver_graph::{NodeName, PinRef};
use openquiver_store::Event;
use crate::journal::Record;
use crate::{RunValue, RuntimeError};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum Frontier {
End,
At(NodeName),
InDoubt(NodeName),
}
pub(crate) struct Replayed {
pub values: BTreeMap<PinRef, RunValue>,
pub frontier: Frontier,
pub finished: bool,
}
pub(crate) fn replay(
events: &[Event],
plan: &CompiledPlan,
entry: &NodeName,
) -> Result<Replayed, RuntimeError> {
let mut values = BTreeMap::new();
let mut frontier = Frontier::End;
let mut started = false;
let mut finished = false;
for event in events {
if finished {
return Err(RuntimeError::BadJournal("it continues after the run ended"));
}
match Record::decode(&event.kind, &event.payload)? {
Record::Started(_) if started => {
return Err(RuntimeError::BadJournal("it starts twice"));
}
Record::Started(start) => {
if start.plan != plan.fingerprint() {
return Err(RuntimeError::DifferentPlan);
}
started = true;
frontier = Frontier::At(entry.clone());
}
_ if !started => {
return Err(RuntimeError::BadJournal(
"it does not begin with the start of a run",
));
}
Record::Entered(entered) => frontier = Frontier::InDoubt(entered.node),
Record::Exited(exited) => {
let node = plan
.node(exited.node.as_str())
.ok_or_else(|| RuntimeError::NotInPlan(exited.node.as_str().to_owned()))?;
for (pin, value) in exited.outputs {
values.insert(PinRef::new(exited.node.clone(), pin), value);
}
frontier = match exited.exec.and_then(|pin| node.exec_next.get(&pin)) {
Some(next) => Frontier::At(next.clone()),
None => Frontier::End,
};
}
Record::Printed(_) => {}
Record::Finished => {
finished = true;
frontier = Frontier::End;
}
}
}
if !started {
return Err(RuntimeError::BadJournal(
"it does not begin with the start of a run",
));
}
Ok(Replayed {
values,
frontier,
finished,
})
}