gun-rs 1.0.4

A realtime, decentralized, offline-first, graph data synchronization engine (Rust port)
Documentation
//! State management for conflict resolution
//!
//! This module implements Gun's state timestamp system, which is used for conflict
//! resolution in the HAM (Hypothetical Amnesia Machine) algorithm. State timestamps
//! are monotonically increasing values that combine system time with a sub-millisecond
//! counter to ensure uniqueness.
//!
//! Based on Gun.js `state.js`. State values are used to determine which version of
//! data wins in conflicts - higher state always wins.
//!
//! ## State Format
//!
//! State values are `f64` timestamps in milliseconds with sub-millisecond precision:
//! - Base: System time in milliseconds since Unix epoch
//! - Fractional part: Sub-millisecond counter (0.0 to 0.999)
//! - Ensures monotonicity even with high write rates
//!
//! ## Conflict Resolution
//!
//! When two peers update the same property simultaneously:
//! - Compare state timestamps
//! - Higher state wins
//! - Merge non-conflicting properties automatically

use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::sync::{Arc, Mutex};
use std::time::{SystemTime, UNIX_EPOCH};

const DRIFT: f64 = 0.0; // Time drift compensation (currently unused)
const D: f64 = 999.0;   // Divisor for sub-millisecond precision

/// State timestamp generator for conflict resolution
///
/// Generates monotonically increasing timestamps that combine system time with
/// a sub-millisecond counter. This ensures uniqueness and proper ordering even
/// with high write rates.
///
/// State values are used throughout Gun to:
/// - Resolve conflicts (higher state wins)
/// - Order updates chronologically
/// - Track when data was last modified
///
/// # Thread Safety
///
/// `State` is thread-safe and can be shared across threads using `Arc<State>`.
#[derive(Clone)]
pub struct State {
    n: Arc<Mutex<f64>>,
    last: Arc<Mutex<f64>>,
}

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

impl State {
    /// Create a new State generator
    ///
    /// Initializes the state system with a counter starting at 0.
    pub fn new() -> Self {
        Self {
            n: Arc::new(Mutex::new(0.0)),
            last: Arc::new(Mutex::new(f64::NEG_INFINITY)),
        }
    }

    /// Generate a new state timestamp
    /// Returns a timestamp that increases with each call
    /// 
    /// # Panics
    /// This function will panic if the system time is before the Unix epoch,
    /// which should never happen in practice on modern systems.
    pub fn next(&self) -> f64 {
        let t = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("System time is before Unix epoch - this should never happen")
            .as_millis() as f64;

        // std::sync::Mutex::lock() returns Result, handle potential poisoning
        // Mutex poisoning indicates a panic occurred while holding the lock,
        // which is a serious bug but we can still recover by using expect()
        let mut n = self.n.lock().expect("State mutex poisoned - this indicates a serious bug");
        let mut last = self.last.lock().expect("State mutex poisoned - this indicates a serious bug");

        if *last < t {
            *n = 0.0;
            *last = t + DRIFT;
            return *last;
        }

        *last = t + (*n / D) + DRIFT;
        *n += 1.0;
        *last
    }

    /// Get the state timestamp for a specific key on a node
    ///
    /// Retrieves the state value stored in the node's metadata for the given key.
    /// Returns `None` if the key doesn't exist or has no state recorded.
    ///
    /// # Arguments
    /// * `node` - Optional reference to the node
    /// * `key` - The property key to check
    ///
    /// # Returns
    /// The state timestamp for the key, or `None` if not found.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use gun::state::{State, Node};
    ///
    /// let node = Node::new();
    /// let state_value = State::is(&Some(node), "my_key");
    /// ```
    pub fn is(node: &Option<Node>, key: &str) -> Option<f64> {
        if let Some(node) = node {
            if let Some(Value::Object(states)) = node.meta.get(">") {
                if let Some(state) = states.get(key) {
                    if let Some(state_num) = state.as_f64() {
                        return Some(state_num);
                    }
                }
            }
        }
        None
    }

    /// Update a node with state information for a key
    ///
    /// This method sets the state timestamp and optionally the soul and data for a node.
    /// It's called whenever data is updated to record when the change occurred.
    ///
    /// # Arguments
    /// * `node` - Mutable reference to the node to update
    /// * `key` - Optional property key (if `None`, only soul is set)
    /// * `state` - Optional state timestamp (generated by [`next`](Self::next))
    /// * `value` - Optional value to store in the node's data
    /// * `soul` - Optional soul identifier for the node
    ///
    /// # Returns
    /// A clone of the updated node (for convenience in chaining).
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use gun::state::{State, Node};
    /// use serde_json::json;
    ///
    /// let mut node = Node::with_soul("my_soul".to_string());
    /// let state = State::new();
    /// let timestamp = state.next();
    ///
    /// State::ify(
    ///     &mut node,
    ///     Some("my_key"),
    ///     Some(timestamp),
    ///     Some(json!("my_value")),
    ///     Some("my_soul"),
    /// );
    /// ```
    pub fn ify(
        node: &mut Node,
        key: Option<&str>,
        state: Option<f64>,
        value: Option<serde_json::Value>,
        soul: Option<&str>,
    ) -> Node {
        if let Some(soul) = soul {
            node.meta
                .insert("#".to_string(), serde_json::Value::String(soul.to_string()));
        }

        if let Some(key) = key {
            if key != "_" {
                let states = node
                    .meta
                    .entry(">".to_string())
                    .or_insert_with(|| serde_json::Value::Object(serde_json::Map::new()));

                if let Some(state_num) = state {
                    if let serde_json::Value::Object(ref mut map) = states {
                    // from_f64 can fail if state_num is NaN or Infinity
                    // In practice, state_num should always be a valid finite number
                    if let Some(number) = serde_json::Number::from_f64(state_num) {
                        map.insert(key.to_string(), serde_json::Value::Number(number));
                    } else {
                        // Log error but continue - this should never happen in practice
                        tracing::error!("Invalid state number: {} (NaN or Infinity)", state_num);
                    }
                    }
                }

                if let Some(val) = value {
                    node.data.insert(key.to_string(), val);
                }
            }
        }

        node.clone()
    }
}

/// A node in the Gun graph
///
/// Nodes are the fundamental data structure in Gun. Each node has:
/// - **data**: Key-value pairs storing the actual data
/// - **meta**: Metadata including the soul (`#`) and state timestamps (`>`)
///
/// Nodes are identified by their "soul" (stored in `meta["#"]`), which is a unique
/// identifier generated by [`GunCore::uuid`](crate::core::GunCore::uuid).
///
/// State information is stored in `meta[">"]` as a map from key to timestamp.
///
/// # Example
///
/// ```rust,no_run
/// use gun::state::Node;
/// use serde_json::json;
///
/// let mut node = Node::with_soul("user_123".to_string());
/// node.data.insert("name".to_string(), json!("Alice"));
/// node.data.insert("age".to_string(), json!(30));
/// ```
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Node {
    pub data: serde_json::Map<String, serde_json::Value>,
    pub meta: serde_json::Map<String, serde_json::Value>,
}

impl Node {
    /// Create a new empty node without a soul
    ///
    /// The soul will need to be set later using `meta.insert("#", ...)`.
    pub fn new() -> Self {
        Self {
            data: serde_json::Map::new(),
            meta: serde_json::Map::new(),
        }
    }

    /// Create a new node with a specific soul
    ///
    /// # Arguments
    /// * `soul` - The unique identifier for this node
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use gun::state::Node;
    ///
    /// let node = Node::with_soul("user_123".to_string());
    /// ```
    pub fn with_soul(soul: String) -> Self {
        let mut meta = serde_json::Map::new();
        meta.insert("#".to_string(), serde_json::Value::String(soul));
        Self {
            data: serde_json::Map::new(),
            meta,
        }
    }

    /// Get the soul (unique identifier) of this node
    ///
    /// Returns `None` if the node doesn't have a soul set.
    ///
    /// # Returns
    /// The soul string, or `None` if not set.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use gun::state::Node;
    ///
    /// let node = Node::with_soul("user_123".to_string());
    /// assert_eq!(node.get_soul(), Some("user_123".to_string()));
    /// ```
    pub fn get_soul(&self) -> Option<String> {
        self.meta
            .get("#")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string())
    }
}

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