You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
3.9 KiB
Rust
136 lines
3.9 KiB
Rust
//! Distributed primitives.
|
|
|
|
use std::fmt;
|
|
|
|
use crate::{Chronofold, LogIndex};
|
|
|
|
/// A trait alias to reduce redundancy in type declarations.
|
|
pub trait Author:
|
|
PartialEq + Eq + PartialOrd + Ord + Clone + Copy + fmt::Debug + fmt::Display
|
|
{
|
|
}
|
|
|
|
/// Blanket implementation of `Author`.
|
|
///
|
|
/// Every type that implements the needed traits automatically implements
|
|
/// `Author` as well.
|
|
impl<T> Author for T where
|
|
T: PartialEq + Eq + PartialOrd + Ord + Clone + Copy + fmt::Debug + fmt::Display
|
|
{
|
|
}
|
|
|
|
/// An ordered pair of the author's index and the author.
|
|
///
|
|
/// The lexicographic order of timestamps forms an arbitrary total order, that
|
|
/// is consistent with cause-effect ordering. That is, if a timestamp is
|
|
/// greater than another, its associated event either happened after the other
|
|
/// or was concurrent.
|
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
pub struct Timestamp<A>(pub LogIndex, pub A);
|
|
|
|
impl<A: fmt::Display> fmt::Display for Timestamp<A> {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "<{}, {}>", self.0, self.1)
|
|
}
|
|
}
|
|
|
|
/// An operation is the unit of change in the distributed context.
|
|
///
|
|
/// Ops are independent of the subjective orders in the chronofolds'
|
|
/// logs. Different authors exchange ops to keep their local replicas
|
|
/// synchronized.
|
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
pub struct Op<A, T> {
|
|
pub id: Timestamp<A>,
|
|
pub payload: OpPayload<A, T>,
|
|
}
|
|
|
|
impl<A, T> Op<A, T> {
|
|
pub fn new(id: Timestamp<A>, payload: OpPayload<A, T>) -> Self {
|
|
Self { id, payload }
|
|
}
|
|
|
|
pub fn root(id: Timestamp<A>) -> Self {
|
|
Op::new(id, OpPayload::Root)
|
|
}
|
|
|
|
pub fn insert(id: Timestamp<A>, reference: Option<Timestamp<A>>, value: T) -> Self {
|
|
Op::new(id, OpPayload::Insert(reference, value))
|
|
}
|
|
|
|
pub fn delete(id: Timestamp<A>, reference: Timestamp<A>) -> Self {
|
|
Op::new(id, OpPayload::Delete(reference))
|
|
}
|
|
}
|
|
|
|
impl<A, T: Clone> Op<A, &T> {
|
|
/// Maps an Op<A, &T> to an Op<A, T> by cloning the payload.
|
|
pub fn cloned(self) -> Op<A, T> {
|
|
Op {
|
|
id: self.id,
|
|
payload: self.payload.cloned(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The payload of an operation.
|
|
///
|
|
/// Ops don't contain `Change<T>` directly, as these can contain information
|
|
/// that is only meaningful within the context of the local chronofold. E.g. a
|
|
/// change may refer to another change by log index, which has to be replaced
|
|
/// by a timestamp in the distributed operation.
|
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
pub enum OpPayload<A, T> {
|
|
Root,
|
|
Insert(Option<Timestamp<A>>, T),
|
|
Delete(Timestamp<A>),
|
|
}
|
|
|
|
impl<A, T> OpPayload<A, T> {
|
|
pub fn reference(&self) -> Option<&Timestamp<A>> {
|
|
use OpPayload::*;
|
|
match self {
|
|
Root => None,
|
|
Insert(reference, _) => reference.as_ref(),
|
|
Delete(reference) => Some(reference),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<A, T: Clone> OpPayload<A, &T> {
|
|
pub fn cloned(self) -> OpPayload<A, T> {
|
|
use OpPayload::*;
|
|
match self {
|
|
Root => Root,
|
|
Insert(reference, t) => Insert(reference, t.clone()),
|
|
Delete(reference) => Delete(reference),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait IntoLocalValue<A, LocalValue> {
|
|
fn into_local_value(self, chronofold: &Chronofold<A, LocalValue>) -> LocalValue;
|
|
}
|
|
|
|
pub trait FromLocalValue<'a, A, LocalValue> {
|
|
fn from_local_value(source: &'a LocalValue, chronofold: &Chronofold<A, LocalValue>) -> Self;
|
|
}
|
|
|
|
impl<A, T, V> IntoLocalValue<A, T> for V
|
|
where
|
|
V: Into<T>,
|
|
{
|
|
fn into_local_value(self, _chronofold: &Chronofold<A, T>) -> T {
|
|
self.into()
|
|
}
|
|
}
|
|
|
|
impl<'a, A, T> FromLocalValue<'a, A, T> for &'a T {
|
|
fn from_local_value(source: &'a T, _chronofold: &Chronofold<A, T>) -> Self {
|
|
source
|
|
}
|
|
}
|