absurder_sql/storage/
optimistic_updates.rs1use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14#[derive(Clone, Debug, Serialize, Deserialize)]
16pub struct OptimisticWrite {
17 pub id: String,
19 pub sql: String,
21 pub timestamp: f64,
23 pub status: OptimisticWriteStatus,
25}
26
27#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
29pub enum OptimisticWriteStatus {
30 Pending,
32 Confirmed,
34 Failed,
36}
37
38pub struct OptimisticUpdatesManager {
40 enabled: bool,
42 pending_writes: HashMap<String, OptimisticWrite>,
44}
45
46impl OptimisticUpdatesManager {
47 pub fn new() -> Self {
49 Self {
50 enabled: false,
51 pending_writes: HashMap::new(),
52 }
53 }
54
55 pub fn set_enabled(&mut self, enabled: bool) {
57 self.enabled = enabled;
58 if !enabled {
59 self.pending_writes.clear();
61 }
62 }
63
64 pub fn is_enabled(&self) -> bool {
66 self.enabled
67 }
68
69 pub fn track_write(&mut self, sql: String) -> String {
71 if !self.enabled {
72 return String::new();
73 }
74
75 let id = Self::generate_id();
77
78 #[cfg(target_arch = "wasm32")]
79 let timestamp = js_sys::Date::now();
80
81 #[cfg(not(target_arch = "wasm32"))]
82 let timestamp = std::time::SystemTime::now()
83 .duration_since(std::time::UNIX_EPOCH)
84 .unwrap()
85 .as_secs_f64()
86 * 1000.0;
87
88 let write = OptimisticWrite {
89 id: id.clone(),
90 sql,
91 timestamp,
92 status: OptimisticWriteStatus::Pending,
93 };
94
95 self.pending_writes.insert(id.clone(), write);
96
97 #[cfg(target_arch = "wasm32")]
98 web_sys::console::log_1(&format!("Tracked optimistic write: {}", id).into());
99
100 id
101 }
102
103 pub fn confirm_write(&mut self, id: &str) {
105 if let Some(write) = self.pending_writes.get_mut(id) {
106 write.status = OptimisticWriteStatus::Confirmed;
107
108 #[cfg(target_arch = "wasm32")]
109 web_sys::console::log_1(&format!("Confirmed optimistic write: {}", id).into());
110 }
111 }
112
113 pub fn fail_write(&mut self, id: &str) {
115 if let Some(write) = self.pending_writes.get_mut(id) {
116 write.status = OptimisticWriteStatus::Failed;
117
118 #[cfg(target_arch = "wasm32")]
119 web_sys::console::log_1(&format!("Failed optimistic write: {}", id).into());
120 }
121 }
122
123 pub fn remove_write(&mut self, id: &str) {
125 self.pending_writes.remove(id);
126 }
127
128 pub fn clear_all(&mut self) {
130 #[cfg(target_arch = "wasm32")]
131 {
132 let count = self.pending_writes.len();
133 web_sys::console::log_1(&format!("Cleared {} optimistic writes", count).into());
134 }
135
136 self.pending_writes.clear();
137 }
138
139 pub fn get_pending_count(&self) -> usize {
141 self.pending_writes
142 .values()
143 .filter(|w| w.status == OptimisticWriteStatus::Pending)
144 .count()
145 }
146
147 pub fn get_pending_writes(&self) -> Vec<&OptimisticWrite> {
149 self.pending_writes
150 .values()
151 .filter(|w| w.status == OptimisticWriteStatus::Pending)
152 .collect()
153 }
154
155 fn generate_id() -> String {
157 #[cfg(target_arch = "wasm32")]
158 {
159 let timestamp = js_sys::Date::now();
160 let random = js_sys::Math::random();
161 format!("opt_{}_{}", timestamp as u64, (random * 1000000.0) as u64)
162 }
163
164 #[cfg(not(target_arch = "wasm32"))]
165 {
166 use std::time::SystemTime;
167 let timestamp = SystemTime::now()
168 .duration_since(std::time::UNIX_EPOCH)
169 .unwrap()
170 .as_nanos();
171 use std::sync::atomic::{AtomicU64, Ordering};
173 static COUNTER: AtomicU64 = AtomicU64::new(0);
174 let counter = COUNTER.fetch_add(1, Ordering::SeqCst);
175 format!("opt_{}_{}", timestamp, counter)
176 }
177 }
178}
179
180impl Default for OptimisticUpdatesManager {
181 fn default() -> Self {
182 Self::new()
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_enable_disable() {
192 let mut manager = OptimisticUpdatesManager::new();
193 assert!(!manager.is_enabled());
194
195 manager.set_enabled(true);
196 assert!(manager.is_enabled());
197
198 manager.set_enabled(false);
199 assert!(!manager.is_enabled());
200 }
201
202 #[test]
203 fn test_track_write() {
204 let mut manager = OptimisticUpdatesManager::new();
205 manager.set_enabled(true);
206
207 let id = manager.track_write("INSERT INTO test VALUES (1)".to_string());
208 assert!(!id.is_empty());
209 assert_eq!(manager.get_pending_count(), 1);
210 }
211
212 #[test]
213 fn test_clear_all() {
214 let mut manager = OptimisticUpdatesManager::new();
215 manager.set_enabled(true);
216
217 manager.track_write("INSERT INTO test VALUES (1)".to_string());
218 manager.track_write("INSERT INTO test VALUES (2)".to_string());
219 assert_eq!(manager.get_pending_count(), 2);
220
221 manager.clear_all();
222 assert_eq!(manager.get_pending_count(), 0);
223 }
224}