use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FieldValidationConstraint {
Format(String),
Min(f64),
Max(f64),
MinLength(i64),
MaxLength(i64),
Pattern(String),
}
impl PartialEq for FieldValidationConstraint {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Format(a), Self::Format(b)) | (Self::Pattern(a), Self::Pattern(b)) => a == b,
(Self::Min(a), Self::Min(b)) | (Self::Max(a), Self::Max(b)) => {
a.to_bits() == b.to_bits()
}
(Self::MinLength(a), Self::MinLength(b)) | (Self::MaxLength(a), Self::MaxLength(b)) => {
a == b
}
_ => false,
}
}
}
impl Eq for FieldValidationConstraint {}
impl Hash for FieldValidationConstraint {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
Self::Format(s) | Self::Pattern(s) => s.hash(state),
Self::Min(f) | Self::Max(f) => f.to_bits().hash(state),
Self::MinLength(i) | Self::MaxLength(i) => i.hash(state),
}
}
}
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
)]
pub enum ScalarType {
String,
Int,
Uuid,
Bool,
Float,
Object,
Array,
Any,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum FieldType {
Scalar(ScalarType),
Object,
Array(ScalarType),
Nested(Box<HashMap<String, FieldDefinition>>),
Any,
}
impl Hash for FieldType {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
FieldType::Scalar(s) => s.hash(state),
FieldType::Array(s) => s.hash(state),
FieldType::Nested(m) => {
let mut keys: Vec<_> = m.keys().collect();
keys.sort();
for k in keys {
k.hash(state);
m.get(k).unwrap().hash(state);
}
}
_ => {}
}
}
}
impl fmt::Display for ScalarType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ScalarType::String => write!(f, "String"),
ScalarType::Int => write!(f, "Int"),
ScalarType::Uuid => write!(f, "Uuid"),
ScalarType::Bool => write!(f, "Bool"),
ScalarType::Float => write!(f, "Float"),
ScalarType::Object => write!(f, "Object"),
ScalarType::Array => write!(f, "Array"),
ScalarType::Any => write!(f, "Any"),
}
}
}
impl fmt::Display for FieldType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FieldType::Scalar(s) => write!(f, "{}", s),
FieldType::Object => write!(f, "Object"),
FieldType::Array(s) => write!(f, "Array<{}>", s),
FieldType::Nested(_) => write!(f, "Nested"),
FieldType::Any => write!(f, "Any"),
}
}
}
impl fmt::Display for FieldDefinition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}{} (indexed: {}, unique: {})",
self.field_type,
if self.nullable { "?" } else { "!" },
self.indexed,
self.unique,
)
}
}
impl fmt::Display for Collection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", serde_json::to_string_pretty(self).unwrap_or_default())
}
}
impl fmt::Display for DurabilityMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DurabilityMode::None => write!(f, "None"),
DurabilityMode::WAL => write!(f, "WAL"),
DurabilityMode::Strict => write!(f, "Strict"),
DurabilityMode::Synchronous => write!(f, "Synchronous"),
}
}
}
impl fmt::Display for ColdStoreMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ColdStoreMode::HighThroughput => write!(f, "HighThroughput"),
ColdStoreMode::LowSpace => write!(f, "LowSpace"),
}
}
}
impl fmt::Display for AuroraConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", serde_json::to_string_pretty(self).unwrap_or_default())
}
}
impl Default for FieldType {
fn default() -> Self {
FieldType::Any
}
}
impl FieldType {
pub const SCALAR_STRING: FieldType = FieldType::Scalar(ScalarType::String);
pub const SCALAR_INT: FieldType = FieldType::Scalar(ScalarType::Int);
pub const SCALAR_BOOL: FieldType = FieldType::Scalar(ScalarType::Bool);
pub const SCALAR_FLOAT: FieldType = FieldType::Scalar(ScalarType::Float);
pub const SCALAR_UUID: FieldType = FieldType::Scalar(ScalarType::Uuid);
pub const SCALAR_OBJECT: FieldType = FieldType::Scalar(ScalarType::Object);
pub const SCALAR_ARRAY: FieldType = FieldType::Scalar(ScalarType::Array);
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
pub struct FieldDefinition {
pub field_type: FieldType,
pub unique: bool,
pub indexed: bool,
pub nullable: bool,
#[serde(default)]
pub validations: Vec<FieldValidationConstraint>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Collection {
pub name: String,
pub fields: HashMap<String, FieldDefinition>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Document {
#[serde(rename = "_sid", alias = "id")]
pub _sid: String,
pub data: HashMap<String, Value>,
}
impl Document {
pub fn new() -> Self {
Self {
_sid: Uuid::now_v7().to_string(),
data: HashMap::new(),
}
}
}
impl fmt::Display for Document {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", serde_json::to_string_pretty(self).unwrap_or_default())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Value {
Null,
Bool(bool),
Int(i64),
Float(f64),
String(String),
Uuid(Uuid),
DateTime(DateTime<Utc>),
Array(Vec<Value>),
Object(HashMap<String, Value>),
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Null, Value::Null) => true,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Int(a), Value::Int(b)) => a == b,
(Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
(Value::String(a), Value::String(b)) => a == b,
(Value::Uuid(a), Value::Uuid(b)) => a == b,
(Value::Uuid(u), Value::String(s)) => &u.to_string() == s,
(Value::String(s), Value::Uuid(u)) => s == &u.to_string(),
(Value::DateTime(a), Value::DateTime(b)) => a == b,
(Value::Array(a), Value::Array(b)) => a == b,
(Value::Object(a), Value::Object(b)) => a == b,
_ => false,
}
}
}
impl Eq for Value {}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Value {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Value::Null, Value::Null) => Ordering::Equal,
(Value::Null, _) => Ordering::Less,
(_, Value::Null) => Ordering::Greater,
(Value::Bool(a), Value::Bool(b)) => a.cmp(b),
(Value::Bool(_), _) => Ordering::Less,
(_, Value::Bool(_)) => Ordering::Greater,
(Value::Int(a), Value::Int(b)) => a.cmp(b),
(Value::Int(_), _) => Ordering::Less,
(_, Value::Int(_)) => Ordering::Greater,
(Value::Float(a), Value::Float(b)) => a.total_cmp(b),
(Value::Float(_), _) => Ordering::Less,
(_, Value::Float(_)) => Ordering::Greater,
(Value::String(a), Value::String(b)) => a.cmp(b),
(Value::String(_), _) => Ordering::Less,
(_, Value::String(_)) => Ordering::Greater,
(Value::Uuid(a), Value::Uuid(b)) => a.cmp(b),
(Value::Uuid(_), _) => Ordering::Less,
(_, Value::Uuid(_)) => Ordering::Greater,
(Value::DateTime(a), Value::DateTime(b)) => a.cmp(b),
(Value::DateTime(_), _) => Ordering::Less,
(_, Value::DateTime(_)) => Ordering::Greater,
(Value::Array(a), Value::Array(b)) => a.cmp(b),
(Value::Array(_), _) => Ordering::Less,
(_, Value::Array(_)) => Ordering::Greater,
(Value::Object(_), Value::Object(_)) => Ordering::Equal, }
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Null => write!(f, "null"),
Value::Bool(b) => write!(f, "{}", b),
Value::Int(i) => write!(f, "{}", i),
Value::Float(fl) => write!(f, "{}", fl),
Value::String(s) => write!(f, "{}", s),
Value::Uuid(u) => write!(f, "{}", u),
Value::DateTime(dt) => write!(f, "{}", dt),
Value::Array(arr) => write!(f, "{}", serde_json::to_string(arr).unwrap_or_else(|_| "[]".to_string())),
Value::Object(obj) => write!(f, "{}", serde_json::to_string(obj).unwrap_or_else(|_| "{}".to_string())),
}
}
}
impl Value {
pub fn as_str(&self) -> Option<&str> {
if let Value::String(s) = self { Some(s) } else { None }
}
pub fn as_i64(&self) -> Option<i64> {
if let Value::Int(i) = self { Some(*i) } else { None }
}
pub fn as_f64(&self) -> Option<f64> {
if let Value::Float(f) = self { Some(*f) } else { None }
}
pub fn as_bool(&self) -> Option<bool> {
if let Value::Bool(b) = self { Some(*b) } else { None }
}
pub fn as_array(&self) -> Option<&Vec<Value>> {
if let Value::Array(a) = self { Some(a) } else { None }
}
pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
if let Value::Object(o) = self { Some(o) } else { None }
}
}
impl From<String> for Value { fn from(v: String) -> Self { Value::String(v) } }
impl From<&str> for Value { fn from(v: &str) -> Self { Value::String(v.to_string()) } }
impl From<bool> for Value { fn from(v: bool) -> Self { Value::Bool(v) } }
impl From<f64> for Value { fn from(v: f64) -> Self { Value::Float(v) } }
impl From<i64> for Value { fn from(v: i64) -> Self { Value::Int(v) } }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum DurabilityMode {
None,
WAL,
Strict,
Synchronous,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ColdStoreMode {
HighThroughput,
LowSpace,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuroraConfig {
pub db_path: PathBuf,
pub create_dirs: bool,
pub hot_cache_size_mb: usize,
pub hot_cache_cleanup_interval_secs: u64,
pub eviction_policy: crate::storage::EvictionPolicy,
pub cold_cache_capacity_mb: usize,
pub cold_flush_interval_ms: Option<u64>,
pub cold_mode: ColdStoreMode,
pub auto_compact: bool,
pub compact_interval_mins: u64,
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,
pub audit_log_path: Option<String>,
pub workers_enabled: bool,
pub worker_threads: usize,
}
impl Default for AuroraConfig {
fn default() -> Self {
Self {
db_path: PathBuf::from("aurora_db"),
create_dirs: true,
hot_cache_size_mb: 256,
hot_cache_cleanup_interval_secs: 60,
eviction_policy: crate::storage::EvictionPolicy::LRU,
cold_cache_capacity_mb: 1024,
cold_flush_interval_ms: Some(5000),
cold_mode: ColdStoreMode::HighThroughput,
auto_compact: true,
compact_interval_mins: 60,
max_index_entries_per_field: 100_000,
enable_write_buffering: true,
write_buffer_size: 10_000,
write_buffer_flush_interval_ms: 1000,
durability_mode: DurabilityMode::WAL,
enable_wal: true,
checkpoint_interval_ms: 10000,
audit_log_path: None,
workers_enabled: false,
worker_threads: 4,
}
}
}