git-bug 0.2.4

A rust library for interfacing with git-bug repositories
Documentation
// git-bug-rs - A rust library for interfacing with git-bug repositories
//
// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This file is part of git-bug-rs/git-gub.
//
// You should have received a copy of the License along with this program.
// If not, see <https://www.gnu.org/licenses/agpl.txt>.

//! > The Lamport timestamp algorithm is a simple logical clock algorithm used
//! > to determine the order of events in a distributed computer system. As
//! > different nodes or processes will typically not be perfectly synchronized,
//! > this algorithm is used to provide a partial ordering of events with
//! > minimal overhead, and conceptually provide a starting point for the more
//! > advanced vector clock method. The algorithm is named after its creator,
//! > Leslie Lamport.
//!
//! Source [wikipedia](https://en.wikipedia.org/wiki/Lamport_timestamp).
//!
//! This is the implementation of this specialized for the use case of
//! `git-bug-rs`.

use serde::{Deserialize, Serialize};

pub mod mem;
pub mod persistent;
pub mod repository;

/// The internal representation of a [`Clock`]'s time.
/// The only property of this type is that it is always advancing, it is not
/// connected to an actual time stamp.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Time(u64);

impl Time {
    /// Construct a Time from a raw value.
    #[must_use]
    pub fn from(value: u64) -> Self {
        Self(value)
    }

    /// Access the internal value of this Time.
    #[must_use]
    pub fn value(self) -> u64 {
        self.0
    }
}

/// A Lamport clock.
///
/// See the module documentation, for information on why this is needed.
pub trait Clock {
    /// The Error returned, when trying to [`increment`][`Clock::increment`]
    /// this clock.
    type IncrementError: std::error::Error;

    /// The Error returned, when trying to [`witness`][`Clock::witness`] this
    /// clock.
    type WitnessError: std::error::Error;

    /// Get the current value of this clock.
    fn time(&self) -> Time;

    /// Return the current value and increment it internally.
    ///
    /// # Errors
    /// If the increment operation failed.
    fn increment(&mut self) -> Result<Time, Self::IncrementError>;

    /// Update our clock based on another processes clock's value, we
    /// “witnessed”.
    ///
    /// We effectively try to keep our clock at `other + 1` time, so that we are
    /// always ahead by one time unit.
    ///
    /// # Examples
    /// ```rust
    /// use git_bug::replica::entity::lamport::{Clock, Time, mem};
    /// # fn main() -> Result<(), <mem::MemClock as Clock>::IncrementError> {
    /// let mut clock = mem::MemClock::new();
    /// assert_eq!(1, clock.time().value());
    /// assert_eq!(1, clock.increment()?.value());
    ///
    /// clock.witness(Time::from(42));
    /// assert_eq!(42, clock.time().value());
    ///
    /// clock.witness(Time::from(30));
    /// assert_eq!(Time::from(42), clock.time());
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Errors
    /// If the witness operation failed.
    fn witness(&mut self, other: Time) -> Result<(), Self::WitnessError>;
}

#[cfg(test)]
/// Internal tests
pub mod test {
    use super::{Clock, Time};

    /// Generic clock test function
    ///
    /// # Panics
    /// If tests fail.
    pub fn test_clock<C>(mut clock: C)
    where
        C: Clock,
    {
        assert_eq!(Time(1), clock.time());

        let time = clock.increment().unwrap();
        assert_eq!(Time(1), time);
        assert_eq!(Time(2), clock.time());

        clock.witness(Time(42)).unwrap();
        assert_eq!(Time(42), clock.time());

        clock.witness(Time(42)).unwrap();
        assert_eq!(Time(42), clock.time());

        clock.witness(Time(30)).unwrap();
        assert_eq!(Time(42), clock.time());
    }
}