blob: e93db9216139c2a6f538c6f5be9380b563b4e8fb [file] [log] [blame]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Representation for delta CRDT types.
//!
//! A delta CRDT type consists of two components:
//! 1. the `base`, which is the read only part that this delta CRDT is building on top of, and
//! 2. the `delta`, which is a mutable part that contains the changes within this delta.
//!
//! For example, a delta CRDT can be created during a transaction, in which the delta component
//! keeps track of the mutations made within the transaction. This allows the resulting update
//! message generated for the network interface to only contain the latest changes.
//!
//! This module offers three containers for delta CRDTs:
//! * [`DeltaOwned`] owns its own delta component, and has a shared reference to the base component.
//! * [`DeltaMut`] has a mutable reference to the delta component, and has a shared reference to the
//! base component.
//! * [`DeltaRef`] has shared references to both the delta and base components.
//!
//! [`CrdtState`] implementations and [`DeltaOwned`] can be converted into [`DeltaMut`] using
//! [`as_mut`][AsDeltaMut::as_mut], and can be converted into [`DeltaRef`] using
//! [`as_ref`][AsDeltaRef::as_ref].
use crate::CrdtState;
/// A read-only reference to a base and delta components to a CRDT.
///
/// This contains a reference to the `base` component, which is the read-only component representing
/// the state when this delta was created, and the `delta` component, where the mutations since the
/// creation of the CRDT are stored.
#[derive(Debug, PartialEq, Eq)]
pub struct DeltaRef<'d, T> {
/// The base component, which can be `None` if there is no base.
pub base: Option<&'d T>,
/// The delta component.
///
/// This can be `None` if delta component doesn't exist, for example when trying to get an
/// element from a map that only exist in the base component.
pub delta: Option<&'d T>,
}
impl<T> DeltaRef<'_, T> {
/// Combines the base part and the delta part and return the resulting merged component.
///
/// See [`CrdtState::merge`] for details on the merge operation.
pub fn merge(&self) -> T
where
T: CrdtState + Clone + Default,
{
match (self.base, self.delta) {
(None, None) => T::default(),
(None, Some(delta)) => delta.clone(),
(Some(base), None) => base.clone(),
(Some(base), Some(delta)) => T::merge(base, delta),
}
}
}
impl<C> AsDeltaRef<C> for DeltaRef<'_, C> {
fn base(&self) -> Option<&C> {
self.base
}
fn delta(&self) -> Option<&C> {
self.delta
}
}
/// A mutable reference to a delta-CRDT.
///
/// Contains a reference to the `base` component, which is the read-only component representing the
/// state when this delta was created, and the `delta` component, where the mutations since the
/// creation of the CRDT are stored.
///
/// Semantically this delta type behaves as if it is `CrdtState::merge(base, delta)`.
#[derive(Debug)]
pub struct DeltaMut<'d, T> {
/// The base component, which can be `None` if there is no base.
pub base: Option<&'d T>,
/// The delta component.
///
/// Unlike [`DeltaRef`], this is not an `Option`. Instead, if the value doesn't exist, the
/// parent container or collection should insert the default value such that any modifications
/// to the subtree can be tracked.
pub delta: &'d mut T,
}
impl<C> AsDeltaRef<C> for DeltaMut<'_, C> {
fn base(&self) -> Option<&C> {
self.base
}
fn delta(&self) -> Option<&C> {
Some(self.delta)
}
}
impl<C> AsDeltaMut<C> for DeltaMut<'_, C> {
fn delta_mut(&mut self) -> &mut C {
self.delta
}
fn as_mut(&mut self) -> DeltaMut<'_, C> {
DeltaMut {
base: self.base,
delta: self.delta,
}
}
}
/// A CRDT structure where the delta-component is owned.
///
/// Contains a reference to the `base` component, which is the read-only component representing the
/// state when this delta was created, and the `delta` component, where the mutations since the
/// creation of the CRDT are stored.
#[derive(Debug)]
pub struct DeltaOwned<'b, T> {
/// The base component.
///
/// Unlike [`DeltaMut`] and [`DeltaRef`], this is not an `Option` because a `DeltaOwned` without
/// a base is the same as just `T`.
pub base: &'b T,
/// The delta component.
pub delta: T,
}
impl<'b, T> DeltaOwned<'b, T> {
/// Creates a new owned delta from the given base.
///
/// The delta component is initially empty, so the contents are semantically the same as the
/// given `base`.
pub fn new(base: &'b T) -> Self
where
T: Default,
{
Self {
base,
delta: Default::default(),
}
}
/// Merges the base and the delta components of this.
///
/// See [`CrdtState::merge`] for details on the merge operation.
pub fn merge(&self) -> T
where
T: CrdtState,
{
T::merge(self.base, &self.delta)
}
/// Converts this into its delta components, discarding the reference to the
/// base component.
pub fn into_delta(self) -> T {
self.delta
}
}
impl<C: CrdtState> AsDeltaRef<C> for DeltaOwned<'_, C> {
fn base(&self) -> Option<&C> {
Some(self.base)
}
fn delta(&self) -> Option<&C> {
Some(&self.delta)
}
}
impl<C: CrdtState> AsDeltaMut<C> for DeltaOwned<'_, C> {
fn delta_mut(&mut self) -> &mut C {
&mut self.delta
}
fn as_mut(&mut self) -> DeltaMut<'_, C> {
DeltaMut {
base: Some(self.base),
delta: &mut self.delta,
}
}
}
/// Something that can behave like a read-only reference to a delta CRDT.
pub trait AsDeltaRef<C> {
/// The base component. This is `None` if there is no base, for example if the given type is
/// just a CRDT component.
fn base(&self) -> Option<&C>;
/// The delta component. This is `None` if there is no delta, for example when a value is read
/// from a map in which the entry only exists in the base component.
fn delta(&self) -> Option<&C>;
/// Returns a [`DeltaRef`] from this.
fn as_ref(&self) -> DeltaRef<'_, C> {
DeltaRef {
base: self.base(),
delta: self.delta(),
}
}
}
/// Something that can behave like a mutable reference to a delta CRDT.
pub trait AsDeltaMut<C>: AsDeltaRef<C> {
/// The delta component.
///
/// Unlike [`AsDeltaRef::delta`], this cannot be `None`. If the value does not exist yet it
/// should be initialized with `Default::default()`.
fn delta_mut(&mut self) -> &mut C;
/// Returns a [`DeltaMut`] from this.
fn as_mut(&mut self) -> DeltaMut<'_, C>;
}
/// A blanket implementation for any [`CrdtState`] to be treated as a delta, which has no base
/// component and the CRDT's state is treated as the delta.
impl<C: CrdtState> AsDeltaRef<C> for C {
fn base(&self) -> Option<&C> {
None
}
fn delta(&self) -> Option<&C> {
Some(self)
}
}
/// A blanket implementation for any [`CrdtState`] to be treated as a delta, which has no base
/// component and the CRDT's state is treated as the delta.
impl<C: CrdtState> AsDeltaMut<C> for C {
fn delta_mut(&mut self) -> &mut C {
self
}
fn as_mut(&mut self) -> DeltaMut<'_, C> {
DeltaMut {
base: None,
delta: self,
}
}
}