git_bug/replica/entity/lamport/mod.rs
1// git-bug-rs - A rust library for interfacing with git-bug repositories
2//
3// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
4// SPDX-License-Identifier: GPL-3.0-or-later
5//
6// This file is part of git-bug-rs/git-gub.
7//
8// You should have received a copy of the License along with this program.
9// If not, see <https://www.gnu.org/licenses/agpl.txt>.
10
11//! > The Lamport timestamp algorithm is a simple logical clock algorithm used
12//! > to determine the order of events in a distributed computer system. As
13//! > different nodes or processes will typically not be perfectly synchronized,
14//! > this algorithm is used to provide a partial ordering of events with
15//! > minimal overhead, and conceptually provide a starting point for the more
16//! > advanced vector clock method. The algorithm is named after its creator,
17//! > Leslie Lamport.
18//!
19//! Source [wikipedia](https://en.wikipedia.org/wiki/Lamport_timestamp).
20//!
21//! This is the implementation of this specialized for the use case of
22//! `git-bug-rs`.
23
24use serde::{Deserialize, Serialize};
25
26pub mod mem;
27pub mod persistent;
28pub mod repository;
29
30/// The internal representation of a [`Clock`]'s time.
31/// The only property of this type is that it is always advancing, it is not
32/// connected to an actual time stamp.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
34pub struct Time(u64);
35
36impl Time {
37 /// Construct a Time from a raw value.
38 #[must_use]
39 pub fn from(value: u64) -> Self {
40 Self(value)
41 }
42
43 /// Access the internal value of this Time.
44 #[must_use]
45 pub fn value(self) -> u64 {
46 self.0
47 }
48}
49
50/// A Lamport clock.
51///
52/// See the module documentation, for information on why this is needed.
53pub trait Clock {
54 /// The Error returned, when trying to [`increment`][`Clock::increment`]
55 /// this clock.
56 type IncrementError: std::error::Error;
57
58 /// The Error returned, when trying to [`witness`][`Clock::witness`] this
59 /// clock.
60 type WitnessError: std::error::Error;
61
62 /// Get the current value of this clock.
63 fn time(&self) -> Time;
64
65 /// Return the current value and increment it internally.
66 ///
67 /// # Errors
68 /// If the increment operation failed.
69 fn increment(&mut self) -> Result<Time, Self::IncrementError>;
70
71 /// Update our clock based on another processes clock's value, we
72 /// “witnessed”.
73 ///
74 /// We effectively try to keep our clock at `other + 1` time, so that we are
75 /// always ahead by one time unit.
76 ///
77 /// # Examples
78 /// ```rust
79 /// use git_bug::replica::entity::lamport::{Clock, Time, mem};
80 /// # fn main() -> Result<(), <mem::MemClock as Clock>::IncrementError> {
81 /// let mut clock = mem::MemClock::new();
82 /// assert_eq!(1, clock.time().value());
83 /// assert_eq!(1, clock.increment()?.value());
84 ///
85 /// clock.witness(Time::from(42));
86 /// assert_eq!(42, clock.time().value());
87 ///
88 /// clock.witness(Time::from(30));
89 /// assert_eq!(Time::from(42), clock.time());
90 /// # Ok(())
91 /// # }
92 /// ```
93 ///
94 /// # Errors
95 /// If the witness operation failed.
96 fn witness(&mut self, other: Time) -> Result<(), Self::WitnessError>;
97}
98
99#[cfg(test)]
100/// Internal tests
101pub mod test {
102 use super::{Clock, Time};
103
104 /// Generic clock test function
105 ///
106 /// # Panics
107 /// If tests fail.
108 pub fn test_clock<C>(mut clock: C)
109 where
110 C: Clock,
111 {
112 assert_eq!(Time(1), clock.time());
113
114 let time = clock.increment().unwrap();
115 assert_eq!(Time(1), time);
116 assert_eq!(Time(2), clock.time());
117
118 clock.witness(Time(42)).unwrap();
119 assert_eq!(Time(42), clock.time());
120
121 clock.witness(Time(42)).unwrap();
122 assert_eq!(Time(42), clock.time());
123
124 clock.witness(Time(30)).unwrap();
125 assert_eq!(Time(42), clock.time());
126 }
127}