icydb_core/model/field.rs
1//! Module: model::field
2//! Responsibility: module-local ownership and contracts for model::field.
3//! Does not own: cross-module orchestration outside this module.
4//! Boundary: exposes this module API while keeping implementation details internal.
5
6use crate::{traits::FieldValueKind, types::EntityTag};
7
8///
9/// FieldModel
10///
11/// Runtime field metadata surfaced by macro-generated `EntityModel` values.
12///
13/// This is the smallest unit consumed by predicate validation, planning,
14/// and executor-side plan checks.
15///
16
17#[derive(Debug)]
18pub struct FieldModel {
19 /// Field name as used in predicates and indexing.
20 pub(crate) name: &'static str,
21 /// Runtime type shape (no schema-layer graph nodes).
22 pub(crate) kind: FieldKind,
23}
24
25impl FieldModel {
26 /// Build one runtime field descriptor.
27 #[must_use]
28 pub const fn new(name: &'static str, kind: FieldKind) -> Self {
29 Self { name, kind }
30 }
31
32 /// Return the stable field name.
33 #[must_use]
34 pub const fn name(&self) -> &'static str {
35 self.name
36 }
37
38 /// Return the runtime type-kind descriptor.
39 #[must_use]
40 pub const fn kind(&self) -> FieldKind {
41 self.kind
42 }
43}
44
45///
46/// RelationStrength
47///
48/// Explicit relation intent for save-time referential integrity.
49///
50
51#[derive(Clone, Copy, Debug, Eq, PartialEq)]
52pub enum RelationStrength {
53 Strong,
54 Weak,
55}
56
57///
58/// FieldKind
59///
60/// Minimal runtime type surface needed by planning, validation, and execution.
61///
62/// This is aligned with `Value` variants and intentionally lossy: it encodes
63/// only the shape required for predicate compatibility and index planning.
64///
65
66#[derive(Clone, Copy, Debug)]
67pub enum FieldKind {
68 // Scalar primitives
69 Account,
70 Blob,
71 Bool,
72 Date,
73 Decimal {
74 /// Required schema-declared fractional scale for decimal fields.
75 scale: u32,
76 },
77 Duration,
78 Enum {
79 /// Fully-qualified enum type path used for strict filter normalization.
80 path: &'static str,
81 },
82 Float32,
83 Float64,
84 Int,
85 Int128,
86 IntBig,
87 Principal,
88 Subaccount,
89 Text,
90 Timestamp,
91 Uint,
92 Uint128,
93 UintBig,
94 Ulid,
95 Unit,
96
97 /// Typed relation; `key_kind` reflects the referenced key type.
98 /// `strength` encodes strong vs. weak relation intent.
99 Relation {
100 /// Fully-qualified Rust type path for diagnostics.
101 target_path: &'static str,
102 /// Stable external name used in storage keys.
103 target_entity_name: &'static str,
104 /// Stable runtime identity used on hot execution paths.
105 target_entity_tag: EntityTag,
106 /// Data store path where the target entity is persisted.
107 target_store_path: &'static str,
108 key_kind: &'static Self,
109 strength: RelationStrength,
110 },
111
112 // Collections
113 List(&'static Self),
114 Set(&'static Self),
115 /// Deterministic, unordered key/value collection.
116 ///
117 /// Map fields are persistable and patchable, but not queryable or indexable.
118 Map {
119 key: &'static Self,
120 value: &'static Self,
121 },
122
123 /// Structured (non-atomic) value.
124 /// Queryability here controls whether predicates may target this field,
125 /// not whether it may be stored or updated.
126 Structured {
127 queryable: bool,
128 },
129}
130
131impl FieldKind {
132 #[must_use]
133 pub const fn value_kind(&self) -> FieldValueKind {
134 match self {
135 Self::Account
136 | Self::Blob
137 | Self::Bool
138 | Self::Date
139 | Self::Duration
140 | Self::Enum { .. }
141 | Self::Float32
142 | Self::Float64
143 | Self::Int
144 | Self::Int128
145 | Self::IntBig
146 | Self::Principal
147 | Self::Subaccount
148 | Self::Text
149 | Self::Timestamp
150 | Self::Uint
151 | Self::Uint128
152 | Self::UintBig
153 | Self::Ulid
154 | Self::Unit
155 | Self::Decimal { .. }
156 | Self::Relation { .. } => FieldValueKind::Atomic,
157 Self::List(_) | Self::Set(_) => FieldValueKind::Structured { queryable: true },
158 Self::Map { .. } => FieldValueKind::Structured { queryable: false },
159 Self::Structured { queryable } => FieldValueKind::Structured {
160 queryable: *queryable,
161 },
162 }
163 }
164
165 /// Returns `true` if this field shape is permitted in
166 /// persisted or query-visible schemas under the current
167 /// determinism policy.
168 ///
169 /// This shape-level check is structural only; query-time policy
170 /// enforcement (for example, map predicate fencing) is applied at
171 /// query construction and validation boundaries.
172 #[must_use]
173 pub const fn is_deterministic_collection_shape(&self) -> bool {
174 match self {
175 Self::Relation { key_kind, .. } => key_kind.is_deterministic_collection_shape(),
176
177 Self::List(inner) | Self::Set(inner) => inner.is_deterministic_collection_shape(),
178
179 Self::Map { key, value } => {
180 key.is_deterministic_collection_shape() && value.is_deterministic_collection_shape()
181 }
182
183 _ => true,
184 }
185 }
186}