use super::action::Action;
use super::precondition::Precondition;
use super::trace::{Trace, TraceEntry};
use crate::logic::proof::{Counterexample, Verdict};
#[derive(Debug)]
pub enum EngineError<A: Action> {
Violated {
engine: Engine<A>,
violations: Vec<Box<dyn Counterexample>>,
},
LogicalError {
engine: Engine<A>,
counterexample: Box<dyn Counterexample>,
},
}
#[allow(clippy::type_complexity)]
pub struct Engine<A: Action> {
situation: A::Sit,
past: Vec<A::Sit>,
future: Vec<A::Sit>,
preconditions: Vec<Box<dyn Precondition<A>>>,
apply_fn: Box<dyn Fn(&A::Sit, &A) -> Result<A::Sit, Box<dyn Counterexample>>>,
trace: Trace<A>,
}
impl<A: Action> core::fmt::Debug for Engine<A> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Engine")
.field("situation", &self.situation)
.field("step", &self.step())
.field("back_depth", &self.back_depth())
.field("forward_depth", &self.forward_depth())
.field("trace_entries", &self.trace.entries().len())
.finish()
}
}
impl<A: Action> Engine<A> {
pub fn new(
situation: A::Sit,
preconditions: Vec<Box<dyn Precondition<A>>>,
apply_fn: impl Fn(&A::Sit, &A) -> Result<A::Sit, Box<dyn Counterexample>> + 'static,
) -> Self {
Self {
situation,
past: Vec::new(),
future: Vec::new(),
preconditions,
apply_fn: Box::new(apply_fn),
trace: Trace::new(),
}
}
pub fn step(&self) -> usize {
self.past.len()
}
pub fn situation(&self) -> &A::Sit {
&self.situation
}
pub fn trace(&self) -> &Trace<A> {
&self.trace
}
#[allow(clippy::result_large_err)]
pub fn next(mut self, action: A) -> Result<Self, EngineError<A>> {
let situation_before = self.situation.clone();
let step = self.step();
let verdicts: Vec<Verdict> = self
.preconditions
.iter()
.map(|p| p.check(&self.situation, &action))
.collect();
let any_violation = verdicts.iter().any(|v| v.is_err());
if any_violation {
let rechecked: Vec<Verdict> = self
.preconditions
.iter()
.map(|p| p.check(&self.situation, &action))
.collect();
let violations: Vec<Box<dyn Counterexample>> =
rechecked.into_iter().filter_map(|v| v.err()).collect();
self.trace.record(TraceEntry {
step,
situation_before,
action,
precondition_verdicts: verdicts,
situation_after: None,
});
return Err(EngineError::Violated {
engine: self,
violations,
});
}
match (self.apply_fn)(&self.situation, &action) {
Ok(new_situation) => {
self.trace.record(TraceEntry {
step,
situation_before,
action,
precondition_verdicts: verdicts,
situation_after: Some(new_situation.clone()),
});
self.past.push(self.situation.clone());
self.future.clear();
self.situation = new_situation;
Ok(self)
}
Err(counterexample) => {
self.trace.record(TraceEntry {
step,
situation_before,
action,
precondition_verdicts: verdicts,
situation_after: None,
});
Err(EngineError::LogicalError {
engine: self,
counterexample,
})
}
}
}
pub fn back(mut self) -> Result<Self, Self> {
match self.past.pop() {
Some(previous) => {
self.future.push(self.situation.clone());
self.situation = previous;
Ok(self)
}
None => Err(self),
}
}
pub fn forward(mut self) -> Result<Self, Self> {
match self.future.pop() {
Some(next) => {
self.past.push(self.situation.clone());
self.situation = next;
Ok(self)
}
None => Err(self),
}
}
pub fn back_depth(&self) -> usize {
self.past.len()
}
pub fn forward_depth(&self) -> usize {
self.future.len()
}
}