pub mod yaml;
use crate::{Decoder, ParseEventError, Scenario};
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::fmt::{Debug};
use std::io;
use std::io::{BufRead, Write};
use std::path::Path;
use std::str::FromStr;
use thiserror::Error;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PersistentScenario<S> {
pub initial: S,
pub timeline: Vec<PersistentEvent>,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PersistentEvent {
pub name: String,
pub encoded: String,
}
impl<S: Clone> From<&Scenario<S>> for PersistentScenario<S> {
fn from(scenario: &Scenario<S>) -> Self {
Self {
initial: scenario.initial.clone(),
timeline: scenario
.timeline
.iter()
.map(|event| PersistentEvent {
name: event.name().into(),
encoded: event.to_string(),
})
.collect(),
}
}
}
impl<S> PersistentScenario<S> {
pub fn decode(self, decoder: &Decoder<S>) -> Result<Scenario<S>, ParseEventError> {
let mut timeline = Vec::with_capacity(self.timeline.len());
for event in self.timeline {
let event = decoder.decode(&event.name, &event.encoded)?;
timeline.push(event);
}
Ok(Scenario {
initial: self.initial,
timeline,
})
}
}
trait IntoInner<T> {
fn into_inner(self) -> T;
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("{0}")]
pub struct UnsupportedFileFormatError(String);
#[derive(Debug, Error)]
pub enum WriteScenarioError {
#[error("io: {0}")]
Io(#[from] io::Error),
#[error("unsupported file format: {0}")]
UnsupportedFileFormat(#[from] UnsupportedFileFormatError),
}
impl WriteScenarioError {
pub fn io(self) -> Option<io::Error> {
match self {
WriteScenarioError::Io(err) => Some(err),
WriteScenarioError::UnsupportedFileFormat(_) => None
}
}
pub fn unsupported_file_format(self) -> Option<UnsupportedFileFormatError> {
match self {
WriteScenarioError::Io(_) => None,
WriteScenarioError::UnsupportedFileFormat(err) => Some(err)
}
}
}
#[derive(Debug, Error)]
pub enum ReadScenarioError {
#[error("io: {0}")]
Io(#[from] io::Error),
#[error("unsupported file format: {0}")]
UnsupportedFileFormat(#[from] UnsupportedFileFormatError),
#[error("parse event: {0}")]
ParseEvent(#[from] ParseEventError),
#[error("deserializer: {0}")]
Deserializer(#[from] Box<dyn Error>),
}
impl ReadScenarioError {
pub fn io(self) -> Option<io::Error> {
match self {
ReadScenarioError::Io(err) => Some(err),
_ => None
}
}
pub fn unsupported_file_format(self) -> Option<UnsupportedFileFormatError> {
match self {
ReadScenarioError::UnsupportedFileFormat(err) => Some(err),
_ => None
}
}
pub fn parse_event(self) -> Option<ParseEventError> {
match self {
ReadScenarioError::ParseEvent(err) => Some(err),
_ => None
}
}
pub fn deserializer(self) -> Option<Box<dyn Error>> {
match self {
ReadScenarioError::Deserializer(err) => Some(err),
_ => None
}
}
}
fn check_ext(path: &Path, expected: &str) -> Result<(), UnsupportedFileFormatError> {
let ext = path
.extension()
.map(|ext| ext.to_str().unwrap_or_default())
.unwrap_or_default();
if ext == expected {
Ok(())
} else {
Err(UnsupportedFileFormatError(format!(
"expected file extension '{expected}', got '{ext}'"
)))
}
}
fn write<C, S>(scenario: &Scenario<S>, w: &mut impl Write) -> Result<(), WriteScenarioError>
where
S: Clone + Serialize,
C: From<PersistentScenario<S>> + ToString,
{
let persistent = PersistentScenario::from(scenario);
let data = C::from(persistent).to_string();
w.write_all(data.as_bytes())?;
Ok(())
}
fn read<C, CE, S>(
decoder: &Decoder<S>,
r: &mut impl BufRead,
) -> Result<Scenario<S>, ReadScenarioError>
where
for<'de> S: Deserialize<'de>,
CE: Error + 'static,
C: FromStr<Err = CE> + IntoInner<PersistentScenario<S>>,
{
let mut buf = String::default();
r.read_to_string(&mut buf)?;
let carrier = C::from_str(&buf).map_err(|err| Box::new(err) as Box<dyn Error>)?;
let persistent = carrier.into_inner();
Ok(persistent.decode(decoder)?)
}
#[cfg(test)]
mod tests;