use std::cmp::{Ordering, PartialOrd};
use std::fmt;
use std::hash::{Hash, Hasher};
use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize)]
pub struct Dot<A> {
pub actor: A,
pub counter: u64,
}
impl<A> Dot<A> {
pub fn new(actor: A, counter: u64) -> Self {
Self { actor, counter }
}
pub fn apply_inc(&mut self) {
self.counter += 1;
}
}
impl<A: Clone> Dot<A> {
pub fn inc(&self) -> Self {
Self {
actor: self.actor.clone(),
counter: self.counter + 1,
}
}
}
impl<A: Copy> Copy for Dot<A> {}
impl<A: PartialEq> PartialEq for Dot<A> {
fn eq(&self, other: &Self) -> bool {
self.actor == other.actor && self.counter == other.counter
}
}
impl<A: Eq> Eq for Dot<A> {}
impl<A: Hash> Hash for Dot<A> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.actor.hash(state);
self.counter.hash(state);
}
}
impl<A: PartialOrd> PartialOrd for Dot<A> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.actor == other.actor {
self.counter.partial_cmp(&other.counter)
} else {
None
}
}
}
impl<A: fmt::Debug> fmt::Debug for Dot<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}.{:?}", self.actor, self.counter)
}
}
impl<A> From<(A, u64)> for Dot<A> {
fn from(dot_material: (A, u64)) -> Self {
let (actor, counter) = dot_material;
Self { actor, counter }
}
}
#[cfg(feature = "quickcheck")]
use quickcheck::{Arbitrary, Gen};
#[cfg(feature = "quickcheck")]
impl<A: Arbitrary + Clone> Arbitrary for Dot<A> {
fn arbitrary(g: &mut Gen) -> Self {
Dot {
actor: A::arbitrary(g),
counter: u64::arbitrary(g) % 50,
}
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
let mut shrunk_dots = Vec::new();
if self.counter > 0 {
shrunk_dots.push(Self::new(self.actor.clone(), self.counter - 1));
}
Box::new(shrunk_dots.into_iter())
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
pub struct OrdDot<A: Ord> {
pub actor: A,
pub counter: u64,
}
impl<A: Ord> From<OrdDot<A>> for Dot<A> {
fn from(OrdDot { actor, counter }: OrdDot<A>) -> Self {
Self { actor, counter }
}
}
impl<A: Ord> From<Dot<A>> for OrdDot<A> {
fn from(Dot { actor, counter }: Dot<A>) -> Self {
Self { actor, counter }
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct DotRange<A> {
pub actor: A,
pub counter_range: core::ops::Range<u64>,
}
impl<A: fmt::Debug + Ord> fmt::Display for OrdDot<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}.{}", self.actor, self.counter)
}
}
impl<A: fmt::Debug> fmt::Display for DotRange<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{:?}.({}..{})",
self.actor, self.counter_range.start, self.counter_range.end
)
}
}
impl<A: fmt::Debug> std::error::Error for DotRange<A> {}
#[cfg(all(test, feature = "quickcheck"))]
mod test {
use super::*;
use quickcheck_macros::quickcheck;
#[quickcheck]
fn prop_inc_increments_only_the_counter(dot: Dot<u8>) -> bool {
dot.inc() == Dot::new(dot.actor, dot.counter + 1)
}
#[quickcheck]
fn prop_partial_order(a: Dot<u8>, b: Dot<u8>) -> bool {
let cmp_ab = a.partial_cmp(&b);
let cmp_ba = b.partial_cmp(&a);
match (cmp_ab, cmp_ba) {
(None, None) => a.actor != b.actor,
(Some(Ordering::Less), Some(Ordering::Greater)) => {
a.actor == b.actor && a.counter < b.counter
}
(Some(Ordering::Greater), Some(Ordering::Less)) => {
a.actor == b.actor && a.counter > b.counter
}
(Some(Ordering::Equal), Some(Ordering::Equal)) => {
a.actor == b.actor && a.counter == b.counter
}
_ => false,
}
}
#[quickcheck]
fn prop_ordered_dot_is_ordered_by_actor_first(dot_a: Dot<u8>, dot_b: Dot<u8>) -> bool {
let ord_dot_a: OrdDot<_> = dot_a.into();
let ord_dot_b: OrdDot<_> = dot_b.into();
match ord_dot_a.actor.cmp(&ord_dot_b.actor) {
Ordering::Less => ord_dot_a < ord_dot_b,
Ordering::Greater => ord_dot_a > ord_dot_b,
Ordering::Equal => {
ord_dot_a.counter.cmp(&ord_dot_b.counter) == ord_dot_a.cmp(&ord_dot_b)
}
}
}
}