use std::{cmp::max, fmt::Display}; pub mod render; #[derive(Debug)] enum Trace { Pure, InvolvesOneResolution, Event(String), Wrapped { name: String, trace: TraceBox }, Parallel(TraceBox, TraceBox), Sequential { first: TraceBox, second: TraceBox }, } #[derive(Debug)] pub struct TraceBox { trace: Box, } impl Trace { fn fmt_parallel(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Parallel(a, b) => { write!(f, "{} | {}", ParallelBox(a), ParallelBox(b)) } trace => write!(f, "{}", trace), } } fn fmt_sequential(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Sequential { first, second } => { write!(f, "{} > {}", SequentialBox(first), SequentialBox(second)) } trace => write!(f, "{}", trace), } } } struct ParallelBox<'a>(&'a TraceBox); struct SequentialBox<'a>(&'a TraceBox); impl<'a> Display for ParallelBox<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.trace.fmt_parallel(f) } } impl<'a> Display for SequentialBox<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.trace.fmt_sequential(f) } } impl Display for Trace { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Pure => write!(f, "."), Self::InvolvesOneResolution => write!(f, "?"), Self::Event(event) => write!(f, "{}", event), Self::Wrapped { name, trace } => write!(f, "{} @ {}", name, trace), Self::Parallel(a, b) => { write!(f, "( {} | {} )", ParallelBox(a), ParallelBox(b)) } Self::Sequential { first, second } => write!( f, "( {} > {} )", SequentialBox(first), SequentialBox(second) ), } } } impl Display for TraceBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.trace) } } impl From for TraceBox { fn from(value: Trace) -> Self { TraceBox { trace: value.into(), } } } impl From for Trace { fn from(value: TraceBox) -> Self { *value.trace } } impl Trace { fn sequential(ta: Self, tb: Self) -> Self { match (ta, tb) { (Trace::Pure, a) => a, (a, Trace::Pure) => a, (a, b) => Trace::Sequential { first: a.into(), second: b.into(), }, } } fn length(&self) -> usize { match self { Self::Pure => 0, Self::InvolvesOneResolution => 1, Self::Event(_) => 0, Self::Wrapped { trace, .. } => trace.length(), Self::Parallel(a, b) => max(a.length(), b.length()), Self::Sequential { first, second } => first.length() + second.length(), } } fn width(&self) -> usize { match self { Self::Pure => 0, Self::InvolvesOneResolution => 1, Self::Event(_) => 0, Self::Wrapped { trace, .. } => trace.width(), Self::Parallel(a, b) => a.width() + b.width(), Self::Sequential { first, second } => max(first.width(), second.width()), } } fn parallel(ta: Self, tb: Self) -> Self { match (ta, tb) { (Trace::Pure, a) => a, (a, Trace::Pure) => a, (a, b) => Trace::Parallel(a.into(), b.into()), } } } impl TraceBox { pub fn pure() -> Self { Trace::Pure.into() } pub fn resolution() -> Self { Trace::InvolvesOneResolution.into() } pub fn event(event: String) -> Self { Trace::Event(event).into() } pub fn wrapped(self, event: String) -> Self { Trace::Wrapped { name: event, trace: self, } .into() } pub fn after(self, t: Self) -> Self { Trace::sequential(t.into(), self.into()).into() } pub fn before(self, t: Self) -> Self { Trace::sequential(self.into(), t.into()).into() } pub fn parallel(ta: Self, tb: Self) -> Self { Trace::parallel(ta.into(), tb.into()).into() } pub fn length(&self) -> usize { self.trace.length() } pub fn width(&self) -> usize { self.trace.width() } }