absurder_sql/storage/
optimistic_updates.rs1use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14
15#[derive(Clone, Debug, Serialize, Deserialize)]
17pub struct OptimisticWrite {
18 pub id: String,
20 pub sql: String,
22 pub timestamp: f64,
24 pub status: OptimisticWriteStatus,
26}
27
28#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
30pub enum OptimisticWriteStatus {
31 Pending,
33 Confirmed,
35 Failed,
37}
38
39pub struct OptimisticUpdatesManager {
41 enabled: bool,
43 pending_writes: HashMap<String, OptimisticWrite>,
45}
46
47impl OptimisticUpdatesManager {
48 pub fn new() -> Self {
50 Self {
51 enabled: false,
52 pending_writes: HashMap::new(),
53 }
54 }
55
56 pub fn set_enabled(&mut self, enabled: bool) {
58 self.enabled = enabled;
59 if !enabled {
60 self.pending_writes.clear();
62 }
63 }
64
65 pub fn is_enabled(&self) -> bool {
67 self.enabled
68 }
69
70 pub fn track_write(&mut self, sql: String) -> String {
72 if !self.enabled {
73 return String::new();
74 }
75
76 let id = Self::generate_id();
78
79 #[cfg(target_arch = "wasm32")]
80 let timestamp = js_sys::Date::now();
81
82 #[cfg(not(target_arch = "wasm32"))]
83 let timestamp = std::time::SystemTime::now()
84 .duration_since(std::time::UNIX_EPOCH)
85 .unwrap()
86 .as_secs_f64() * 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}