1use chrono::{DateTime, Utc};
14use serde::{Deserialize, Serialize};
15use std::cmp::Ordering;
16use std::collections::HashMap;
17use std::fmt;
18use std::hash::{Hash, Hasher};
19use std::path::PathBuf;
20use uuid::Uuid;
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
25pub enum FieldValidationConstraint {
26 Format(String),
27 Min(f64),
28 Max(f64),
29 MinLength(i64),
30 MaxLength(i64),
31 Pattern(String),
32}
33
34impl PartialEq for FieldValidationConstraint {
35 fn eq(&self, other: &Self) -> bool {
36 match (self, other) {
37 (Self::Format(a), Self::Format(b)) | (Self::Pattern(a), Self::Pattern(b)) => a == b,
38 (Self::Min(a), Self::Min(b)) | (Self::Max(a), Self::Max(b)) => {
39 a.to_bits() == b.to_bits()
40 }
41 (Self::MinLength(a), Self::MinLength(b)) | (Self::MaxLength(a), Self::MaxLength(b)) => {
42 a == b
43 }
44 _ => false,
45 }
46 }
47}
48
49impl Eq for FieldValidationConstraint {}
50
51impl Hash for FieldValidationConstraint {
52 fn hash<H: Hasher>(&self, state: &mut H) {
53 std::mem::discriminant(self).hash(state);
54 match self {
55 Self::Format(s) | Self::Pattern(s) => s.hash(state),
56 Self::Min(f) | Self::Max(f) => f.to_bits().hash(state),
57 Self::MinLength(i) | Self::MaxLength(i) => i.hash(state),
58 }
59 }
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
63pub enum ScalarType {
64 String,
65 Int,
66 Uuid,
67 Bool,
68 Float,
69 DateTime,
70 Object,
71 Array,
72 Any,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
76pub enum FieldType {
77 Scalar(ScalarType),
78 Object,
79 Array(ScalarType),
80 Nested(Box<HashMap<String, FieldDefinition>>),
81 Any,
82}
83
84impl Hash for FieldType {
85 fn hash<H: Hasher>(&self, state: &mut H) {
86 std::mem::discriminant(self).hash(state);
87 match self {
88 FieldType::Scalar(s) => s.hash(state),
89 FieldType::Array(s) => s.hash(state),
90 FieldType::Nested(m) => {
91 let mut keys: Vec<_> = m.keys().collect();
92 keys.sort();
93 for k in keys {
94 k.hash(state);
95 m.get(k).unwrap().hash(state);
96 }
97 }
98 _ => {}
99 }
100 }
101}
102
103impl fmt::Display for ScalarType {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 match self {
106 ScalarType::String => write!(f, "String"),
107 ScalarType::Int => write!(f, "Int"),
108 ScalarType::Uuid => write!(f, "Uuid"),
109 ScalarType::Bool => write!(f, "Bool"),
110 ScalarType::Float => write!(f, "Float"),
111 ScalarType::DateTime => write!(f, "DateTime"),
112 ScalarType::Object => write!(f, "Object"),
113 ScalarType::Array => write!(f, "Array"),
114 ScalarType::Any => write!(f, "Any"),
115 }
116 }
117}
118
119impl fmt::Display for FieldType {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 match self {
122 FieldType::Scalar(s) => write!(f, "{}", s),
123 FieldType::Object => write!(f, "Object"),
124 FieldType::Array(s) => write!(f, "Array<{}>", s),
125 FieldType::Nested(_) => write!(f, "Nested"),
126 FieldType::Any => write!(f, "Any"),
127 }
128 }
129}
130
131impl fmt::Display for FieldDefinition {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 write!(
134 f,
135 "{}{} (indexed: {}, unique: {})",
136 self.field_type,
137 if self.nullable { "?" } else { "!" },
138 self.indexed,
139 self.unique,
140 )
141 }
142}
143
144impl fmt::Display for Collection {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 write!(
147 f,
148 "{}",
149 serde_json::to_string_pretty(self).unwrap_or_default()
150 )
151 }
152}
153
154impl fmt::Display for DurabilityMode {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 match self {
157 DurabilityMode::None => write!(f, "None"),
158 DurabilityMode::WAL => write!(f, "WAL"),
159 DurabilityMode::Strict => write!(f, "Strict"),
160 DurabilityMode::Synchronous => write!(f, "Synchronous"),
161 }
162 }
163}
164
165impl fmt::Display for ColdStoreMode {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 match self {
168 ColdStoreMode::HighThroughput => write!(f, "HighThroughput"),
169 ColdStoreMode::LowSpace => write!(f, "LowSpace"),
170 }
171 }
172}
173
174impl fmt::Display for AuroraConfig {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 write!(
177 f,
178 "{}",
179 serde_json::to_string_pretty(self).unwrap_or_default()
180 )
181 }
182}
183
184impl Default for FieldType {
185 fn default() -> Self {
186 FieldType::Any
187 }
188}
189
190impl FieldType {
191 pub const SCALAR_STRING: FieldType = FieldType::Scalar(ScalarType::String);
192 pub const SCALAR_INT: FieldType = FieldType::Scalar(ScalarType::Int);
193 pub const SCALAR_BOOL: FieldType = FieldType::Scalar(ScalarType::Bool);
194 pub const SCALAR_FLOAT: FieldType = FieldType::Scalar(ScalarType::Float);
195 pub const SCALAR_UUID: FieldType = FieldType::Scalar(ScalarType::Uuid);
196 pub const SCALAR_OBJECT: FieldType = FieldType::Scalar(ScalarType::Object);
197 pub const SCALAR_ARRAY: FieldType = FieldType::Scalar(ScalarType::Array);
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
201pub struct Relation {
202 pub to: String,
203 pub key: String,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
207pub struct FieldDefinition {
208 pub field_type: FieldType,
209 pub unique: bool,
210 pub indexed: bool,
211 pub nullable: bool,
212 #[serde(default)]
214 pub validations: Vec<FieldValidationConstraint>,
215 pub relation: Option<Relation>,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct Collection {
221 pub name: String,
222 pub fields: HashMap<String, FieldDefinition>,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct Document {
230 #[serde(rename = "_sid", alias = "id")]
232 pub _sid: String,
233 pub data: HashMap<String, Value>,
235}
236
237impl Document {
238 pub fn new() -> Self {
240 Self {
241 _sid: Uuid::now_v7().to_string(),
242 data: HashMap::new(),
243 }
244 }
245
246 pub fn id(&self) -> &str {
248 &self._sid
249 }
250
251 pub fn get(&self, field: &str) -> Option<&Value> {
253 self.data.get(field)
254 }
255
256 pub fn bind<T: serde::de::DeserializeOwned>(mut self) -> crate::error::Result<T> {
261 self.data
262 .insert("_sid".to_string(), Value::String(self._sid.clone()));
263
264 if !self.data.contains_key("id") {
265 self.data
266 .insert("id".to_string(), Value::String(self._sid.clone()));
267 }
268
269 let json = serde_json::to_value(&self.data)?;
270 Ok(serde_json::from_value(json)?)
271 }
272}
273
274impl fmt::Display for Document {
275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276 write!(f, "{{\n \"_sid\": \"{}\",\n \"data\": {{\n", self._sid)?;
277 let mut first = true;
278 for (k, v) in &self.data {
279 if !first {
280 write!(f, ",\n")?;
281 }
282 write!(f, " \"{}\": {}", k, v)?;
283 first = false;
284 }
285 write!(f, "\n }}\n}}")
286 }
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
293#[serde(untagged)]
294pub enum Value {
295 Null,
297 Bool(bool),
299 Int(i64),
301 Float(f64),
303 String(String),
305 Uuid(Uuid),
307 DateTime(DateTime<Utc>),
309 Array(Vec<Value>),
311 Object(HashMap<String, Value>),
313}
314
315impl PartialEq for Value {
316 fn eq(&self, other: &Self) -> bool {
317 match (self, other) {
318 (Value::Null, Value::Null) => true,
319 (Value::Bool(a), Value::Bool(b)) => a == b,
320 (Value::Int(a), Value::Int(b)) => a == b,
321 (Value::Int(a), Value::Float(b)) => *a as f64 == *b,
322 (Value::Float(a), Value::Int(b)) => *a == *b as f64,
323 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
324 (Value::String(a), Value::String(b)) => a == b,
325 (Value::Uuid(a), Value::Uuid(b)) => a == b,
326 (Value::Uuid(u), Value::String(s)) | (Value::String(s), Value::Uuid(u)) => {
327 u.to_string() == *s
328 }
329 (Value::DateTime(a), Value::DateTime(b)) => a == b,
330 (Value::Array(a), Value::Array(b)) => a == b,
331 (Value::Object(a), Value::Object(b)) => a == b,
332 _ => false,
333 }
334 }
335}
336
337impl Eq for Value {}
338
339impl PartialOrd for Value {
340 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
341 Some(self.cmp(other))
342 }
343}
344
345impl Ord for Value {
346 fn cmp(&self, other: &Self) -> Ordering {
347 match (self, other) {
348 (Value::Null, Value::Null) => Ordering::Equal,
349 (Value::Null, _) => Ordering::Less,
350 (_, Value::Null) => Ordering::Greater,
351
352 (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
353 (Value::Bool(_), _) => Ordering::Less,
354 (_, Value::Bool(_)) => Ordering::Greater,
355
356 (Value::Int(a), Value::Int(b)) => a.cmp(b),
357 (Value::Int(a), Value::Float(b)) => (*a as f64).total_cmp(b),
358 (Value::Float(a), Value::Int(b)) => a.total_cmp(&(*b as f64)),
359 (Value::Float(a), Value::Float(b)) => a.total_cmp(b),
360 (Value::Int(_) | Value::Float(_), _) => Ordering::Less,
361 (_, Value::Int(_) | Value::Float(_)) => Ordering::Greater,
362
363 (Value::String(a), Value::String(b)) => a.cmp(b),
364 (Value::Uuid(u), Value::String(s)) => u.to_string().cmp(s),
365 (Value::String(s), Value::Uuid(u)) => s.cmp(&u.to_string()),
366 (Value::String(_), _) => Ordering::Less,
367 (_, Value::String(_)) => Ordering::Greater,
368
369 (Value::Uuid(a), Value::Uuid(b)) => a.cmp(b),
370 (Value::Uuid(_), _) => Ordering::Less,
371 (_, Value::Uuid(_)) => Ordering::Greater,
372
373 (Value::DateTime(a), Value::DateTime(b)) => a.cmp(b),
374 (Value::DateTime(_), _) => Ordering::Less,
375 (_, Value::DateTime(_)) => Ordering::Greater,
376
377 (Value::Array(a), Value::Array(b)) => a.cmp(b),
378 (Value::Array(_), _) => Ordering::Less,
379 (_, Value::Array(_)) => Ordering::Greater,
380
381 (Value::Object(_), Value::Object(_)) => Ordering::Equal, }
383 }
384}
385
386impl fmt::Display for Value {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 match self {
389 Value::Null => write!(f, "null"),
390 Value::Bool(b) => write!(f, "{}", b),
391 Value::Int(i) => write!(f, "{}", i),
392 Value::Float(fl) => write!(f, "{}", fl),
393 Value::String(s) => write!(f, "\"{}\"", s),
394 Value::Uuid(u) => write!(f, "\"{}\"", u),
395 Value::DateTime(dt) => write!(f, "\"{}\"", dt),
396 Value::Array(arr) => {
397 write!(f, "[")?;
398 for (i, val) in arr.iter().enumerate() {
399 if i > 0 {
400 write!(f, ", ")?;
401 }
402 write!(f, "{}", val)?;
403 }
404 write!(f, "]")
405 }
406 Value::Object(obj) => {
407 write!(f, "{{")?;
408 let mut first = true;
409 for (k, v) in obj {
410 if !first {
411 write!(f, ", ")?;
412 }
413 write!(f, "\"{}\": {}", k, v)?;
414 first = false;
415 }
416 write!(f, "}}")
417 }
418 }
419 }
420}
421
422impl Value {
423 pub fn as_str(&self) -> Option<&str> {
425 if let Value::String(s) = self {
426 Some(s)
427 } else {
428 None
429 }
430 }
431
432 pub fn as_i64(&self) -> Option<i64> {
434 if let Value::Int(i) = self {
435 Some(*i)
436 } else {
437 None
438 }
439 }
440
441 pub fn as_u64(&self) -> Option<u64> {
443 if let Value::Int(i) = self {
444 (*i >= 0).then_some(*i as u64)
445 } else {
446 None
447 }
448 }
449
450 pub fn as_f64(&self) -> Option<f64> {
452 if let Value::Float(f) = self {
453 Some(*f)
454 } else {
455 None
456 }
457 }
458
459 pub fn as_bool(&self) -> Option<bool> {
461 if let Value::Bool(b) = self {
462 Some(*b)
463 } else {
464 None
465 }
466 }
467
468 pub fn as_array(&self) -> Option<&Vec<Value>> {
470 if let Value::Array(a) = self {
471 Some(a)
472 } else {
473 None
474 }
475 }
476
477 pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
479 if let Value::Object(o) = self {
480 Some(o)
481 } else {
482 None
483 }
484 }
485
486 pub fn coerce_to(&self, target: &FieldType) -> Value {
490 match target {
491 FieldType::Scalar(ScalarType::String) => match self {
492 Value::String(s) => Value::String(s.clone()),
493 Value::Null => Value::Null,
494 _ => Value::String(self.to_string()),
495 },
496 FieldType::Scalar(ScalarType::Int) => match self {
497 Value::Int(i) => Value::Int(*i),
498 Value::Float(f) => Value::Int(*f as i64),
499 Value::String(s) => s.parse().map(Value::Int).unwrap_or(Value::Null),
500 _ => Value::Null,
501 },
502 FieldType::Scalar(ScalarType::Float) => match self {
503 Value::Float(f) => Value::Float(*f),
504 Value::Int(i) => Value::Float(*i as f64),
505 Value::String(s) => s.parse().map(Value::Float).unwrap_or(Value::Null),
506 _ => Value::Null,
507 },
508 FieldType::Scalar(ScalarType::Uuid) => match self {
509 Value::Uuid(u) => Value::Uuid(*u),
510 Value::String(s) => Uuid::parse_str(s).map(Value::Uuid).unwrap_or(Value::Null),
511 _ => Value::Null,
512 },
513 FieldType::Scalar(ScalarType::Bool) => match self {
514 Value::Bool(b) => Value::Bool(*b),
515 Value::String(s) => Value::Bool(s == "true"),
516 _ => Value::Null,
517 },
518 FieldType::Scalar(ScalarType::DateTime) => match self {
519 Value::DateTime(dt) => Value::DateTime(*dt),
520 Value::String(s) => s
521 .parse::<DateTime<Utc>>()
522 .map(Value::DateTime)
523 .unwrap_or(Value::Null),
524 _ => Value::Null,
525 },
526 _ => self.clone(),
527 }
528 }
529}
530
531impl From<String> for Value {
532 fn from(v: String) -> Self {
533 Value::String(v)
534 }
535}
536impl From<&str> for Value {
537 fn from(v: &str) -> Self {
538 Value::String(v.to_string())
539 }
540}
541impl From<bool> for Value {
542 fn from(v: bool) -> Self {
543 Value::Bool(v)
544 }
545}
546impl From<f64> for Value {
547 fn from(v: f64) -> Self {
548 Value::Float(v)
549 }
550}
551impl From<i64> for Value {
552 fn from(v: i64) -> Self {
553 Value::Int(v)
554 }
555}
556
557#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
558pub enum DurabilityMode {
559 None,
560 WAL,
561 Strict,
562 Synchronous,
565}
566
567#[derive(Debug, Clone, Serialize, Deserialize)]
568pub enum ColdStoreMode {
569 HighThroughput,
570 LowSpace,
571}
572
573#[derive(Debug, Clone, Serialize, Deserialize)]
574pub struct AuroraConfig {
575 pub db_path: PathBuf,
576 pub create_dirs: bool,
577 pub hot_cache_size_mb: usize,
578 pub hot_cache_cleanup_interval_secs: u64,
579 pub eviction_policy: crate::storage::EvictionPolicy,
580 pub cold_cache_capacity_mb: usize,
581 pub cold_flush_interval_ms: Option<u64>,
582 pub cold_mode: ColdStoreMode,
583 pub auto_compact: bool,
584 pub compact_interval_mins: u64,
585 pub max_index_entries_per_field: usize,
586 pub enable_write_buffering: bool,
587 pub write_buffer_size: usize,
588 pub write_buffer_flush_interval_ms: u64,
589 pub durability_mode: DurabilityMode,
590 pub enable_wal: bool,
591 pub checkpoint_interval_ms: u64,
592 pub audit_log_path: Option<String>,
593 pub workers_enabled: bool,
594 pub worker_threads: usize,
595}
596
597impl Default for AuroraConfig {
598 fn default() -> Self {
599 Self {
600 db_path: PathBuf::from("aurora_db"),
601 create_dirs: true,
602 hot_cache_size_mb: 256,
603 hot_cache_cleanup_interval_secs: 60,
604 eviction_policy: crate::storage::EvictionPolicy::LRU,
605 cold_cache_capacity_mb: 1024,
606 cold_flush_interval_ms: Some(5000),
607 cold_mode: ColdStoreMode::HighThroughput,
608 auto_compact: true,
609 compact_interval_mins: 60,
610 max_index_entries_per_field: 100_000,
611 enable_write_buffering: true,
612 write_buffer_size: 10_000,
613 write_buffer_flush_interval_ms: 1000,
614 durability_mode: DurabilityMode::WAL,
615 enable_wal: true,
616 checkpoint_interval_ms: 10000,
617 audit_log_path: None,
618 workers_enabled: false,
619 worker_threads: 4,
620 }
621 }
622}
623
624#[cfg(test)]
625mod tests {
626 use super::Value;
627
628 #[test]
629 fn test_as_u64_positive_int() {
630 assert_eq!(Value::Int(42).as_u64(), Some(42));
631 }
632
633 #[test]
634 fn test_as_u64_negative_int_returns_none() {
635 assert_eq!(Value::Int(-1).as_u64(), None);
636 }
637
638 #[test]
639 fn test_as_u64_float_returns_none() {
640 assert_eq!(Value::Float(42.0).as_u64(), None);
641 }
642}