gun/state.rs
1//! State management for conflict resolution
2//!
3//! This module implements Gun's state timestamp system, which is used for conflict
4//! resolution in the HAM (Hypothetical Amnesia Machine) algorithm. State timestamps
5//! are monotonically increasing values that combine system time with a sub-millisecond
6//! counter to ensure uniqueness.
7//!
8//! Based on Gun.js `state.js`. State values are used to determine which version of
9//! data wins in conflicts - higher state always wins.
10//!
11//! ## State Format
12//!
13//! State values are `f64` timestamps in milliseconds with sub-millisecond precision:
14//! - Base: System time in milliseconds since Unix epoch
15//! - Fractional part: Sub-millisecond counter (0.0 to 0.999)
16//! - Ensures monotonicity even with high write rates
17//!
18//! ## Conflict Resolution
19//!
20//! When two peers update the same property simultaneously:
21//! - Compare state timestamps
22//! - Higher state wins
23//! - Merge non-conflicting properties automatically
24
25use serde::{Deserialize, Serialize};
26use serde_json::Value;
27use std::sync::{Arc, Mutex};
28use std::time::{SystemTime, UNIX_EPOCH};
29
30const DRIFT: f64 = 0.0; // Time drift compensation (currently unused)
31const D: f64 = 999.0; // Divisor for sub-millisecond precision
32
33/// State timestamp generator for conflict resolution
34///
35/// Generates monotonically increasing timestamps that combine system time with
36/// a sub-millisecond counter. This ensures uniqueness and proper ordering even
37/// with high write rates.
38///
39/// State values are used throughout Gun to:
40/// - Resolve conflicts (higher state wins)
41/// - Order updates chronologically
42/// - Track when data was last modified
43///
44/// # Thread Safety
45///
46/// `State` is thread-safe and can be shared across threads using `Arc<State>`.
47#[derive(Clone)]
48pub struct State {
49 n: Arc<Mutex<f64>>,
50 last: Arc<Mutex<f64>>,
51}
52
53impl Default for State {
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl State {
60 /// Create a new State generator
61 ///
62 /// Initializes the state system with a counter starting at 0.
63 pub fn new() -> Self {
64 Self {
65 n: Arc::new(Mutex::new(0.0)),
66 last: Arc::new(Mutex::new(f64::NEG_INFINITY)),
67 }
68 }
69
70 /// Generate a new state timestamp
71 /// Returns a timestamp that increases with each call
72 ///
73 /// # Panics
74 /// This function will panic if the system time is before the Unix epoch,
75 /// which should never happen in practice on modern systems.
76 pub fn next(&self) -> f64 {
77 let t = SystemTime::now()
78 .duration_since(UNIX_EPOCH)
79 .expect("System time is before Unix epoch - this should never happen")
80 .as_millis() as f64;
81
82 // std::sync::Mutex::lock() returns Result, handle potential poisoning
83 // Mutex poisoning indicates a panic occurred while holding the lock,
84 // which is a serious bug but we can still recover by using expect()
85 let mut n = self.n.lock().expect("State mutex poisoned - this indicates a serious bug");
86 let mut last = self.last.lock().expect("State mutex poisoned - this indicates a serious bug");
87
88 if *last < t {
89 *n = 0.0;
90 *last = t + DRIFT;
91 return *last;
92 }
93
94 *last = t + (*n / D) + DRIFT;
95 *n += 1.0;
96 *last
97 }
98
99 /// Get the state timestamp for a specific key on a node
100 ///
101 /// Retrieves the state value stored in the node's metadata for the given key.
102 /// Returns `None` if the key doesn't exist or has no state recorded.
103 ///
104 /// # Arguments
105 /// * `node` - Optional reference to the node
106 /// * `key` - The property key to check
107 ///
108 /// # Returns
109 /// The state timestamp for the key, or `None` if not found.
110 ///
111 /// # Example
112 ///
113 /// ```rust,no_run
114 /// use gun::state::{State, Node};
115 ///
116 /// let node = Node::new();
117 /// let state_value = State::is(&Some(node), "my_key");
118 /// ```
119 pub fn is(node: &Option<Node>, key: &str) -> Option<f64> {
120 if let Some(node) = node {
121 if let Some(Value::Object(states)) = node.meta.get(">") {
122 if let Some(state) = states.get(key) {
123 if let Some(state_num) = state.as_f64() {
124 return Some(state_num);
125 }
126 }
127 }
128 }
129 None
130 }
131
132 /// Update a node with state information for a key
133 ///
134 /// This method sets the state timestamp and optionally the soul and data for a node.
135 /// It's called whenever data is updated to record when the change occurred.
136 ///
137 /// # Arguments
138 /// * `node` - Mutable reference to the node to update
139 /// * `key` - Optional property key (if `None`, only soul is set)
140 /// * `state` - Optional state timestamp (generated by [`next`](Self::next))
141 /// * `value` - Optional value to store in the node's data
142 /// * `soul` - Optional soul identifier for the node
143 ///
144 /// # Returns
145 /// A clone of the updated node (for convenience in chaining).
146 ///
147 /// # Example
148 ///
149 /// ```rust,no_run
150 /// use gun::state::{State, Node};
151 /// use serde_json::json;
152 ///
153 /// let mut node = Node::with_soul("my_soul".to_string());
154 /// let state = State::new();
155 /// let timestamp = state.next();
156 ///
157 /// State::ify(
158 /// &mut node,
159 /// Some("my_key"),
160 /// Some(timestamp),
161 /// Some(json!("my_value")),
162 /// Some("my_soul"),
163 /// );
164 /// ```
165 pub fn ify(
166 node: &mut Node,
167 key: Option<&str>,
168 state: Option<f64>,
169 value: Option<serde_json::Value>,
170 soul: Option<&str>,
171 ) -> Node {
172 if let Some(soul) = soul {
173 node.meta
174 .insert("#".to_string(), serde_json::Value::String(soul.to_string()));
175 }
176
177 if let Some(key) = key {
178 if key != "_" {
179 let states = node
180 .meta
181 .entry(">".to_string())
182 .or_insert_with(|| serde_json::Value::Object(serde_json::Map::new()));
183
184 if let Some(state_num) = state {
185 if let serde_json::Value::Object(ref mut map) = states {
186 // from_f64 can fail if state_num is NaN or Infinity
187 // In practice, state_num should always be a valid finite number
188 if let Some(number) = serde_json::Number::from_f64(state_num) {
189 map.insert(key.to_string(), serde_json::Value::Number(number));
190 } else {
191 // Log error but continue - this should never happen in practice
192 tracing::error!("Invalid state number: {} (NaN or Infinity)", state_num);
193 }
194 }
195 }
196
197 if let Some(val) = value {
198 node.data.insert(key.to_string(), val);
199 }
200 }
201 }
202
203 node.clone()
204 }
205}
206
207/// A node in the Gun graph
208///
209/// Nodes are the fundamental data structure in Gun. Each node has:
210/// - **data**: Key-value pairs storing the actual data
211/// - **meta**: Metadata including the soul (`#`) and state timestamps (`>`)
212///
213/// Nodes are identified by their "soul" (stored in `meta["#"]`), which is a unique
214/// identifier generated by [`GunCore::uuid`](crate::core::GunCore::uuid).
215///
216/// State information is stored in `meta[">"]` as a map from key to timestamp.
217///
218/// # Example
219///
220/// ```rust,no_run
221/// use gun::state::Node;
222/// use serde_json::json;
223///
224/// let mut node = Node::with_soul("user_123".to_string());
225/// node.data.insert("name".to_string(), json!("Alice"));
226/// node.data.insert("age".to_string(), json!(30));
227/// ```
228#[derive(Clone, Debug, Serialize, Deserialize)]
229pub struct Node {
230 pub data: serde_json::Map<String, serde_json::Value>,
231 pub meta: serde_json::Map<String, serde_json::Value>,
232}
233
234impl Node {
235 /// Create a new empty node without a soul
236 ///
237 /// The soul will need to be set later using `meta.insert("#", ...)`.
238 pub fn new() -> Self {
239 Self {
240 data: serde_json::Map::new(),
241 meta: serde_json::Map::new(),
242 }
243 }
244
245 /// Create a new node with a specific soul
246 ///
247 /// # Arguments
248 /// * `soul` - The unique identifier for this node
249 ///
250 /// # Example
251 ///
252 /// ```rust,no_run
253 /// use gun::state::Node;
254 ///
255 /// let node = Node::with_soul("user_123".to_string());
256 /// ```
257 pub fn with_soul(soul: String) -> Self {
258 let mut meta = serde_json::Map::new();
259 meta.insert("#".to_string(), serde_json::Value::String(soul));
260 Self {
261 data: serde_json::Map::new(),
262 meta,
263 }
264 }
265
266 /// Get the soul (unique identifier) of this node
267 ///
268 /// Returns `None` if the node doesn't have a soul set.
269 ///
270 /// # Returns
271 /// The soul string, or `None` if not set.
272 ///
273 /// # Example
274 ///
275 /// ```rust,no_run
276 /// use gun::state::Node;
277 ///
278 /// let node = Node::with_soul("user_123".to_string());
279 /// assert_eq!(node.get_soul(), Some("user_123".to_string()));
280 /// ```
281 pub fn get_soul(&self) -> Option<String> {
282 self.meta
283 .get("#")
284 .and_then(|v| v.as_str())
285 .map(|s| s.to_string())
286 }
287}
288
289impl Default for Node {
290 fn default() -> Self {
291 Self::new()
292 }
293}