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>.

//! An in-memory implementation of a [`Clock`][`super::Clock`].

use std::sync::atomic::{AtomicU64, Ordering};

use super::Clock;

/// A thread safe implementation of a lamport clock.
/// It uses efficient atomic operations for all of its functions.
#[derive(Debug)]
pub struct MemClock {
    counter: AtomicU64,
}

impl Clock for MemClock {
    type IncrementError = std::convert::Infallible;
    type WitnessError = std::convert::Infallible;

    fn time(&self) -> super::Time {
        super::Time(self.counter.load(Ordering::SeqCst))
    }

    fn increment(&mut self) -> Result<super::Time, Self::IncrementError> {
        Ok(super::Time(self.counter.fetch_add(1, Ordering::SeqCst)))
    }

    fn witness(&mut self, time: super::Time) -> Result<(), Self::WitnessError> {
        let current = self.time().0;
        let other = time.0;

        if other <= current {
            return Ok(());
        }

        // Ensure that our local clock is at least one ahead.
        while self
            .counter
            .compare_exchange(current, other, Ordering::SeqCst, Ordering::SeqCst)
            != Ok(current)
        {
            // The compare_exchange failed, so we just retry. Eventually it
            // should succeed or a future witness will pass us by
            // and our witness will end.
            // TODO(@bpeetz): This should be bounded, shouldn't it? <2025-04-15>
        }

        Ok(())
    }
}

impl Default for MemClock {
    fn default() -> Self {
        Self::new()
    }
}

impl MemClock {
    /// Create a new [`MemClock`].
    /// The starting value is 1.
    #[must_use]
    pub fn new() -> Self {
        Self {
            counter: AtomicU64::new(1),
        }
    }

    /// Creates a new in memory clock, with the initial time value of `value`.
    ///
    /// # Panics
    /// If the value is invalid (i.e., the value is equal to 0).
    #[must_use]
    pub fn new_with_value(value: u64) -> Self {
        if value == 0 {
            todo!("Read invalid value from persisted clock.")
        }

        Self {
            counter: AtomicU64::new(value),
        }
    }
}

#[cfg(test)]
mod test {
    use super::MemClock;
    use crate::replica::entity::lamport::test::test_clock;

    #[test]
    fn test_mem_clock() {
        let clock = MemClock::new();
        test_clock(clock);
    }
}