use std::marker::PhantomData;
use super::{Hierarchy, Interaction, InteractionPattern, Kind, Label, Length};
use crate::Unit;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct PatternState<U = u8>
where
U: Unit,
{
interactions: Vec<Interaction>,
finalized: bool,
_unit: PhantomData<U>,
}
impl<U> PatternState<U>
where
U: Unit,
{
#[must_use]
pub const fn new() -> Self {
Self {
interactions: Vec::new(),
finalized: false,
_unit: PhantomData,
}
}
#[must_use]
pub fn finalize(self) -> InteractionPattern {
assert!(!self.finalized, "Transcript is already finalized.");
match InteractionPattern::new(self.interactions) {
Ok(transcript) => transcript,
Err(e) => panic!("Error validating interaction pattern: {e}"),
}
}
pub fn interact(&mut self, interaction: Interaction) {
assert!(!self.finalized, "Transcript is already finalized.");
if let Some(begin) = self.last_open_begin() {
assert!(
begin.kind() == Kind::Protocol || begin.kind() == interaction.kind(),
"Invalid interaction kind: expected {}, got {}",
begin.kind(),
interaction.kind()
);
assert!(
interaction.hierarchy() != Hierarchy::End || interaction.closes(begin),
"Mismatched begin and end: {begin}, {interaction}"
);
} else {
assert!(
interaction.hierarchy() != Hierarchy::End,
"Missing begin for {interaction}"
);
}
self.interactions.push(interaction);
}
fn last_open_begin(&self) -> Option<&Interaction> {
let mut stack = 0;
for interaction in self.interactions.iter().rev() {
match interaction.hierarchy() {
Hierarchy::End => stack += 1,
Hierarchy::Begin => {
if stack == 0 {
return Some(interaction);
}
stack -= 1;
}
_ => {}
}
}
None
}
}
impl<U> super::Pattern for PatternState<U>
where
U: Unit,
{
fn abort(&mut self) {
assert!(!self.finalized, "Transcript is already finalized.");
self.finalized = true;
}
fn begin<T: ?Sized>(&mut self, label: Label, kind: Kind, length: Length) {
self.interact(Interaction::new::<T>(Hierarchy::Begin, kind, label, length));
}
fn end<T: ?Sized>(&mut self, label: Label, kind: Kind, length: Length) {
self.interact(Interaction::new::<T>(Hierarchy::End, kind, label, length));
}
}