#[cfg(any(test, feature = "sql"))]
use crate::db::predicate::relabel_sql_predicate_field_root;
use crate::{
db::schema::{
FieldId, SchemaFieldSlot, SchemaRowLayout, SchemaVersion,
schema_snapshot_index_integrity_detail, schema_snapshot_integrity_detail,
schema_snapshot_relation_integrity_detail,
},
error::InternalError,
model::field::{
FieldDatabaseDefault, FieldInsertGeneration, FieldKind, FieldStorageDecode,
FieldWriteManagement, LeafCodec, RelationStrength,
},
types::EntityTag,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct AcceptedSchemaSnapshot {
snapshot: PersistedSchemaSnapshot,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) struct AcceptedSchemaFootprint {
fields: u64,
nested_leaf_facts: u64,
}
impl AcceptedSchemaFootprint {
#[must_use]
const fn new(fields: u64, nested_leaf_facts: u64) -> Self {
Self {
fields,
nested_leaf_facts,
}
}
#[must_use]
pub(in crate::db) const fn fields(self) -> u64 {
self.fields
}
#[must_use]
pub(in crate::db) const fn nested_leaf_facts(self) -> u64 {
self.nested_leaf_facts
}
}
impl AcceptedSchemaSnapshot {
pub(in crate::db) fn try_new(snapshot: PersistedSchemaSnapshot) -> Result<Self, InternalError> {
if let Some(detail) = schema_snapshot_integrity_detail(
"accepted schema snapshot",
snapshot.version(),
snapshot.primary_key_field_ids(),
snapshot.row_layout(),
snapshot.fields(),
) {
return Err(InternalError::store_invariant(detail));
}
if let Some(detail) = schema_snapshot_index_integrity_detail(
"accepted schema snapshot",
snapshot.row_layout(),
snapshot.fields(),
snapshot.indexes(),
) {
return Err(InternalError::store_invariant(detail));
}
if let Some(detail) = schema_snapshot_relation_integrity_detail(
"accepted schema snapshot",
snapshot.row_layout(),
snapshot.fields(),
snapshot.relations(),
) {
return Err(InternalError::store_invariant(detail));
}
Ok(Self { snapshot })
}
#[must_use]
#[cfg(test)]
pub(in crate::db) const fn new(snapshot: PersistedSchemaSnapshot) -> Self {
Self { snapshot }
}
#[must_use]
pub(in crate::db) const fn persisted_snapshot(&self) -> &PersistedSchemaSnapshot {
&self.snapshot
}
#[must_use]
pub(in crate::db) const fn entity_path(&self) -> &str {
self.snapshot.entity_path()
}
#[must_use]
pub(in crate::db) const fn entity_name(&self) -> &str {
self.snapshot.entity_name()
}
#[must_use]
fn primary_key_fields(&self) -> Vec<&PersistedFieldSnapshot> {
self.snapshot
.primary_key_field_ids()
.iter()
.filter_map(|field_id| {
self.snapshot
.fields()
.iter()
.find(|field| field.id() == *field_id)
})
.collect()
}
#[must_use]
pub(in crate::db) fn primary_key_field_names(&self) -> Vec<&str> {
self.primary_key_fields()
.iter()
.map(|field| field.name())
.collect()
}
#[must_use]
pub(in crate::db) fn primary_key_field_kinds(&self) -> Vec<&PersistedFieldKind> {
self.primary_key_fields()
.iter()
.map(|field| field.kind())
.collect()
}
#[must_use]
#[cfg(test)]
fn field_by_name(&self, name: &str) -> Option<&PersistedFieldSnapshot> {
self.snapshot
.fields()
.iter()
.find(|field| field.name() == name)
}
#[must_use]
#[cfg(test)]
pub(in crate::db) fn field_kind_by_name(&self, name: &str) -> Option<&PersistedFieldKind> {
self.field_by_name(name).map(PersistedFieldSnapshot::kind)
}
#[must_use]
pub(in crate::db) fn footprint(&self) -> AcceptedSchemaFootprint {
let fields = u64::try_from(self.snapshot.fields().len()).unwrap_or(u64::MAX);
let nested_leaf_facts = self
.snapshot
.fields()
.iter()
.map(|field| u64::try_from(field.nested_leaves().len()).unwrap_or(u64::MAX))
.fold(0u64, u64::saturating_add);
AcceptedSchemaFootprint::new(fields, nested_leaf_facts)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct PersistedSchemaSnapshot {
version: SchemaVersion,
entity_path: String,
entity_name: String,
primary_key_field_ids: Vec<FieldId>,
row_layout: SchemaRowLayout,
fields: Vec<PersistedFieldSnapshot>,
indexes: Vec<PersistedIndexSnapshot>,
relations: Vec<PersistedRelationEdgeSnapshot>,
}
pub(in crate::db) trait IntoPrimaryKeyFieldIds {
fn into_primary_key_field_ids(self) -> Vec<FieldId>;
}
impl IntoPrimaryKeyFieldIds for FieldId {
fn into_primary_key_field_ids(self) -> Vec<FieldId> {
vec![self]
}
}
impl IntoPrimaryKeyFieldIds for Vec<FieldId> {
fn into_primary_key_field_ids(self) -> Vec<FieldId> {
self
}
}
impl PersistedSchemaSnapshot {
#[must_use]
#[cfg_attr(
not(test),
expect(
dead_code,
reason = "owner-local tests build field-only snapshots while production now preserves accepted index contracts through new_with_indexes"
)
)]
pub(in crate::db) fn new(
version: SchemaVersion,
entity_path: String,
entity_name: String,
primary_key_field_ids: impl IntoPrimaryKeyFieldIds,
row_layout: SchemaRowLayout,
fields: Vec<PersistedFieldSnapshot>,
) -> Self {
Self::new_with_primary_key_fields_and_indexes(
version,
entity_path,
entity_name,
primary_key_field_ids.into_primary_key_field_ids(),
row_layout,
fields,
Vec::new(),
)
}
#[must_use]
#[cfg(test)]
pub(in crate::db) fn new_with_indexes(
version: SchemaVersion,
entity_path: String,
entity_name: String,
primary_key_field_ids: impl IntoPrimaryKeyFieldIds,
row_layout: SchemaRowLayout,
fields: Vec<PersistedFieldSnapshot>,
indexes: Vec<PersistedIndexSnapshot>,
) -> Self {
Self::new_with_primary_key_fields_and_indexes(
version,
entity_path,
entity_name,
primary_key_field_ids.into_primary_key_field_ids(),
row_layout,
fields,
indexes,
)
}
#[must_use]
pub(in crate::db) fn new_with_primary_key_fields_and_indexes(
version: SchemaVersion,
entity_path: String,
entity_name: String,
primary_key_field_ids: impl IntoPrimaryKeyFieldIds,
row_layout: SchemaRowLayout,
fields: Vec<PersistedFieldSnapshot>,
indexes: Vec<PersistedIndexSnapshot>,
) -> Self {
Self {
version,
entity_path,
entity_name,
primary_key_field_ids: primary_key_field_ids.into_primary_key_field_ids(),
row_layout,
fields,
indexes,
relations: Vec::new(),
}
}
#[must_use]
pub(in crate::db) fn with_relations(
mut self,
relations: Vec<PersistedRelationEdgeSnapshot>,
) -> Self {
self.relations = relations;
self
}
#[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) fn first_primary_key_field_id(&self) -> FieldId {
self.primary_key_field_ids[0]
}
#[must_use]
pub(in crate::db) const fn primary_key_field_ids(&self) -> &[FieldId] {
self.primary_key_field_ids.as_slice()
}
#[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()
}
#[must_use]
pub(in crate::db) const fn indexes(&self) -> &[PersistedIndexSnapshot] {
self.indexes.as_slice()
}
#[must_use]
pub(in crate::db) const fn relations(&self) -> &[PersistedRelationEdgeSnapshot] {
self.relations.as_slice()
}
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) fn clone_with_version(&self, version: SchemaVersion) -> Self {
Self::new_with_primary_key_fields_and_indexes(
version,
self.entity_path.clone(),
self.entity_name.clone(),
self.primary_key_field_ids.clone(),
self.row_layout.clone_with_version(version),
self.fields.clone(),
self.indexes.clone(),
)
.with_relations(self.relations.clone())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct PersistedRelationEdgeSnapshot {
name: String,
target_path: String,
local_field_ids: Vec<FieldId>,
}
impl PersistedRelationEdgeSnapshot {
#[must_use]
pub(in crate::db) const fn new(
name: String,
target_path: String,
local_field_ids: Vec<FieldId>,
) -> Self {
Self {
name,
target_path,
local_field_ids,
}
}
#[must_use]
pub(in crate::db) const fn name(&self) -> &str {
self.name.as_str()
}
#[must_use]
pub(in crate::db) const fn target_path(&self) -> &str {
self.target_path.as_str()
}
#[must_use]
pub(in crate::db) const fn local_field_ids(&self) -> &[FieldId] {
self.local_field_ids.as_slice()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct PersistedIndexSnapshot {
ordinal: u16,
name: String,
store: String,
unique: bool,
origin: PersistedIndexOrigin,
key: PersistedIndexKeySnapshot,
predicate_sql: Option<String>,
}
impl PersistedIndexSnapshot {
#[must_use]
pub(in crate::db) const fn new(
ordinal: u16,
name: String,
store: String,
unique: bool,
key: PersistedIndexKeySnapshot,
predicate_sql: Option<String>,
) -> Self {
Self::new_with_origin(
ordinal,
name,
store,
unique,
PersistedIndexOrigin::Generated,
key,
predicate_sql,
)
}
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) const fn new_sql_ddl(
ordinal: u16,
name: String,
store: String,
unique: bool,
key: PersistedIndexKeySnapshot,
predicate_sql: Option<String>,
) -> Self {
Self::new_with_origin(
ordinal,
name,
store,
unique,
PersistedIndexOrigin::SqlDdl,
key,
predicate_sql,
)
}
#[must_use]
pub(in crate::db) const fn new_with_origin(
ordinal: u16,
name: String,
store: String,
unique: bool,
origin: PersistedIndexOrigin,
key: PersistedIndexKeySnapshot,
predicate_sql: Option<String>,
) -> Self {
Self {
ordinal,
name,
store,
unique,
origin,
key,
predicate_sql,
}
}
#[must_use]
pub(in crate::db) const fn ordinal(&self) -> u16 {
self.ordinal
}
#[must_use]
pub(in crate::db) const fn name(&self) -> &str {
self.name.as_str()
}
#[must_use]
pub(in crate::db) const fn store(&self) -> &str {
self.store.as_str()
}
#[must_use]
pub(in crate::db) const fn unique(&self) -> bool {
self.unique
}
#[must_use]
pub(in crate::db) const fn generated(&self) -> bool {
matches!(self.origin, PersistedIndexOrigin::Generated)
}
#[must_use]
pub(in crate::db) const fn origin(&self) -> PersistedIndexOrigin {
self.origin
}
#[must_use]
pub(in crate::db) const fn key(&self) -> &PersistedIndexKeySnapshot {
&self.key
}
#[must_use]
pub(in crate::db) const fn predicate_sql(&self) -> Option<&str> {
match &self.predicate_sql {
Some(sql) => Some(sql.as_str()),
None => None,
}
}
#[cfg(any(test, feature = "sql"))]
#[must_use]
pub(in crate::db) fn clone_with_renamed_field_path_root(
&self,
field_id: FieldId,
old_name: &str,
new_name: &str,
) -> Self {
Self {
ordinal: self.ordinal,
name: self.name.clone(),
store: self.store.clone(),
unique: self.unique,
origin: self.origin,
key: self
.key
.clone_with_renamed_field_path_root(field_id, new_name),
predicate_sql: self.predicate_sql.as_deref().map(|predicate_sql| {
relabel_sql_predicate_field_root(predicate_sql, old_name, new_name)
.unwrap_or_else(|| predicate_sql.to_string())
}),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum PersistedIndexOrigin {
Generated,
SqlDdl,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) enum PersistedIndexKeySnapshot {
FieldPath(Vec<PersistedIndexFieldPathSnapshot>),
Items(Vec<PersistedIndexKeyItemSnapshot>),
}
impl PersistedIndexKeySnapshot {
#[must_use]
pub(in crate::db) const fn is_field_path_only(&self) -> bool {
matches!(self, Self::FieldPath(_))
}
#[must_use]
pub(in crate::db) const fn field_paths(&self) -> &[PersistedIndexFieldPathSnapshot] {
match self {
Self::FieldPath(paths) => paths.as_slice(),
Self::Items(_) => &[],
}
}
#[must_use]
pub(in crate::db) fn references_field(&self, field_id: FieldId) -> bool {
match self {
Self::FieldPath(paths) => paths.iter().any(|path| path.field_id() == field_id),
Self::Items(items) => items.iter().any(|item| item.references_field(field_id)),
}
}
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) fn clone_with_renamed_field_path_root(
&self,
field_id: FieldId,
new_name: &str,
) -> Self {
match self {
Self::FieldPath(paths) => Self::FieldPath(
paths
.iter()
.map(|path| path.clone_with_renamed_root(field_id, new_name))
.collect(),
),
Self::Items(items) => Self::Items(
items
.iter()
.map(|item| item.clone_with_renamed_field_path_root(field_id, new_name))
.collect(),
),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) enum PersistedIndexKeyItemSnapshot {
FieldPath(PersistedIndexFieldPathSnapshot),
Expression(Box<PersistedIndexExpressionSnapshot>),
}
impl PersistedIndexKeyItemSnapshot {
#[must_use]
pub(in crate::db) fn references_field(&self, field_id: FieldId) -> bool {
match self {
Self::FieldPath(path) => path.field_id() == field_id,
Self::Expression(expression) => expression.source().field_id() == field_id,
}
}
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) fn clone_with_renamed_field_path_root(
&self,
field_id: FieldId,
new_name: &str,
) -> Self {
match self {
Self::FieldPath(path) => {
Self::FieldPath(path.clone_with_renamed_root(field_id, new_name))
}
Self::Expression(expression) => Self::Expression(Box::new(
expression.clone_with_renamed_source_root(field_id, new_name),
)),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct PersistedIndexFieldPathSnapshot {
field_id: FieldId,
slot: SchemaFieldSlot,
path: Vec<String>,
kind: PersistedFieldKind,
nullable: bool,
}
impl PersistedIndexFieldPathSnapshot {
#[must_use]
pub(in crate::db) const fn new(
field_id: FieldId,
slot: SchemaFieldSlot,
path: Vec<String>,
kind: PersistedFieldKind,
nullable: bool,
) -> Self {
Self {
field_id,
slot,
path,
kind,
nullable,
}
}
#[must_use]
pub(in crate::db) const fn field_id(&self) -> FieldId {
self.field_id
}
#[must_use]
pub(in crate::db) const fn slot(&self) -> SchemaFieldSlot {
self.slot
}
#[must_use]
pub(in crate::db) const fn path(&self) -> &[String] {
self.path.as_slice()
}
#[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]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) fn clone_with_renamed_root(&self, field_id: FieldId, new_name: &str) -> Self {
let mut path = self.path.clone();
if self.field_id == field_id
&& let Some(root) = path.first_mut()
{
*root = new_name.to_string();
}
Self {
field_id: self.field_id,
slot: self.slot,
path,
kind: self.kind.clone(),
nullable: self.nullable,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct PersistedIndexExpressionSnapshot {
op: PersistedIndexExpressionOp,
source: PersistedIndexFieldPathSnapshot,
input_kind: PersistedFieldKind,
output_kind: PersistedFieldKind,
canonical_text: String,
}
impl PersistedIndexExpressionSnapshot {
#[must_use]
pub(in crate::db) const fn new(
op: PersistedIndexExpressionOp,
source: PersistedIndexFieldPathSnapshot,
input_kind: PersistedFieldKind,
output_kind: PersistedFieldKind,
canonical_text: String,
) -> Self {
Self {
op,
source,
input_kind,
output_kind,
canonical_text,
}
}
#[must_use]
pub(in crate::db) const fn op(&self) -> PersistedIndexExpressionOp {
self.op
}
#[must_use]
pub(in crate::db) const fn source(&self) -> &PersistedIndexFieldPathSnapshot {
&self.source
}
#[must_use]
pub(in crate::db) const fn input_kind(&self) -> &PersistedFieldKind {
&self.input_kind
}
#[must_use]
pub(in crate::db) const fn output_kind(&self) -> &PersistedFieldKind {
&self.output_kind
}
#[must_use]
pub(in crate::db) const fn canonical_text(&self) -> &str {
self.canonical_text.as_str()
}
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) fn clone_with_renamed_source_root(
&self,
field_id: FieldId,
new_name: &str,
) -> Self {
let source = self.source.clone_with_renamed_root(field_id, new_name);
let canonical_text = canonical_expression_text_for_path(self.op, source.path());
Self {
op: self.op,
source,
input_kind: self.input_kind.clone(),
output_kind: self.output_kind.clone(),
canonical_text,
}
}
}
#[cfg(any(test, feature = "sql"))]
fn canonical_expression_text_for_path(op: PersistedIndexExpressionOp, path: &[String]) -> String {
let path = path.join(".");
match op {
PersistedIndexExpressionOp::Lower => format!("expr:v1:LOWER({path})"),
PersistedIndexExpressionOp::Upper => format!("expr:v1:UPPER({path})"),
PersistedIndexExpressionOp::Trim => format!("expr:v1:TRIM({path})"),
PersistedIndexExpressionOp::LowerTrim => format!("expr:v1:LOWER(TRIM({path}))"),
PersistedIndexExpressionOp::Date => format!("expr:v1:DATE({path})"),
PersistedIndexExpressionOp::Year => format!("expr:v1:YEAR({path})"),
PersistedIndexExpressionOp::Month => format!("expr:v1:MONTH({path})"),
PersistedIndexExpressionOp::Day => format!("expr:v1:DAY({path})"),
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum PersistedIndexExpressionOp {
Lower,
Upper,
Trim,
LowerTrim,
Date,
Year,
Month,
Day,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum PersistedFieldOrigin {
Generated,
SqlDdl,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct PersistedFieldSnapshot {
id: FieldId,
name: String,
slot: SchemaFieldSlot,
kind: PersistedFieldKind,
nested_leaves: Vec<PersistedNestedLeafSnapshot>,
nullable: bool,
default: SchemaFieldDefault,
write_policy: SchemaFieldWritePolicy,
origin: PersistedFieldOrigin,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
}
impl PersistedFieldSnapshot {
#[cfg(test)]
#[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,
nested_leaves: Vec<PersistedNestedLeafSnapshot>,
nullable: bool,
default: SchemaFieldDefault,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
) -> Self {
Self::new_with_write_policy(
id,
name,
slot,
kind,
nested_leaves,
nullable,
default,
SchemaFieldWritePolicy::none(),
storage_decode,
leaf_codec,
)
}
#[expect(
clippy::too_many_arguments,
reason = "schema snapshot construction keeps every persisted field contract explicit"
)]
#[must_use]
pub(in crate::db) const fn new_with_write_policy(
id: FieldId,
name: String,
slot: SchemaFieldSlot,
kind: PersistedFieldKind,
nested_leaves: Vec<PersistedNestedLeafSnapshot>,
nullable: bool,
default: SchemaFieldDefault,
write_policy: SchemaFieldWritePolicy,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
) -> Self {
Self::new_with_write_policy_and_origin(
id,
name,
slot,
kind,
nested_leaves,
nullable,
default,
write_policy,
PersistedFieldOrigin::Generated,
storage_decode,
leaf_codec,
)
}
#[expect(
clippy::too_many_arguments,
reason = "schema snapshot construction keeps every persisted field contract explicit"
)]
#[must_use]
pub(in crate::db) const fn new_with_write_policy_and_origin(
id: FieldId,
name: String,
slot: SchemaFieldSlot,
kind: PersistedFieldKind,
nested_leaves: Vec<PersistedNestedLeafSnapshot>,
nullable: bool,
default: SchemaFieldDefault,
write_policy: SchemaFieldWritePolicy,
origin: PersistedFieldOrigin,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
) -> Self {
Self {
id,
name,
slot,
kind,
nested_leaves,
nullable,
default,
write_policy,
origin,
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 nested_leaves(&self) -> &[PersistedNestedLeafSnapshot] {
self.nested_leaves.as_slice()
}
#[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]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) fn clone_with_default(&self, default: SchemaFieldDefault) -> Self {
Self {
id: self.id,
name: self.name.clone(),
slot: self.slot,
kind: self.kind.clone(),
nested_leaves: self.nested_leaves.clone(),
nullable: self.nullable,
default,
write_policy: self.write_policy,
origin: self.origin,
storage_decode: self.storage_decode,
leaf_codec: self.leaf_codec,
}
}
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) fn clone_with_nullable(&self, nullable: bool) -> Self {
Self {
id: self.id,
name: self.name.clone(),
slot: self.slot,
kind: self.kind.clone(),
nested_leaves: self.nested_leaves.clone(),
nullable,
default: self.default.clone(),
write_policy: self.write_policy,
origin: self.origin,
storage_decode: self.storage_decode,
leaf_codec: self.leaf_codec,
}
}
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) fn clone_with_name(&self, name: String) -> Self {
Self {
id: self.id,
name,
slot: self.slot,
kind: self.kind.clone(),
nested_leaves: self.nested_leaves.clone(),
nullable: self.nullable,
default: self.default.clone(),
write_policy: self.write_policy,
origin: self.origin,
storage_decode: self.storage_decode,
leaf_codec: self.leaf_codec,
}
}
#[must_use]
pub(in crate::db) const fn write_policy(&self) -> SchemaFieldWritePolicy {
self.write_policy
}
#[must_use]
pub(in crate::db) const fn origin(&self) -> PersistedFieldOrigin {
self.origin
}
#[must_use]
pub(in crate::db) const fn generated(&self) -> bool {
matches!(self.origin, PersistedFieldOrigin::Generated)
}
#[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, Debug, Eq, PartialEq)]
pub(in crate::db) struct PersistedNestedLeafSnapshot {
path: Vec<String>,
kind: PersistedFieldKind,
nullable: bool,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
}
impl PersistedNestedLeafSnapshot {
#[must_use]
pub(in crate::db) const fn new(
path: Vec<String>,
kind: PersistedFieldKind,
nullable: bool,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
) -> Self {
Self {
path,
kind,
nullable,
storage_decode,
leaf_codec,
}
}
#[must_use]
pub(in crate::db) const fn path(&self) -> &[String] {
self.path.as_slice()
}
#[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 storage_decode(&self) -> FieldStorageDecode {
self.storage_decode
}
#[must_use]
pub(in crate::db) const fn leaf_codec(&self) -> LeafCodec {
self.leaf_codec
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) enum SchemaFieldDefault {
None,
SlotPayload(Vec<u8>),
}
impl SchemaFieldDefault {
#[must_use]
pub(in crate::db) fn from_model_default(default: FieldDatabaseDefault) -> Self {
match default {
FieldDatabaseDefault::None => Self::None,
FieldDatabaseDefault::EncodedSlotPayload(bytes) => Self::SlotPayload(Vec::from(bytes)),
}
}
#[must_use]
pub(in crate::db) const fn is_none(&self) -> bool {
matches!(self, Self::None)
}
#[must_use]
pub(in crate::db) const fn slot_payload(&self) -> Option<&[u8]> {
match self {
Self::None => None,
Self::SlotPayload(bytes) => Some(bytes.as_slice()),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) struct SchemaFieldWritePolicy {
insert_generation: Option<FieldInsertGeneration>,
write_management: Option<FieldWriteManagement>,
}
impl SchemaFieldWritePolicy {
#[cfg(test)]
#[must_use]
pub(in crate::db) const fn none() -> Self {
Self {
insert_generation: None,
write_management: None,
}
}
#[must_use]
pub(in crate::db) const fn from_model_policies(
insert_generation: Option<FieldInsertGeneration>,
write_management: Option<FieldWriteManagement>,
) -> Self {
Self {
insert_generation,
write_management,
}
}
#[must_use]
pub(in crate::db) const fn insert_generation(self) -> Option<FieldInsertGeneration> {
self.insert_generation
}
#[must_use]
pub(in crate::db) const fn write_management(self) -> Option<FieldWriteManagement> {
self.write_management
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) enum PersistedFieldKind {
Account,
Blob {
max_len: Option<u32>,
},
Bool,
Date,
Decimal {
scale: u32,
},
Duration,
Enum {
path: String,
variants: Vec<PersistedEnumVariant>,
},
Float32,
Float64,
Int8,
Int16,
Int32,
Int64,
Int128,
IntBig {
max_bytes: u32,
},
Principal,
Subaccount,
Text {
max_len: Option<u32>,
},
Timestamp,
Nat8,
Nat16,
Nat32,
Nat64,
Nat128,
NatBig {
max_bytes: u32,
},
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 { max_len } => Self::Blob { max_len },
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::Int8 => Self::Int8,
FieldKind::Int16 => Self::Int16,
FieldKind::Int32 => Self::Int32,
FieldKind::Int64 => Self::Int64,
FieldKind::Int128 => Self::Int128,
FieldKind::IntBig { max_bytes } => Self::IntBig { max_bytes },
FieldKind::Principal => Self::Principal,
FieldKind::Subaccount => Self::Subaccount,
FieldKind::Text { max_len } => Self::Text { max_len },
FieldKind::Timestamp => Self::Timestamp,
FieldKind::Nat8 => Self::Nat8,
FieldKind::Nat16 => Self::Nat16,
FieldKind::Nat32 => Self::Nat32,
FieldKind::Nat64 => Self::Nat64,
FieldKind::Nat128 => Self::Nat128,
FieldKind::NatBig { max_bytes } => Self::NatBig { max_bytes },
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,
}
}
}
#[cfg(test)]
mod tests;