reifydb-runtime 0.4.11

Runtime infrastructure for ReifyDB
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2025 ReifyDB

//! Native clock implementation.

use std::{
	cmp, fmt, ops,
	sync::{
		Arc,
		atomic::{AtomicU64, Ordering},
	},
	time,
	time::{Duration, SystemTime, UNIX_EPOCH},
};

#[allow(clippy::disallowed_methods)]
#[inline(always)]
fn platform_now_nanos() -> u64 {
	SystemTime::now().duration_since(UNIX_EPOCH).expect("System time is before Unix epoch").as_nanos() as u64
}

/// A clock that provides time - either real system time or mock time for testing.
#[derive(Clone)]
pub enum Clock {
	/// Real system clock - delegates to platform time
	Real,
	/// Mock clock with controllable time
	Mock(MockClock),
}

impl Clock {
	/// Get current time in nanoseconds since Unix epoch
	pub fn now_nanos(&self) -> u64 {
		match self {
			Clock::Real => platform_now_nanos(),
			Clock::Mock(mock) => mock.now_nanos(),
		}
	}

	/// Get current time in microseconds since Unix epoch
	pub fn now_micros(&self) -> u64 {
		self.now_nanos() / 1_000
	}

	/// Get current time in milliseconds since Unix epoch
	pub fn now_millis(&self) -> u64 {
		self.now_nanos() / 1_000_000
	}

	/// Get current time in seconds since Unix epoch
	pub fn now_secs(&self) -> u64 {
		self.now_nanos() / 1_000_000_000
	}

	#[allow(clippy::disallowed_methods)]
	pub fn instant(&self) -> Instant {
		match self {
			Clock::Real => Instant {
				inner: InstantInner::Real(time::Instant::now()),
			},
			Clock::Mock(mock) => Instant {
				inner: InstantInner::Mock {
					captured_nanos: mock.now_nanos(),
					clock: mock.clone(),
				},
			},
		}
	}
}

/// Mock clock with atomic time storage for thread-safe access.
#[derive(Clone)]
pub struct MockClock {
	inner: Arc<MockClockInner>,
}

struct MockClockInner {
	time_nanos: AtomicU64,
}

impl MockClock {
	/// Create a new mock clock starting at the given nanoseconds
	pub fn new(initial_nanos: u64) -> Self {
		Self {
			inner: Arc::new(MockClockInner {
				time_nanos: AtomicU64::new(initial_nanos),
			}),
		}
	}

	/// Create a new mock clock starting at the given milliseconds
	pub fn from_millis(millis: u64) -> Self {
		Self::new(millis * 1_000_000)
	}

	/// Get current time in nanoseconds
	pub fn now_nanos(&self) -> u64 {
		self.inner.time_nanos.load(Ordering::Acquire)
	}

	/// Get current time in microseconds
	pub fn now_micros(&self) -> u64 {
		self.now_nanos() / 1_000
	}

	/// Get current time in milliseconds
	pub fn now_millis(&self) -> u64 {
		self.now_nanos() / 1_000_000
	}

	/// Get current time in seconds
	pub fn now_secs(&self) -> u64 {
		self.now_nanos() / 1_000_000_000
	}

	/// Set time to specific nanoseconds
	pub fn set_nanos(&self, nanos: u64) {
		self.inner.time_nanos.store(nanos, Ordering::Release);
	}

	/// Set time to specific microseconds
	pub fn set_micros(&self, micros: u64) {
		self.set_nanos(micros * 1_000);
	}

	/// Set time to specific milliseconds
	pub fn set_millis(&self, millis: u64) {
		self.set_nanos(millis * 1_000_000);
	}

	/// Advance time by nanoseconds
	pub fn advance_nanos(&self, nanos: u64) {
		self.set_nanos(self.now_nanos().saturating_add(nanos));
	}

	/// Advance time by microseconds
	pub fn advance_micros(&self, micros: u64) {
		self.advance_nanos(micros * 1_000);
	}

	/// Advance time by milliseconds
	pub fn advance_millis(&self, millis: u64) {
		self.advance_nanos(millis * 1_000_000);
	}

	/// Advance time by seconds
	pub fn advance_secs(&self, secs: u64) {
		self.advance_nanos(secs * 1_000_000_000);
	}

	/// Advance time by minutes
	pub fn advance_minutes(&self, minutes: u64) {
		self.advance_secs(minutes * 60);
	}

	/// Advance time by hours
	pub fn advance_hours(&self, hours: u64) {
		self.advance_secs(hours * 3600);
	}

	/// Advance time by days
	pub fn advance_days(&self, days: u64) {
		self.advance_secs(days * 86400);
	}
}

#[derive(Clone)]
enum InstantInner {
	Real(time::Instant),
	Mock {
		captured_nanos: u64,
		clock: MockClock,
	},
}

#[derive(Clone)]
pub struct Instant {
	inner: InstantInner,
}

impl Instant {
	#[inline]
	pub fn elapsed(&self) -> Duration {
		match &self.inner {
			InstantInner::Real(instant) => instant.elapsed(),
			InstantInner::Mock {
				captured_nanos,
				clock,
			} => {
				let now = clock.now_nanos();
				let elapsed_nanos = now.saturating_sub(*captured_nanos);
				Duration::from_nanos(elapsed_nanos)
			}
		}
	}

	#[inline]
	pub fn duration_since(&self, earlier: &Instant) -> Duration {
		match (&self.inner, &earlier.inner) {
			(InstantInner::Real(this), InstantInner::Real(other)) => this.duration_since(*other),
			(
				InstantInner::Mock {
					captured_nanos: this_nanos,
					..
				},
				InstantInner::Mock {
					captured_nanos: other_nanos,
					..
				},
			) => {
				let elapsed = this_nanos.saturating_sub(*other_nanos);
				Duration::from_nanos(elapsed)
			}
			_ => panic!("Cannot compare instants from different clock types"),
		}
	}
}

impl PartialEq for Instant {
	fn eq(&self, other: &Self) -> bool {
		match (&self.inner, &other.inner) {
			(InstantInner::Real(a), InstantInner::Real(b)) => a == b,
			(
				InstantInner::Mock {
					captured_nanos: a,
					..
				},
				InstantInner::Mock {
					captured_nanos: b,
					..
				},
			) => a == b,
			_ => panic!("Cannot compare instants from different clock types"),
		}
	}
}

impl Eq for Instant {}

impl PartialOrd for Instant {
	fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
		Some(self.cmp(other))
	}
}

impl Ord for Instant {
	fn cmp(&self, other: &Self) -> cmp::Ordering {
		match (&self.inner, &other.inner) {
			(InstantInner::Real(a), InstantInner::Real(b)) => a.cmp(b),
			(
				InstantInner::Mock {
					captured_nanos: a,
					..
				},
				InstantInner::Mock {
					captured_nanos: b,
					..
				},
			) => a.cmp(b),
			_ => panic!("Cannot compare instants from different clock types"),
		}
	}
}

impl ops::Add<Duration> for Instant {
	type Output = Instant;

	fn add(self, duration: Duration) -> Instant {
		match self.inner {
			InstantInner::Real(instant) => Instant {
				inner: InstantInner::Real(instant + duration),
			},
			InstantInner::Mock {
				captured_nanos,
				clock,
			} => Instant {
				inner: InstantInner::Mock {
					captured_nanos: captured_nanos.saturating_add(duration.as_nanos() as u64),
					clock,
				},
			},
		}
	}
}

impl ops::Sub for &Instant {
	type Output = Duration;

	fn sub(self, other: &Instant) -> Duration {
		self.duration_since(other)
	}
}

impl fmt::Debug for Instant {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match &self.inner {
			InstantInner::Real(instant) => f.debug_tuple("Instant::Real").field(instant).finish(),
			InstantInner::Mock {
				captured_nanos,
				..
			} => f.debug_tuple("Instant::Mock").field(captured_nanos).finish(),
		}
	}
}