use crate::persistence::{ReadScenarioError, WriteScenarioError};
use crate::{Event, Queue, Scenario, TransitionError};
use thiserror::Error;
use crate::event::process_insertions;
#[derive(Debug)]
pub struct Simulation<S> {
scenario: Scenario<S>,
current_state: S,
cursor: usize,
}
impl<S: Default + Clone> Default for Simulation<S> {
fn default() -> Self {
Simulation::from(Scenario::default())
}
}
impl<S> Simulation<S> {
pub fn step(&mut self) -> Result<(), SimulationError<S>> {
if self.cursor == self.scenario.timeline.len() {
return Err(SimulationError::TimelineExhausted);
}
let event = &self.scenario.timeline[self.cursor];
let mut queue = Queue::new(self.cursor + 1, &self.scenario.timeline);
event.apply(&mut self.current_state, &mut queue)?;
let (offset, _, insertions) = queue.into_inner();
process_insertions(offset, insertions, &mut self.scenario.timeline);
self.cursor += 1;
Ok(())
}
pub fn reset(&mut self)
where
S: Clone,
{
self.current_state = self.scenario.initial.clone();
self.cursor = 0;
}
pub fn jump(&mut self, location: usize) -> Result<(), SimulationError<S>>
where
S: Clone,
{
if location > self.scenario.timeline.len() {
return Err(SimulationError::TimelineExhausted);
}
if location < self.cursor {
self.reset();
}
while self.cursor < location {
self.step()?;
}
Ok(())
}
pub fn run(&mut self) -> Result<(), SimulationError<S>> {
while self.cursor < self.scenario.timeline.len() {
self.step()?;
}
Ok(())
}
pub fn push_event(&mut self, event: Box<dyn Event<State = S>>) -> Result<(), SimulationError<S>> {
if self.cursor != self.scenario.timeline.len() {
return Err(SimulationError::TruncationRequired(event));
}
self.scenario.timeline.push(event);
Ok(())
}
pub fn truncate(&mut self) {
self.scenario.timeline.truncate(self.cursor);
}
pub fn scenario(&self) -> &Scenario<S> {
&self.scenario
}
pub fn set_scenario(&mut self, scenario: Scenario<S>)
where
S: Clone,
{
self.scenario = scenario;
self.reset();
}
pub fn current_state(&self) -> &S {
&self.current_state
}
pub fn cursor(&self) -> usize {
self.cursor
}
}
impl<S: Clone> From<Scenario<S>> for Simulation<S> {
fn from(scenario: Scenario<S>) -> Self {
let current_state = scenario.initial.clone();
Self {
scenario,
current_state,
cursor: 0,
}
}
}
#[derive(Debug, Error)]
pub enum SimulationError<S> {
#[error("timeline exhausted")]
TimelineExhausted,
#[error("transition: {0}")]
Transition(#[from] TransitionError),
#[error("truncation required")]
TruncationRequired(Box<dyn Event<State = S>>),
#[error("read scenario: {0}")]
ReadScenario(#[from] ReadScenarioError),
#[error("write scenario: {0}")]
WriteScenario(#[from] WriteScenarioError),
}
impl<S> SimulationError<S> {
pub fn is_timeline_exhausted(&self) -> bool {
matches!(self, Self::TimelineExhausted)
}
pub fn transition(self) -> Option<TransitionError> {
match self {
SimulationError::Transition(err) => Some(err),
_ => None,
}
}
pub fn truncation_required(self) -> Option<Box<dyn Event<State = S>>> {
match self {
SimulationError::TruncationRequired(err) => Some(err),
_ => None,
}
}
pub fn read_scenario(self) -> Option<ReadScenarioError> {
match self {
SimulationError::ReadScenario(err) => Some(err),
_ => None,
}
}
pub fn write_scenario(self) -> Option<WriteScenarioError> {
match self {
SimulationError::WriteScenario(err) => Some(err),
_ => None,
}
}
}
#[cfg(test)]
mod tests;