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, PartialEq)]
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 Eq for Value {}
242
243impl PartialOrd for Value {
244    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
245        Some(self.cmp(other))
246    }
247}
248
249impl Ord for Value {
250    fn cmp(&self, other: &Self) -> Ordering {
251        match (self, other) {
252            (Value::Null, Value::Null) => Ordering::Equal,
253            (Value::Null, _) => Ordering::Less,
254            (_, Value::Null) => Ordering::Greater,
255            
256            (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
257            (Value::Bool(_), _) => Ordering::Less,
258            (_, Value::Bool(_)) => Ordering::Greater,
259
260            (Value::Int(a), Value::Int(b)) => a.cmp(b),
261            (Value::Int(_), _) => Ordering::Less,
262            (_, Value::Int(_)) => Ordering::Greater,
263
264            (Value::Float(a), Value::Float(b)) => a.total_cmp(b),
265            (Value::Float(_), _) => Ordering::Less,
266            (_, Value::Float(_)) => Ordering::Greater,
267
268            (Value::String(a), Value::String(b)) => a.cmp(b),
269            (Value::String(_), _) => Ordering::Less,
270            (_, Value::String(_)) => Ordering::Greater,
271            
272            (Value::Uuid(a), Value::Uuid(b)) => a.cmp(b),
273            (Value::Uuid(_), _) => Ordering::Less,
274            (_, Value::Uuid(_)) => Ordering::Greater,
275            
276            (Value::DateTime(a), Value::DateTime(b)) => a.cmp(b),
277            (Value::DateTime(_), _) => Ordering::Less,
278            (_, Value::DateTime(_)) => Ordering::Greater,
279
280            (Value::Array(a), Value::Array(b)) => a.cmp(b),
281            (Value::Array(_), _) => Ordering::Less,
282            (_, Value::Array(_)) => Ordering::Greater,
283
284            (Value::Object(_), Value::Object(_)) => Ordering::Equal, // Simplified for brevity
285        }
286    }
287}
288
289impl fmt::Display for Value {
290    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291        match self {
292            Value::Null => write!(f, "null"),
293            Value::Bool(b) => write!(f, "{}", b),
294            Value::Int(i) => write!(f, "{}", i),
295            Value::Float(fl) => write!(f, "{}", fl),
296            Value::String(s) => write!(f, "{}", s),
297            Value::Uuid(u) => write!(f, "{}", u),
298            Value::DateTime(dt) => write!(f, "{}", dt),
299            Value::Array(arr) => write!(f, "{}", serde_json::to_string(arr).unwrap_or_else(|_| "[]".to_string())),
300            Value::Object(obj) => write!(f, "{}", serde_json::to_string(obj).unwrap_or_else(|_| "{}".to_string())),
301        }
302    }
303}
304
305impl Value {
306    pub fn as_str(&self) -> Option<&str> {
307        if let Value::String(s) = self { Some(s) } else { None }
308    }
309    pub fn as_i64(&self) -> Option<i64> {
310        if let Value::Int(i) = self { Some(*i) } else { None }
311    }
312    pub fn as_f64(&self) -> Option<f64> {
313        if let Value::Float(f) = self { Some(*f) } else { None }
314    }
315    pub fn as_bool(&self) -> Option<bool> {
316        if let Value::Bool(b) = self { Some(*b) } else { None }
317    }
318    pub fn as_array(&self) -> Option<&Vec<Value>> {
319        if let Value::Array(a) = self { Some(a) } else { None }
320    }
321    pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
322        if let Value::Object(o) = self { Some(o) } else { None }
323    }
324}
325
326impl From<String> for Value { fn from(v: String) -> Self { Value::String(v) } }
327impl From<&str> for Value { fn from(v: &str) -> Self { Value::String(v.to_string()) } }
328impl From<bool> for Value { fn from(v: bool) -> Self { Value::Bool(v) } }
329impl From<f64> for Value { fn from(v: f64) -> Self { Value::Float(v) } }
330impl From<i64> for Value { fn from(v: i64) -> Self { Value::Int(v) } }
331
332#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
333pub enum DurabilityMode {
334    None,
335    WAL,
336    Strict,
337    /// Synchronous mode: every write blocks until it reaches durable storage.
338    /// No buffering, no WAL — safest but slowest.
339    Synchronous,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
343pub enum ColdStoreMode {
344    HighThroughput,
345    LowSpace,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct AuroraConfig {
350    pub db_path: PathBuf,
351    pub create_dirs: bool,
352    pub hot_cache_size_mb: usize,
353    pub hot_cache_cleanup_interval_secs: u64,
354    pub eviction_policy: crate::storage::EvictionPolicy,
355    pub cold_cache_capacity_mb: usize,
356    pub cold_flush_interval_ms: Option<u64>,
357    pub cold_mode: ColdStoreMode,
358    pub auto_compact: bool,
359    pub compact_interval_mins: u64,
360    pub max_index_entries_per_field: usize,
361    pub enable_write_buffering: bool,
362    pub write_buffer_size: usize,
363    pub write_buffer_flush_interval_ms: u64,
364    pub durability_mode: DurabilityMode,
365    pub enable_wal: bool,
366    pub checkpoint_interval_ms: u64,
367    pub audit_log_path: Option<String>,
368    pub workers_enabled: bool,
369    pub worker_threads: usize,
370}
371
372impl Default for AuroraConfig {
373    fn default() -> Self {
374        Self {
375            db_path: PathBuf::from("aurora_db"),
376            create_dirs: true,
377            hot_cache_size_mb: 256,
378            hot_cache_cleanup_interval_secs: 60,
379            eviction_policy: crate::storage::EvictionPolicy::LRU,
380            cold_cache_capacity_mb: 1024,
381            cold_flush_interval_ms: Some(5000),
382            cold_mode: ColdStoreMode::HighThroughput,
383            auto_compact: true,
384            compact_interval_mins: 60,
385            max_index_entries_per_field: 100_000,
386            enable_write_buffering: true,
387            write_buffer_size: 10_000,
388            write_buffer_flush_interval_ms: 1000,
389            durability_mode: DurabilityMode::WAL,
390            enable_wal: true,
391            checkpoint_interval_ms: 10000,
392            audit_log_path: None,
393            workers_enabled: false,
394            worker_threads: 4,
395        }
396    }
397}