use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::ops::Deref;
use std::str::FromStr;
use thiserror::Error;
pub struct Queue<'a, S> {
offset: usize,
timeline: &'a Vec<Box<dyn Event<State = S>>>,
insertions: Vec<(usize, Box<dyn Event<State = S>>)>
}
impl<'a, S> Queue<'a, S> {
pub fn new(offset: usize, timeline: &'a Vec<Box<dyn Event<State = S>>>) -> Self {
assert!(offset >= 1, "offset ({offset}) cannot be less than 1");
assert!(offset <= timeline.len(), "offset ({offset}) cannot exceed length of timeline {}", timeline.len());
Self {
offset,
timeline,
insertions: Vec::default()
}
}
pub fn insert_later(&mut self, index: usize, event: Box<dyn Event<State = S>>) {
let lim = self.timeline.len() + self.insertions.len() - self.offset;
assert!(index <= lim, "insertion index ({index}) cannot exceed length of queue ({lim})");
self.insertions.push((index, event));
}
pub fn push_later(&mut self, event: Box<dyn Event<State = S>>) {
self.insert_later(self.timeline.len() + self.insertions.len() - self.offset, event);
}
pub fn past(&self) -> &[Box<dyn Event<State = S>>] {
&self.timeline[..self.offset - 1]
}
pub fn future(&self) -> &[Box<dyn Event<State = S>>] {
&self.timeline[self.offset..]
}
#[allow(clippy::type_complexity)]
pub fn into_inner(self) -> (usize, &'a Vec<Box<dyn Event<State = S>>>, Vec<(usize, Box<dyn Event<State = S>>)>) {
(self.offset, self.timeline, self.insertions)
}
}
pub(crate) fn process_insertions<S>(offset: usize, insertions: Vec<(usize, Box<dyn Event<State = S>>)>, timeline: &mut Vec<Box<dyn Event<State = S>>>) {
for (index, event) in insertions {
timeline.insert(offset + index, event);
}
}
impl<S> Deref for Queue<'_, S> {
type Target = [Box<dyn Event<State = S>>];
fn deref(&self) -> &Self::Target {
self.future()
}
}
impl<S> Debug for Queue<'_, S> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.deref().fmt(f)
}
}
pub trait StaticNamed {
fn name() -> &'static str;
}
pub trait Named {
fn name(&self) -> Cow<'static, str>;
}
impl<N: StaticNamed> Named for N {
fn name(&self) -> Cow<'static, str> {
<N as StaticNamed>::name().into()
}
}
pub trait Event: Named + Debug + ToString {
type State;
fn apply(&self, state: &mut Self::State, queue: &mut Queue<Self::State>) -> Result<(), TransitionError>;
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("{0}")]
pub struct TransitionError(pub Cow<'static, str>);
#[derive(Debug)]
pub struct Scenario<S> {
pub initial: S,
pub timeline: Vec<Box<dyn Event<State = S>>>,
}
impl<S: Default> Default for Scenario<S> {
fn default() -> Self {
Self {
initial: S::default(),
timeline: Vec::default(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("{0}")]
pub struct ParseEventError(pub Cow<'static, str>);
pub trait NamedEventParser: Named {
type State;
fn parse(&self, s: &str) -> Result<Box<dyn Event<State = Self::State>>, ParseEventError>;
}
pub struct Decoder<S> {
by_name: BTreeMap<String, Box<dyn NamedEventParser<State = S>>>,
}
impl<S> Decoder<S> {
pub fn new(parsers: Vec<Box<dyn NamedEventParser<State = S>>>) -> Self {
parsers.try_into().unwrap()
}
pub fn parsers(&self) -> impl Iterator<Item = &Box<dyn NamedEventParser<State = S>>> {
self.by_name.values()
}
pub fn decode(&self, name: &str, encoded: &str) -> Result<Box<dyn Event<State = S>>, ParseEventError> {
let parser = self
.by_name
.get(name)
.ok_or_else(|| ParseEventError(format!("no event parser for '{name}'").into()))?;
parser.parse(encoded)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("{0}")]
pub struct InvalidEventParserSpec(String);
impl<S> TryFrom<Vec<Box<dyn NamedEventParser<State = S>>>> for Decoder<S> {
type Error = InvalidEventParserSpec;
fn try_from(parsers: Vec<Box<dyn NamedEventParser<State = S>>>) -> Result<Self, Self::Error> {
let mut by_name = BTreeMap::default();
for parser in parsers {
let name = parser.name();
if by_name.insert(name.to_string(), parser).is_some() {
return Err(InvalidEventParserSpec(format!(
"duplicate event parser for '{}'",
name
)));
}
}
Ok(Self { by_name })
}
}
pub struct Parser<E>(PhantomData<E>);
impl<E> Default for Parser<E> {
fn default() -> Self {
Self(PhantomData::default())
}
}
impl<E> StaticNamed for Parser<E>
where
E: StaticNamed,
{
fn name() -> &'static str {
<E as StaticNamed>::name()
}
}
impl<E> NamedEventParser for Parser<E>
where
E: StaticNamed + FromStr + Event + 'static,
ParseEventError: From<<E as FromStr>::Err>,
{
type State = E::State;
fn parse(&self, s: &str) -> Result<Box<dyn Event<State = Self::State>>, ParseEventError> {
Ok(Box::new(E::from_str(s)?))
}
}
#[cfg(test)]
mod tests;