use crate::{
db::schema::{FieldId, SchemaFieldSlot, SchemaRowLayout, SchemaVersion},
model::field::{
FieldDatabaseDefault, FieldKind, FieldStorageDecode, LeafCodec, RelationStrength,
},
types::EntityTag,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct AcceptedSchemaSnapshot {
snapshot: PersistedSchemaSnapshot,
}
impl AcceptedSchemaSnapshot {
#[must_use]
pub(in crate::db::schema) const fn new(snapshot: PersistedSchemaSnapshot) -> Self {
Self { snapshot }
}
#[must_use]
pub(in crate::db) const fn snapshot(&self) -> &PersistedSchemaSnapshot {
&self.snapshot
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct PersistedSchemaSnapshot {
version: SchemaVersion,
entity_path: String,
entity_name: String,
primary_key_field_id: FieldId,
row_layout: SchemaRowLayout,
fields: Vec<PersistedFieldSnapshot>,
}
impl PersistedSchemaSnapshot {
#[must_use]
pub(in crate::db) const fn new(
version: SchemaVersion,
entity_path: String,
entity_name: String,
primary_key_field_id: FieldId,
row_layout: SchemaRowLayout,
fields: Vec<PersistedFieldSnapshot>,
) -> Self {
Self {
version,
entity_path,
entity_name,
primary_key_field_id,
row_layout,
fields,
}
}
#[must_use]
pub(in crate::db) const fn version(&self) -> SchemaVersion {
self.version
}
#[must_use]
pub(in crate::db) const fn entity_path(&self) -> &str {
self.entity_path.as_str()
}
#[must_use]
pub(in crate::db) const fn entity_name(&self) -> &str {
self.entity_name.as_str()
}
#[must_use]
pub(in crate::db) const fn primary_key_field_id(&self) -> FieldId {
self.primary_key_field_id
}
#[must_use]
pub(in crate::db) const fn row_layout(&self) -> &SchemaRowLayout {
&self.row_layout
}
#[must_use]
pub(in crate::db) const fn fields(&self) -> &[PersistedFieldSnapshot] {
self.fields.as_slice()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct PersistedFieldSnapshot {
id: FieldId,
name: String,
slot: SchemaFieldSlot,
kind: PersistedFieldKind,
nullable: bool,
default: SchemaFieldDefault,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
}
impl PersistedFieldSnapshot {
#[expect(
clippy::too_many_arguments,
reason = "schema snapshot construction keeps every persisted field contract explicit"
)]
#[must_use]
pub(in crate::db) const fn new(
id: FieldId,
name: String,
slot: SchemaFieldSlot,
kind: PersistedFieldKind,
nullable: bool,
default: SchemaFieldDefault,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
) -> Self {
Self {
id,
name,
slot,
kind,
nullable,
default,
storage_decode,
leaf_codec,
}
}
#[must_use]
pub(in crate::db) const fn id(&self) -> FieldId {
self.id
}
#[must_use]
pub(in crate::db) const fn name(&self) -> &str {
self.name.as_str()
}
#[must_use]
pub(in crate::db) const fn slot(&self) -> SchemaFieldSlot {
self.slot
}
#[must_use]
pub(in crate::db) const fn kind(&self) -> &PersistedFieldKind {
&self.kind
}
#[must_use]
pub(in crate::db) const fn nullable(&self) -> bool {
self.nullable
}
#[must_use]
pub(in crate::db) const fn default(&self) -> SchemaFieldDefault {
self.default
}
#[must_use]
pub(in crate::db) const fn storage_decode(&self) -> FieldStorageDecode {
self.storage_decode
}
#[must_use]
pub(in crate::db) const fn leaf_codec(&self) -> LeafCodec {
self.leaf_codec
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum SchemaFieldDefault {
None,
}
impl SchemaFieldDefault {
#[must_use]
pub(in crate::db) const fn from_model_default(default: FieldDatabaseDefault) -> Self {
match default {
FieldDatabaseDefault::None => Self::None,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) enum PersistedFieldKind {
Account,
Blob,
Bool,
Date,
Decimal {
scale: u32,
},
Duration,
Enum {
path: String,
variants: Vec<PersistedEnumVariant>,
},
Float32,
Float64,
Int,
Int128,
IntBig,
Principal,
Subaccount,
Text {
max_len: Option<u32>,
},
Timestamp,
Uint,
Uint128,
UintBig,
Ulid,
Unit,
Relation {
target_path: String,
target_entity_name: String,
target_entity_tag: EntityTag,
target_store_path: String,
key_kind: Box<Self>,
strength: PersistedRelationStrength,
},
List(Box<Self>),
Set(Box<Self>),
Map {
key: Box<Self>,
value: Box<Self>,
},
Structured {
queryable: bool,
},
}
impl PersistedFieldKind {
#[must_use]
pub(in crate::db) fn from_model_kind(kind: FieldKind) -> Self {
match kind {
FieldKind::Account => Self::Account,
FieldKind::Blob => Self::Blob,
FieldKind::Bool => Self::Bool,
FieldKind::Date => Self::Date,
FieldKind::Decimal { scale } => Self::Decimal { scale },
FieldKind::Duration => Self::Duration,
FieldKind::Enum { path, variants } => Self::Enum {
path: path.to_string(),
variants: variants
.iter()
.map(|variant| PersistedEnumVariant {
ident: variant.ident().to_string(),
payload_kind: variant
.payload_kind()
.map(|payload| Box::new(Self::from_model_kind(*payload))),
payload_storage_decode: variant.payload_storage_decode(),
})
.collect(),
},
FieldKind::Float32 => Self::Float32,
FieldKind::Float64 => Self::Float64,
FieldKind::Int => Self::Int,
FieldKind::Int128 => Self::Int128,
FieldKind::IntBig => Self::IntBig,
FieldKind::Principal => Self::Principal,
FieldKind::Subaccount => Self::Subaccount,
FieldKind::Text { max_len } => Self::Text { max_len },
FieldKind::Timestamp => Self::Timestamp,
FieldKind::Uint => Self::Uint,
FieldKind::Uint128 => Self::Uint128,
FieldKind::UintBig => Self::UintBig,
FieldKind::Ulid => Self::Ulid,
FieldKind::Unit => Self::Unit,
FieldKind::Relation {
target_path,
target_entity_name,
target_entity_tag,
target_store_path,
key_kind,
strength,
} => Self::Relation {
target_path: target_path.to_string(),
target_entity_name: target_entity_name.to_string(),
target_entity_tag,
target_store_path: target_store_path.to_string(),
key_kind: Box::new(Self::from_model_kind(*key_kind)),
strength: PersistedRelationStrength::from_model_strength(strength),
},
FieldKind::List(inner) => Self::List(Box::new(Self::from_model_kind(*inner))),
FieldKind::Set(inner) => Self::Set(Box::new(Self::from_model_kind(*inner))),
FieldKind::Map { key, value } => Self::Map {
key: Box::new(Self::from_model_kind(*key)),
value: Box::new(Self::from_model_kind(*value)),
},
FieldKind::Structured { queryable } => Self::Structured { queryable },
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct PersistedEnumVariant {
ident: String,
payload_kind: Option<Box<PersistedFieldKind>>,
payload_storage_decode: FieldStorageDecode,
}
impl PersistedEnumVariant {
#[must_use]
pub(in crate::db) const fn new(
ident: String,
payload_kind: Option<Box<PersistedFieldKind>>,
payload_storage_decode: FieldStorageDecode,
) -> Self {
Self {
ident,
payload_kind,
payload_storage_decode,
}
}
#[must_use]
pub(in crate::db) const fn ident(&self) -> &str {
self.ident.as_str()
}
#[must_use]
pub(in crate::db) fn payload_kind(&self) -> Option<&PersistedFieldKind> {
match self.payload_kind.as_ref() {
Some(kind) => Some(kind.as_ref()),
None => None,
}
}
#[must_use]
pub(in crate::db) const fn payload_storage_decode(&self) -> FieldStorageDecode {
self.payload_storage_decode
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum PersistedRelationStrength {
Strong,
Weak,
}
impl PersistedRelationStrength {
#[must_use]
const fn from_model_strength(strength: RelationStrength) -> Self {
match strength {
RelationStrength::Strong => Self::Strong,
RelationStrength::Weak => Self::Weak,
}
}
}