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