Skip to main content

aurora_db/
types.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::cmp::Ordering;
4use std::collections::HashMap;
5use std::fmt;
6use std::hash::{Hash, Hasher};
7use std::path::PathBuf;
8use uuid::Uuid;
9
10/// Validation constraint stored on a field definition.
11/// Applied during insert/update to enforce data integrity.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub enum FieldValidationConstraint {
14    Format(String),
15    Min(f64),
16    Max(f64),
17    MinLength(i64),
18    MaxLength(i64),
19    Pattern(String),
20}
21
22impl PartialEq for FieldValidationConstraint {
23    fn eq(&self, other: &Self) -> bool {
24        match (self, other) {
25            (Self::Format(a), Self::Format(b)) | (Self::Pattern(a), Self::Pattern(b)) => a == b,
26            (Self::Min(a), Self::Min(b)) | (Self::Max(a), Self::Max(b)) => {
27                a.to_bits() == b.to_bits()
28            }
29            (Self::MinLength(a), Self::MinLength(b)) | (Self::MaxLength(a), Self::MaxLength(b)) => {
30                a == b
31            }
32            _ => false,
33        }
34    }
35}
36
37impl Eq for FieldValidationConstraint {}
38
39impl Hash for FieldValidationConstraint {
40    fn hash<H: Hasher>(&self, state: &mut H) {
41        std::mem::discriminant(self).hash(state);
42        match self {
43            Self::Format(s) | Self::Pattern(s) => s.hash(state),
44            Self::Min(f) | Self::Max(f) => f.to_bits().hash(state),
45            Self::MinLength(i) | Self::MaxLength(i) => i.hash(state),
46        }
47    }
48}
49
50#[derive(
51    Debug,
52    Clone,
53    Serialize,
54    Deserialize,
55    PartialEq,
56    Eq,
57    PartialOrd,
58    Ord,
59    Hash,
60)]
61pub enum ScalarType {
62    String,
63    Int,
64    Uuid,
65    Bool,
66    Float,
67    Object,
68    Array,
69    Any,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
73pub enum FieldType {
74    Scalar(ScalarType),
75    Object,
76    Array(ScalarType),
77    Nested(Box<HashMap<String, FieldDefinition>>),
78    Any,
79}
80
81impl Hash for FieldType {
82    fn hash<H: Hasher>(&self, state: &mut H) {
83        std::mem::discriminant(self).hash(state);
84        match self {
85            FieldType::Scalar(s) => s.hash(state),
86            FieldType::Array(s) => s.hash(state),
87            FieldType::Nested(m) => {
88                let mut keys: Vec<_> = m.keys().collect();
89                keys.sort();
90                for k in keys {
91                    k.hash(state);
92                    m.get(k).unwrap().hash(state);
93                }
94            }
95            _ => {}
96        }
97    }
98}
99
100impl fmt::Display for ScalarType {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        match self {
103            ScalarType::String => write!(f, "String"),
104            ScalarType::Int => write!(f, "Int"),
105            ScalarType::Uuid => write!(f, "Uuid"),
106            ScalarType::Bool => write!(f, "Bool"),
107            ScalarType::Float => write!(f, "Float"),
108            ScalarType::Object => write!(f, "Object"),
109            ScalarType::Array => write!(f, "Array"),
110            ScalarType::Any => write!(f, "Any"),
111        }
112    }
113}
114
115impl fmt::Display for FieldType {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        match self {
118            FieldType::Scalar(s) => write!(f, "{}", s),
119            FieldType::Object => write!(f, "Object"),
120            FieldType::Array(s) => write!(f, "Array<{}>", s),
121            FieldType::Nested(_) => write!(f, "Nested"),
122            FieldType::Any => write!(f, "Any"),
123        }
124    }
125}
126
127impl fmt::Display for FieldDefinition {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        write!(
130            f,
131            "{}{} (indexed: {}, unique: {})",
132            self.field_type,
133            if self.nullable { "?" } else { "!" },
134            self.indexed,
135            self.unique,
136        )
137    }
138}
139
140impl fmt::Display for Collection {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        write!(f, "{}", serde_json::to_string_pretty(self).unwrap_or_default())
143    }
144}
145
146impl fmt::Display for DurabilityMode {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        match self {
149            DurabilityMode::None => write!(f, "None"),
150            DurabilityMode::WAL => write!(f, "WAL"),
151            DurabilityMode::Strict => write!(f, "Strict"),
152            DurabilityMode::Synchronous => write!(f, "Synchronous"),
153        }
154    }
155}
156
157impl fmt::Display for ColdStoreMode {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        match self {
160            ColdStoreMode::HighThroughput => write!(f, "HighThroughput"),
161            ColdStoreMode::LowSpace => write!(f, "LowSpace"),
162        }
163    }
164}
165
166impl fmt::Display for AuroraConfig {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        write!(f, "{}", serde_json::to_string_pretty(self).unwrap_or_default())
169    }
170}
171
172impl Default for FieldType {
173    fn default() -> Self {
174        FieldType::Any
175    }
176}
177
178impl FieldType {
179    pub const SCALAR_STRING: FieldType = FieldType::Scalar(ScalarType::String);
180    pub const SCALAR_INT: FieldType = FieldType::Scalar(ScalarType::Int);
181    pub const SCALAR_BOOL: FieldType = FieldType::Scalar(ScalarType::Bool);
182    pub const SCALAR_FLOAT: FieldType = FieldType::Scalar(ScalarType::Float);
183    pub const SCALAR_UUID: FieldType = FieldType::Scalar(ScalarType::Uuid);
184    pub const SCALAR_OBJECT: FieldType = FieldType::Scalar(ScalarType::Object);
185    pub const SCALAR_ARRAY: FieldType = FieldType::Scalar(ScalarType::Array);
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
189pub struct FieldDefinition {
190    pub field_type: FieldType,
191    pub unique: bool,
192    pub indexed: bool,
193    pub nullable: bool,
194    /// Validation constraints applied on insert/update.
195    #[serde(default)]
196    pub validations: Vec<FieldValidationConstraint>,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct Collection {
201    pub name: String,
202    pub fields: HashMap<String, FieldDefinition>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct Document {
207    #[serde(rename = "_sid", alias = "id")]
208    pub _sid: String,
209    pub data: HashMap<String, Value>,
210}
211
212impl Document {
213    pub fn new() -> Self {
214        Self {
215            _sid: Uuid::now_v7().to_string(),
216            data: HashMap::new(),
217        }
218    }
219}
220
221impl fmt::Display for Document {
222    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223        write!(f, "{}", serde_json::to_string_pretty(self).unwrap_or_default())
224    }
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
228#[serde(untagged)]
229pub enum Value {
230    Null,
231    Bool(bool),
232    Int(i64),
233    Float(f64),
234    String(String),
235    Uuid(Uuid),
236    DateTime(DateTime<Utc>),
237    Array(Vec<Value>),
238    Object(HashMap<String, Value>),
239}
240
241impl PartialEq for Value {
242    fn eq(&self, other: &Self) -> bool {
243        match (self, other) {
244            (Value::Null, Value::Null) => true,
245            (Value::Bool(a), Value::Bool(b)) => a == b,
246            (Value::Int(a), Value::Int(b)) => a == b,
247            (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
248            (Value::String(a), Value::String(b)) => a == b,
249            (Value::Uuid(a), Value::Uuid(b)) => a == b,
250            (Value::Uuid(u), Value::String(s)) => &u.to_string() == s,
251            (Value::String(s), Value::Uuid(u)) => s == &u.to_string(),
252            (Value::DateTime(a), Value::DateTime(b)) => a == b,
253            (Value::Array(a), Value::Array(b)) => a == b,
254            (Value::Object(a), Value::Object(b)) => a == b,
255            _ => false,
256        }
257    }
258}
259
260impl Eq for Value {}
261
262impl PartialOrd for Value {
263    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
264        Some(self.cmp(other))
265    }
266}
267
268impl Ord for Value {
269    fn cmp(&self, other: &Self) -> Ordering {
270        match (self, other) {
271            (Value::Null, Value::Null) => Ordering::Equal,
272            (Value::Null, _) => Ordering::Less,
273            (_, Value::Null) => Ordering::Greater,
274            
275            (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
276            (Value::Bool(_), _) => Ordering::Less,
277            (_, Value::Bool(_)) => Ordering::Greater,
278
279            (Value::Int(a), Value::Int(b)) => a.cmp(b),
280            (Value::Int(_), _) => Ordering::Less,
281            (_, Value::Int(_)) => Ordering::Greater,
282
283            (Value::Float(a), Value::Float(b)) => a.total_cmp(b),
284            (Value::Float(_), _) => Ordering::Less,
285            (_, Value::Float(_)) => Ordering::Greater,
286
287            (Value::String(a), Value::String(b)) => a.cmp(b),
288            (Value::String(_), _) => Ordering::Less,
289            (_, Value::String(_)) => Ordering::Greater,
290            
291            (Value::Uuid(a), Value::Uuid(b)) => a.cmp(b),
292            (Value::Uuid(_), _) => Ordering::Less,
293            (_, Value::Uuid(_)) => Ordering::Greater,
294            
295            (Value::DateTime(a), Value::DateTime(b)) => a.cmp(b),
296            (Value::DateTime(_), _) => Ordering::Less,
297            (_, Value::DateTime(_)) => Ordering::Greater,
298
299            (Value::Array(a), Value::Array(b)) => a.cmp(b),
300            (Value::Array(_), _) => Ordering::Less,
301            (_, Value::Array(_)) => Ordering::Greater,
302
303            (Value::Object(_), Value::Object(_)) => Ordering::Equal, // Simplified for brevity
304        }
305    }
306}
307
308impl fmt::Display for Value {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        match self {
311            Value::Null => write!(f, "null"),
312            Value::Bool(b) => write!(f, "{}", b),
313            Value::Int(i) => write!(f, "{}", i),
314            Value::Float(fl) => write!(f, "{}", fl),
315            Value::String(s) => write!(f, "{}", s),
316            Value::Uuid(u) => write!(f, "{}", u),
317            Value::DateTime(dt) => write!(f, "{}", dt),
318            Value::Array(arr) => write!(f, "{}", serde_json::to_string(arr).unwrap_or_else(|_| "[]".to_string())),
319            Value::Object(obj) => write!(f, "{}", serde_json::to_string(obj).unwrap_or_else(|_| "{}".to_string())),
320        }
321    }
322}
323
324impl Value {
325    pub fn as_str(&self) -> Option<&str> {
326        if let Value::String(s) = self { Some(s) } else { None }
327    }
328    pub fn as_i64(&self) -> Option<i64> {
329        if let Value::Int(i) = self { Some(*i) } else { None }
330    }
331    pub fn as_f64(&self) -> Option<f64> {
332        if let Value::Float(f) = self { Some(*f) } else { None }
333    }
334    pub fn as_bool(&self) -> Option<bool> {
335        if let Value::Bool(b) = self { Some(*b) } else { None }
336    }
337    pub fn as_array(&self) -> Option<&Vec<Value>> {
338        if let Value::Array(a) = self { Some(a) } else { None }
339    }
340    pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
341        if let Value::Object(o) = self { Some(o) } else { None }
342    }
343}
344
345impl From<String> for Value { fn from(v: String) -> Self { Value::String(v) } }
346impl From<&str> for Value { fn from(v: &str) -> Self { Value::String(v.to_string()) } }
347impl From<bool> for Value { fn from(v: bool) -> Self { Value::Bool(v) } }
348impl From<f64> for Value { fn from(v: f64) -> Self { Value::Float(v) } }
349impl From<i64> for Value { fn from(v: i64) -> Self { Value::Int(v) } }
350
351#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
352pub enum DurabilityMode {
353    None,
354    WAL,
355    Strict,
356    /// Synchronous mode: every write blocks until it reaches durable storage.
357    /// No buffering, no WAL — safest but slowest.
358    Synchronous,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize)]
362pub enum ColdStoreMode {
363    HighThroughput,
364    LowSpace,
365}
366
367#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct AuroraConfig {
369    pub db_path: PathBuf,
370    pub create_dirs: bool,
371    pub hot_cache_size_mb: usize,
372    pub hot_cache_cleanup_interval_secs: u64,
373    pub eviction_policy: crate::storage::EvictionPolicy,
374    pub cold_cache_capacity_mb: usize,
375    pub cold_flush_interval_ms: Option<u64>,
376    pub cold_mode: ColdStoreMode,
377    pub auto_compact: bool,
378    pub compact_interval_mins: u64,
379    pub max_index_entries_per_field: usize,
380    pub enable_write_buffering: bool,
381    pub write_buffer_size: usize,
382    pub write_buffer_flush_interval_ms: u64,
383    pub durability_mode: DurabilityMode,
384    pub enable_wal: bool,
385    pub checkpoint_interval_ms: u64,
386    pub audit_log_path: Option<String>,
387    pub workers_enabled: bool,
388    pub worker_threads: usize,
389}
390
391impl Default for AuroraConfig {
392    fn default() -> Self {
393        Self {
394            db_path: PathBuf::from("aurora_db"),
395            create_dirs: true,
396            hot_cache_size_mb: 256,
397            hot_cache_cleanup_interval_secs: 60,
398            eviction_policy: crate::storage::EvictionPolicy::LRU,
399            cold_cache_capacity_mb: 1024,
400            cold_flush_interval_ms: Some(5000),
401            cold_mode: ColdStoreMode::HighThroughput,
402            auto_compact: true,
403            compact_interval_mins: 60,
404            max_index_entries_per_field: 100_000,
405            enable_write_buffering: true,
406            write_buffer_size: 10_000,
407            write_buffer_flush_interval_ms: 1000,
408            durability_mode: DurabilityMode::WAL,
409            enable_wal: true,
410            checkpoint_interval_ms: 10000,
411            audit_log_path: None,
412            workers_enabled: false,
413            worker_threads: 4,
414        }
415    }
416}