vortex-core 0.1.0

Core types and deterministic scheduler for Vortex simulation engine
Documentation
//! Trait definitions for the Vortex I/O boundary.
//!
//! These traits define the interface between your application and the outside world.
//! In production, you implement them with real TCP, real disk, real clock.
//! In simulation, Vortex swaps in deterministic implementations.

use crate::NodeId;

/// Error type for Vortex I/O operations.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VortexError {
    /// Network error (connection refused, timeout, etc.).
    Network(String),
    /// Storage error (disk full, corruption, I/O failure).
    Storage(String),
    /// Operation timed out.
    Timeout,
    /// Node is not available (crashed, partitioned).
    Unavailable,
    /// Custom application error.
    Custom(String),
}

impl std::fmt::Display for VortexError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            VortexError::Network(s) => write!(f, "network error: {s}"),
            VortexError::Storage(s) => write!(f, "storage error: {s}"),
            VortexError::Timeout => write!(f, "timeout"),
            VortexError::Unavailable => write!(f, "unavailable"),
            VortexError::Custom(s) => write!(f, "{s}"),
        }
    }
}

impl std::error::Error for VortexError {}

pub type Result<T> = std::result::Result<T, VortexError>;

/// Network I/O boundary trait.
///
/// In production: real TCP/UDP sockets.
/// In simulation: `SimNetwork` with configurable latency, drops, partitions.
pub trait VortexNetwork {
    /// Send a message to a specific node.
    fn send(&mut self, to: NodeId, payload: Vec<u8>) -> Result<()>;

    /// Receive the next pending message, if any.
    fn recv(&mut self) -> Option<(NodeId, Vec<u8>)>;

    /// Broadcast a message to all known nodes.
    fn broadcast(&mut self, nodes: &[NodeId], payload: Vec<u8>) -> Result<()> {
        for &node in nodes {
            self.send(node, payload.clone())?;
        }
        Ok(())
    }
}

/// Persistent storage boundary trait.
///
/// In production: real filesystem, RocksDB, SQLite, etc.
/// In simulation: `SimStorage` with crash/corruption fault injection.
pub trait VortexStorage {
    /// Get the value for a key.
    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;

    /// Put a key-value pair.
    fn put(&mut self, key: &[u8], value: &[u8]) -> Result<()>;

    /// Delete a key.
    fn delete(&mut self, key: &[u8]) -> Result<()>;

    /// Scan a range of keys [start, end).
    fn scan(&self, start: &[u8], end: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>>;

    /// Apply a batch of operations atomically.
    fn write_batch(&mut self, ops: Vec<StorageOp>) -> Result<()>;

    /// Flush pending writes to durable storage.
    fn flush(&mut self) -> Result<()>;

    /// Create a point-in-time snapshot.
    fn snapshot(&self) -> Result<Vec<(Vec<u8>, Vec<u8>)>>;
}

/// A single storage operation (for batch writes).
#[derive(Debug, Clone)]
pub enum StorageOp {
    Put { key: Vec<u8>, value: Vec<u8> },
    Delete { key: Vec<u8> },
}

/// Clock boundary trait.
///
/// In production: `SystemTime::now()`.
/// In simulation: `SimClock` with virtual time, skew, and drift injection.
pub trait VortexClock {
    /// Get the current time in microseconds.
    fn now_us(&self) -> u64;

    /// Sleep for the given duration (in microseconds).
    /// In simulation, this schedules a timer event.
    fn sleep_us(&self, duration_us: u64);
}

/// Task/event scheduling trait.
///
/// In production: tokio timers, OS timers, etc.
/// In simulation: `SimScheduler` events.
pub trait VortexScheduler {
    /// Schedule a callback after `delay_ticks` ticks. Returns a handle.
    fn schedule(&mut self, delay_ticks: u64, tag: String) -> u64;

    /// Cancel a scheduled event by handle.
    fn cancel(&mut self, handle: u64) -> bool;
}