1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::cmp::Ordering;
4use std::collections::HashMap;
5use std::convert::TryInto;
6use std::error::Error;
7use std::fmt;
8use std::hash::{Hash, Hasher};
9use std::path::{Path, PathBuf};
10use uuid::Uuid;
11
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
13pub enum FieldType {
14 String,
15 Int,
16 Uuid,
17 Bool,
18 Float,
19 Array,
20 Object,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
24pub struct FieldDefinition {
25 pub field_type: FieldType,
26 pub unique: bool,
27 pub indexed: bool,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct Collection {
32 pub name: String,
33 pub fields: HashMap<String, FieldDefinition>,
34}
35
36#[derive(Clone, Serialize, Deserialize)]
37pub struct Document {
38 pub id: String,
39 pub data: HashMap<String, Value>,
40}
41
42impl Default for Document {
43 fn default() -> Self {
44 Self {
45 id: Uuid::new_v4().to_string(),
46 data: HashMap::new(),
47 }
48 }
49}
50
51impl Document {
52 pub fn new() -> Self {
53 Self::default()
54 }
55}
56
57impl fmt::Display for Document {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(f, "{{ ")?;
60 let mut first = true;
61 for (key, value) in &self.data {
62 if !first {
63 write!(f, ", ")?;
64 }
65 write!(f, "\"{}\": {}", key, value)?;
66 first = false;
67 }
68 write!(f, " }}")
69 }
70}
71
72impl fmt::Debug for Document {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 fmt::Display::fmt(self, f)
75 }
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub enum Value {
80 Null,
81 String(String),
82 Int(i64),
83 Float(f64),
84 Bool(bool),
85 Array(Vec<Value>),
86 Object(HashMap<String, Value>),
87 Uuid(Uuid),
88}
89
90impl Hash for Value {
92 fn hash<H: Hasher>(&self, state: &mut H) {
93 match self {
94 Value::Null => 0.hash(state),
95 Value::String(s) => s.hash(state),
96 Value::Int(i) => i.hash(state),
97 Value::Float(f) => {
98 f.to_bits().hash(state)
100 }
101 Value::Bool(b) => b.hash(state),
102 Value::Array(arr) => arr.hash(state),
103 Value::Object(map) => {
104 let mut keys: Vec<_> = map.keys().collect();
106 keys.sort();
107 for key in keys {
108 key.hash(state);
109 map.get(key).unwrap().hash(state);
110 }
111 }
112 Value::Uuid(u) => u.hash(state),
113 }
114 }
115}
116
117impl PartialEq for Value {
118 fn eq(&self, other: &Self) -> bool {
119 match (self, other) {
120 (Value::Null, Value::Null) => true,
121 (Value::String(a), Value::String(b)) => a == b,
122 (Value::Int(a), Value::Int(b)) => a == b,
123 (Value::Float(a), Value::Float(b)) => a.to_bits() == b.to_bits(),
124 (Value::Bool(a), Value::Bool(b)) => a == b,
125 (Value::Array(a), Value::Array(b)) => a == b,
126 (Value::Object(a), Value::Object(b)) => a == b,
127 (Value::Uuid(a), Value::Uuid(b)) => a == b,
128 _ => false,
129 }
130 }
131}
132
133impl Eq for Value {}
134
135impl fmt::Display for Value {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 match self {
139 Value::String(s) => write!(f, "\"{}\"", s),
140 Value::Int(i) => write!(f, "{}", i),
141 Value::Float(fl) => write!(f, "{}", fl),
142 Value::Bool(b) => write!(f, "{}", b),
143 Value::Array(arr) => {
144 let items: Vec<String> = arr.iter().map(|v| v.to_string()).collect();
145 write!(f, "[{}]", items.join(", "))
146 }
147 Value::Object(obj) => {
148 let items: Vec<String> = obj
149 .iter()
150 .map(|(k, v)| format!("\"{}\": {}", k, v))
151 .collect();
152 write!(f, "{{{}}}", items.join(", "))
153 }
154 Value::Uuid(u) => write!(f, "\"{}\"", u),
155 Value::Null => write!(f, "null"),
156 }
157 }
158}
159
160fn type_rank(v: &Value) -> u8 {
162 match v {
163 Value::Null => 0,
164 Value::Bool(_) => 1,
165 Value::Int(_) => 2,
166 Value::Float(_) => 3,
167 Value::String(_) => 4,
168 Value::Uuid(_) => 5,
169 Value::Array(_) => 6,
170 Value::Object(_) => 7,
171 }
172}
173
174impl PartialOrd for Value {
175 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
176 let self_rank = type_rank(self);
177 let other_rank = type_rank(other);
178
179 if self_rank != other_rank {
180 return Some(self_rank.cmp(&other_rank));
181 }
182
183 match (self, other) {
184 (Value::String(a), Value::String(b)) => a.partial_cmp(b),
185 (Value::Int(a), Value::Int(b)) => a.partial_cmp(b),
186 (Value::Float(a), Value::Float(b)) => a.partial_cmp(b),
187 (Value::Bool(a), Value::Bool(b)) => a.partial_cmp(b),
188 (Value::Array(a), Value::Array(b)) => a.partial_cmp(b),
189 (Value::Uuid(a), Value::Uuid(b)) => a.partial_cmp(b),
190 (Value::Object(_), Value::Object(_)) => Some(Ordering::Equal),
191 (Value::Null, Value::Null) => Some(Ordering::Equal),
192 _ => None,
193 }
194 }
195}
196
197impl Ord for Value {
198 fn cmp(&self, other: &Self) -> Ordering {
199 self.partial_cmp(other).unwrap()
200 }
201}
202
203impl From<i64> for Value {
205 fn from(v: i64) -> Self {
206 Value::Int(v)
207 }
208}
209
210impl From<i32> for Value {
211 fn from(v: i32) -> Self {
212 Value::Int(v as i64)
213 }
214}
215
216impl From<&str> for Value {
217 fn from(v: &str) -> Self {
218 Value::String(v.to_string())
219 }
220}
221
222impl From<String> for Value {
223 fn from(v: String) -> Self {
224 Value::String(v)
225 }
226}
227
228impl From<bool> for Value {
229 fn from(v: bool) -> Self {
230 Value::Bool(v)
231 }
232}
233
234impl From<f64> for Value {
235 fn from(v: f64) -> Self {
236 Value::Float(v)
237 }
238}
239
240impl From<Vec<Value>> for Value {
241 fn from(v: Vec<Value>) -> Self {
242 Value::Array(v)
243 }
244}
245
246impl From<HashMap<String, Value>> for Value {
247 fn from(v: HashMap<String, Value>) -> Self {
248 Value::Object(v)
249 }
250}
251
252impl From<Uuid> for Value {
253 fn from(v: Uuid) -> Self {
254 Value::Uuid(v)
255 }
256}
257
258impl Value {
260 pub fn as_str(&self) -> Option<&str> {
261 if let Value::String(s) = self {
262 Some(s)
263 } else {
264 None
265 }
266 }
267
268 pub fn as_bool(&self) -> Option<bool> {
269 if let Value::Bool(b) = self {
270 Some(*b)
271 } else {
272 None
273 }
274 }
275
276 pub fn as_i64(&self) -> Option<i64> {
277 if let Value::Int(i) = self {
278 Some(*i)
279 } else {
280 None
281 }
282 }
283
284 pub fn as_f64(&self) -> Option<f64> {
285 if let Value::Float(f) = self {
286 Some(*f)
287 } else {
288 None
289 }
290 }
291
292 pub fn as_array(&self) -> Option<&Vec<Value>> {
293 if let Value::Array(arr) = self {
294 Some(arr)
295 } else {
296 None
297 }
298 }
299
300 pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
301 if let Value::Object(obj) = self {
302 Some(obj)
303 } else {
304 None
305 }
306 }
307
308 pub fn as_uuid(&self) -> Option<Uuid> {
309 match self {
310 Value::Uuid(u) => Some(*u),
311 Value::String(s) => Uuid::parse_str(s).ok(),
312 _ => None,
313 }
314 }
315
316 pub fn as_datetime(&self) -> Option<DateTime<Utc>> {
317 match self {
318 Value::String(s) => DateTime::parse_from_rfc3339(s)
319 .ok()
320 .map(|dt| dt.with_timezone(&Utc)),
321 _ => None,
322 }
323 }
324
325 pub fn to_safe_string(&self) -> Option<String> {
327 match self {
328 Value::String(s) => Some(s.clone()),
329 Value::Int(i) => Some(i.to_string()),
330 Value::Bool(b) => Some(b.to_string()),
331 Value::Float(f) => Some(f.to_string()),
332 Value::Uuid(u) => Some(u.to_string()),
333 _ => None,
334 }
335 }
336
337 pub fn as_i32(&self) -> Option<i32> {
339 self.as_i64().and_then(|i| i.try_into().ok())
340 }
341}
342
343pub fn required_str<'a>(
348 map: &'a HashMap<String, Value>,
349 key: &str,
350) -> Result<&'a str, Box<dyn Error>> {
351 map.get(key)
352 .and_then(|v| v.as_str())
353 .ok_or_else(|| format!("Missing or invalid '{}' (str)", key).into())
354}
355
356pub fn optional_str(map: &HashMap<String, Value>, key: &str) -> Option<String> {
357 map.get(key).and_then(|v| v.as_str()).map(|s| s.to_string())
358}
359
360pub fn required_uuid(map: &HashMap<String, Value>, key: &str) -> Result<Uuid, Box<dyn Error>> {
361 map.get(key)
362 .and_then(|v| v.as_uuid())
363 .ok_or_else(|| format!("Missing or invalid '{}' (uuid)", key).into())
364}
365
366pub fn optional_uuid(map: &HashMap<String, Value>, key: &str) -> Option<Uuid> {
367 map.get(key).and_then(|v| v.as_uuid())
368}
369
370pub fn required_i64(map: &HashMap<String, Value>, key: &str) -> Result<i64, Box<dyn Error>> {
371 map.get(key)
372 .and_then(|v| v.as_i64())
373 .ok_or_else(|| format!("Missing or invalid '{}' (i64)", key).into())
374}
375
376pub fn optional_i64(map: &HashMap<String, Value>, key: &str) -> Option<i64> {
377 map.get(key).and_then(|v| v.as_i64())
378}
379
380pub fn required_bool(map: &HashMap<String, Value>, key: &str) -> Result<bool, Box<dyn Error>> {
381 map.get(key)
382 .and_then(|v| v.as_bool())
383 .ok_or_else(|| format!("Missing or invalid '{}' (bool)", key).into())
384}
385
386pub fn optional_bool(map: &HashMap<String, Value>, key: &str) -> Option<bool> {
387 map.get(key).and_then(|v| v.as_bool())
388}
389
390pub fn required_datetime(
391 map: &HashMap<String, Value>,
392 key: &str,
393) -> Result<DateTime<Utc>, Box<dyn Error>> {
394 map.get(key)
395 .and_then(|v| v.as_datetime())
396 .ok_or_else(|| format!("Missing or invalid '{}' (datetime)", key).into())
397}
398
399pub fn optional_datetime(map: &HashMap<String, Value>, key: &str) -> Option<DateTime<Utc>> {
400 map.get(key).and_then(|v| v.as_datetime())
401}
402
403pub fn array_of_strings(map: &HashMap<String, Value>, key: &str) -> Vec<String> {
405 map.get(key)
406 .and_then(|v| v.as_array())
407 .map(|arr| {
408 arr.iter()
409 .filter_map(|v| v.as_str().map(|s| s.to_string()))
410 .collect()
411 })
412 .unwrap_or_default()
413}
414
415#[derive(Debug, Clone)]
417pub struct AuroraConfig {
418 pub db_path: PathBuf,
420 pub create_dirs: bool, pub hot_cache_size_mb: usize,
424 pub hot_cache_cleanup_interval_secs: u64,
425 pub eviction_policy: crate::storage::EvictionPolicy,
426
427 pub cold_cache_capacity_mb: usize,
429 pub cold_flush_interval_ms: Option<u64>,
430 pub cold_mode: ColdStoreMode,
431
432 pub auto_compact: bool,
434 pub compact_interval_mins: u64,
435
436 pub max_index_entries_per_field: usize, pub enable_write_buffering: bool, pub write_buffer_size: usize, pub write_buffer_flush_interval_ms: u64, }
444
445#[derive(Debug, Clone, Copy)]
446pub enum ColdStoreMode {
447 HighThroughput,
448 LowSpace,
449}
450
451impl Default for AuroraConfig {
452 fn default() -> Self {
453 Self {
454 db_path: PathBuf::from("aurora.db"),
455 create_dirs: true,
456
457 hot_cache_size_mb: 128,
458 hot_cache_cleanup_interval_secs: 30,
459 eviction_policy: crate::storage::EvictionPolicy::Hybrid,
460
461 cold_cache_capacity_mb: 64,
462 cold_flush_interval_ms: Some(100),
463 cold_mode: ColdStoreMode::HighThroughput,
464
465 auto_compact: true,
466 compact_interval_mins: 60,
467
468 max_index_entries_per_field: 100_000,
469
470 enable_write_buffering: false,
471 write_buffer_size: 1000,
472 write_buffer_flush_interval_ms: 10,
473 }
474 }
475}
476
477impl AuroraConfig {
478 pub fn with_path<P: AsRef<Path>>(path: P) -> Self {
480 let mut config = Self::default();
481 config.db_path = path.as_ref().to_path_buf();
482 config
483 }
484
485 pub fn read_optimized() -> Self {
487 Self {
488 hot_cache_size_mb: 512,
489 eviction_policy: crate::storage::EvictionPolicy::LFU,
490 cold_cache_capacity_mb: 256,
491 cold_mode: ColdStoreMode::HighThroughput,
492 ..Default::default()
493 }
494 }
495
496 pub fn write_optimized() -> Self {
498 Self {
499 hot_cache_size_mb: 128,
500 eviction_policy: crate::storage::EvictionPolicy::LRU,
501 cold_cache_capacity_mb: 512,
502 cold_flush_interval_ms: Some(50),
503 enable_write_buffering: true,
504 write_buffer_size: 10000,
505 ..Default::default()
506 }
507 }
508
509 pub fn low_memory() -> Self {
511 Self {
512 hot_cache_size_mb: 32,
513 eviction_policy: crate::storage::EvictionPolicy::LRU,
514 cold_cache_capacity_mb: 32,
515 cold_mode: ColdStoreMode::LowSpace,
516 max_index_entries_per_field: 10_000,
517 ..Default::default()
518 }
519 }
520
521 pub fn realtime() -> Self {
523 Self {
524 hot_cache_size_mb: 1024,
525 eviction_policy: crate::storage::EvictionPolicy::Hybrid,
526 cold_cache_capacity_mb: 512,
527 cold_flush_interval_ms: Some(25),
528 enable_write_buffering: true,
529 write_buffer_size: 5000,
530 auto_compact: false,
531 max_index_entries_per_field: 500_000,
532 ..Default::default()
533 }
534 }
535}