kiromi-ai-memory 0.2.2

Local-first multi-tenant memory store engine: Markdown/text content on object storage, metadata in SQLite, plugin-shaped embedder/storage/metadata, hybrid text+vector search.
Documentation
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! Misc utilities used across modules.

use std::sync::Arc;
use std::sync::atomic::{AtomicI64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};

/// Wall-clock abstraction. Production uses `SystemClock`; tests use `MockClock`.
pub trait Clock: Send + Sync + std::fmt::Debug {
    /// Unix milliseconds.
    fn now_ms(&self) -> i64;
}

/// Real system clock.
#[derive(Debug, Default, Clone, Copy)]
pub struct SystemClock;

impl Clock for SystemClock {
    fn now_ms(&self) -> i64 {
        let dur = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_default();
        i64::try_from(dur.as_millis()).unwrap_or(i64::MAX)
    }
}

/// Mock clock backed by an atomic. Tests advance it explicitly.
#[derive(Debug, Clone)]
pub struct MockClock {
    inner: Arc<AtomicI64>,
}

impl MockClock {
    /// Create with an initial timestamp.
    #[must_use]
    pub fn new(start_ms: i64) -> Self {
        Self {
            inner: Arc::new(AtomicI64::new(start_ms)),
        }
    }

    /// Advance the clock by `delta_ms`.
    pub fn advance(&self, delta_ms: i64) {
        self.inner.fetch_add(delta_ms, Ordering::SeqCst);
    }
}

impl Clock for MockClock {
    fn now_ms(&self) -> i64 {
        self.inner.load(Ordering::SeqCst)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn mock_advances() {
        let c = MockClock::new(100);
        assert_eq!(c.now_ms(), 100);
        c.advance(42);
        assert_eq!(c.now_ms(), 142);
    }

    #[test]
    fn system_clock_is_positive() {
        assert!(SystemClock.now_ms() > 0);
    }
}