use std::collections::HashSet;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub struct Pid(pub u64);
impl From<u64> for Pid {
fn from(v: u64) -> Self {
Self(v)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Timestamp(Duration);
impl Timestamp {
#[must_use]
pub fn from_millis(ms: u64) -> Self {
Self(Duration::from_millis(ms))
}
#[must_use]
pub fn as_duration(&self) -> Duration {
self.0
}
}
impl From<Duration> for Timestamp {
fn from(d: Duration) -> Self {
Self(d)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum OpType {
Invoke,
Ok,
Fail,
Info,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum OpFn {
Add,
Read,
}
#[derive(Debug, Clone)]
pub struct Op<T> {
pub index: usize,
pub op_type: OpType,
pub f: OpFn,
pub value: OpValue<T>,
pub time: Option<Timestamp>,
pub process: Pid,
}
impl<T> Op<T> {
pub fn add_invoke(index: usize, process: impl Into<Pid>, value: T) -> Self {
Self {
index,
op_type: OpType::Invoke,
f: OpFn::Add,
value: OpValue::Single(value),
time: None,
process: process.into(),
}
}
pub fn add_ok(index: usize, process: impl Into<Pid>, value: T) -> Self {
Self {
index,
op_type: OpType::Ok,
f: OpFn::Add,
value: OpValue::Single(value),
time: None,
process: process.into(),
}
}
pub fn read_invoke(index: usize, process: impl Into<Pid>) -> Self {
Self {
index,
op_type: OpType::Invoke,
f: OpFn::Read,
value: OpValue::None,
time: None,
process: process.into(),
}
}
pub fn read_ok(
index: usize,
process: impl Into<Pid>,
values: impl IntoIterator<Item = T>,
) -> Self {
Self {
index,
op_type: OpType::Ok,
f: OpFn::Read,
value: OpValue::Vec(values.into_iter().collect()),
time: None,
process: process.into(),
}
}
pub fn add_info(index: usize, process: impl Into<Pid>, value: T) -> Self {
Self {
index,
op_type: OpType::Info,
f: OpFn::Add,
value: OpValue::Single(value),
time: None,
process: process.into(),
}
}
pub fn add_fail(index: usize, process: impl Into<Pid>, value: T) -> Self {
Self {
index,
op_type: OpType::Fail,
f: OpFn::Add,
value: OpValue::Single(value),
time: None,
process: process.into(),
}
}
pub fn read_info(index: usize, process: impl Into<Pid>) -> Self {
Self {
index,
op_type: OpType::Info,
f: OpFn::Read,
value: OpValue::None,
time: None,
process: process.into(),
}
}
pub fn read_fail(index: usize, process: impl Into<Pid>) -> Self {
Self {
index,
op_type: OpType::Fail,
f: OpFn::Read,
value: OpValue::None,
time: None,
process: process.into(),
}
}
#[must_use]
pub fn at(mut self, time: Timestamp) -> Self {
self.time = Some(time);
self
}
#[must_use]
pub fn at_millis(self, ms: u64) -> Self {
self.at(Timestamp::from_millis(ms))
}
}
#[derive(Debug, Clone)]
pub enum OpValue<T> {
Single(T),
Set(HashSet<T>),
Vec(Vec<T>),
None,
}
impl<T> OpValue<T> {
pub fn as_single(&self) -> Option<&T> {
match self {
OpValue::Single(v) => Some(v),
_ => None,
}
}
pub fn as_set(&self) -> Option<&HashSet<T>> {
match self {
OpValue::Set(s) => Some(s),
_ => None,
}
}
pub fn as_vec(&self) -> Option<&Vec<T>> {
match self {
OpValue::Vec(v) => Some(v),
_ => None,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct History<T> {
ops: Vec<Op<T>>,
}
impl<T> History<T> {
#[must_use]
pub fn new() -> Self {
Self { ops: Vec::new() }
}
#[must_use]
pub fn from_ops(ops: Vec<Op<T>>) -> Self {
Self { ops }
}
pub fn push(&mut self, op: Op<T>) {
self.ops.push(op);
}
#[must_use]
pub fn ops(&self) -> &[Op<T>] {
&self.ops
}
#[must_use]
pub fn len(&self) -> usize {
self.ops.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.ops.is_empty()
}
#[must_use]
pub fn invocation(&self, completion_pos: usize) -> Option<&Op<T>> {
if completion_pos == 0 || completion_pos >= self.ops.len() {
return None;
}
let completion = &self.ops[completion_pos];
self.ops[..completion_pos].iter().rev().find(|op| {
op.process == completion.process && op.op_type == OpType::Invoke && op.f == completion.f
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_invocation_at_len_returns_none() {
let mut history: History<i32> = History::new();
history.push(Op::add_invoke(0, 0u64, 1));
history.push(Op::add_ok(1, 0u64, 1));
assert!(history.invocation(history.len()).is_none());
}
#[test]
fn test_invocation_beyond_len_returns_none() {
let mut history: History<i32> = History::new();
history.push(Op::add_invoke(0, 0u64, 1));
history.push(Op::add_ok(1, 0u64, 1));
assert!(history.invocation(history.len() + 1).is_none());
assert!(history.invocation(history.len() + 100).is_none());
}
#[test]
fn test_invocation_at_zero_returns_none() {
let mut history: History<i32> = History::new();
history.push(Op::add_invoke(0, 0u64, 1));
history.push(Op::add_ok(1, 0u64, 1));
assert!(history.invocation(0).is_none());
}
#[test]
fn test_invocation_empty_history() {
let history: History<i32> = History::new();
assert!(history.invocation(0).is_none());
assert!(history.invocation(1).is_none());
}
}