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/// FieldStorageDecode
10///
11/// FieldStorageDecode captures how one persisted field payload must be
12/// interpreted at structural decode boundaries.
13/// Semantic `FieldKind` alone is not always authoritative for persisted decode:
14/// some fields intentionally store raw `Value` payloads even when their planner
15/// shape is narrower.
16///
17
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum FieldStorageDecode {
20 /// Decode the persisted field payload according to semantic `FieldKind`.
21 ByKind,
22 /// Decode the persisted field payload directly into `Value`.
23 Value,
24}
25
26///
27/// EnumVariantModel
28///
29/// EnumVariantModel carries structural decode metadata for one generated enum
30/// variant payload.
31/// Runtime structural decode uses this to stay on the field-kind contract for
32/// enum payloads instead of falling back to generic untyped CBOR decoding.
33///
34
35#[derive(Clone, Copy, Debug)]
36pub struct EnumVariantModel {
37 /// Stable schema variant tag.
38 pub(crate) ident: &'static str,
39 /// Declared payload kind when this variant carries data.
40 pub(crate) payload_kind: Option<&'static FieldKind>,
41 /// Persisted payload decode contract for the carried data.
42 pub(crate) payload_storage_decode: FieldStorageDecode,
43}
44
45impl EnumVariantModel {
46 /// Build one enum variant structural decode descriptor.
47 #[must_use]
48 pub const fn new(
49 ident: &'static str,
50 payload_kind: Option<&'static FieldKind>,
51 payload_storage_decode: FieldStorageDecode,
52 ) -> Self {
53 Self {
54 ident,
55 payload_kind,
56 payload_storage_decode,
57 }
58 }
59
60 /// Return the stable schema variant tag.
61 #[must_use]
62 pub const fn ident(&self) -> &'static str {
63 self.ident
64 }
65
66 /// Return the declared payload kind when this variant carries data.
67 #[must_use]
68 pub const fn payload_kind(&self) -> Option<&'static FieldKind> {
69 self.payload_kind
70 }
71
72 /// Return the persisted payload decode contract for this variant.
73 #[must_use]
74 pub const fn payload_storage_decode(&self) -> FieldStorageDecode {
75 self.payload_storage_decode
76 }
77}
78
79///
80/// FieldModel
81///
82/// Runtime field metadata surfaced by macro-generated `EntityModel` values.
83///
84/// This is the smallest unit consumed by predicate validation, planning,
85/// and executor-side plan checks.
86///
87
88#[derive(Debug)]
89pub struct FieldModel {
90 /// Field name as used in predicates and indexing.
91 pub(crate) name: &'static str,
92 /// Runtime type shape (no schema-layer graph nodes).
93 pub(crate) kind: FieldKind,
94 /// Persisted field decode contract used by structural runtime decoders.
95 pub(crate) storage_decode: FieldStorageDecode,
96}
97
98impl FieldModel {
99 /// Build one runtime field descriptor.
100 #[must_use]
101 pub const fn new(name: &'static str, kind: FieldKind) -> Self {
102 Self::new_with_storage_decode(name, kind, FieldStorageDecode::ByKind)
103 }
104
105 /// Build one runtime field descriptor with an explicit persisted decode contract.
106 #[must_use]
107 pub const fn new_with_storage_decode(
108 name: &'static str,
109 kind: FieldKind,
110 storage_decode: FieldStorageDecode,
111 ) -> Self {
112 Self {
113 name,
114 kind,
115 storage_decode,
116 }
117 }
118
119 /// Return the stable field name.
120 #[must_use]
121 pub const fn name(&self) -> &'static str {
122 self.name
123 }
124
125 /// Return the runtime type-kind descriptor.
126 #[must_use]
127 pub const fn kind(&self) -> FieldKind {
128 self.kind
129 }
130
131 /// Return the persisted field decode contract.
132 #[must_use]
133 pub const fn storage_decode(&self) -> FieldStorageDecode {
134 self.storage_decode
135 }
136}
137
138///
139/// RelationStrength
140///
141/// Explicit relation intent for save-time referential integrity.
142///
143
144#[derive(Clone, Copy, Debug, Eq, PartialEq)]
145pub enum RelationStrength {
146 Strong,
147 Weak,
148}
149
150///
151/// FieldKind
152///
153/// Minimal runtime type surface needed by planning, validation, and execution.
154///
155/// This is aligned with `Value` variants and intentionally lossy: it encodes
156/// only the shape required for predicate compatibility and index planning.
157///
158
159#[derive(Clone, Copy, Debug)]
160pub enum FieldKind {
161 // Scalar primitives
162 Account,
163 Blob,
164 Bool,
165 Date,
166 Decimal {
167 /// Required schema-declared fractional scale for decimal fields.
168 scale: u32,
169 },
170 Duration,
171 Enum {
172 /// Fully-qualified enum type path used for strict filter normalization.
173 path: &'static str,
174 /// Declared per-variant payload decode metadata.
175 variants: &'static [EnumVariantModel],
176 },
177 Float32,
178 Float64,
179 Int,
180 Int128,
181 IntBig,
182 Principal,
183 Subaccount,
184 Text,
185 Timestamp,
186 Uint,
187 Uint128,
188 UintBig,
189 Ulid,
190 Unit,
191
192 /// Typed relation; `key_kind` reflects the referenced key type.
193 /// `strength` encodes strong vs. weak relation intent.
194 Relation {
195 /// Fully-qualified Rust type path for diagnostics.
196 target_path: &'static str,
197 /// Stable external name used in storage keys.
198 target_entity_name: &'static str,
199 /// Stable runtime identity used on hot execution paths.
200 target_entity_tag: EntityTag,
201 /// Data store path where the target entity is persisted.
202 target_store_path: &'static str,
203 key_kind: &'static Self,
204 strength: RelationStrength,
205 },
206
207 // Collections
208 List(&'static Self),
209 Set(&'static Self),
210 /// Deterministic, unordered key/value collection.
211 ///
212 /// Map fields are persistable and patchable, but not queryable or indexable.
213 Map {
214 key: &'static Self,
215 value: &'static Self,
216 },
217
218 /// Structured (non-atomic) value.
219 /// Queryability here controls whether predicates may target this field,
220 /// not whether it may be stored or updated.
221 Structured {
222 queryable: bool,
223 },
224}
225
226impl FieldKind {
227 #[must_use]
228 pub const fn value_kind(&self) -> FieldValueKind {
229 match self {
230 Self::Account
231 | Self::Blob
232 | Self::Bool
233 | Self::Date
234 | Self::Duration
235 | Self::Enum { .. }
236 | Self::Float32
237 | Self::Float64
238 | Self::Int
239 | Self::Int128
240 | Self::IntBig
241 | Self::Principal
242 | Self::Subaccount
243 | Self::Text
244 | Self::Timestamp
245 | Self::Uint
246 | Self::Uint128
247 | Self::UintBig
248 | Self::Ulid
249 | Self::Unit
250 | Self::Decimal { .. }
251 | Self::Relation { .. } => FieldValueKind::Atomic,
252 Self::List(_) | Self::Set(_) => FieldValueKind::Structured { queryable: true },
253 Self::Map { .. } => FieldValueKind::Structured { queryable: false },
254 Self::Structured { queryable } => FieldValueKind::Structured {
255 queryable: *queryable,
256 },
257 }
258 }
259
260 /// Returns `true` if this field shape is permitted in
261 /// persisted or query-visible schemas under the current
262 /// determinism policy.
263 ///
264 /// This shape-level check is structural only; query-time policy
265 /// enforcement (for example, map predicate fencing) is applied at
266 /// query construction and validation boundaries.
267 #[must_use]
268 pub const fn is_deterministic_collection_shape(&self) -> bool {
269 match self {
270 Self::Relation { key_kind, .. } => key_kind.is_deterministic_collection_shape(),
271
272 Self::List(inner) | Self::Set(inner) => inner.is_deterministic_collection_shape(),
273
274 Self::Map { key, value } => {
275 key.is_deterministic_collection_shape() && value.is_deterministic_collection_shape()
276 }
277
278 _ => true,
279 }
280 }
281}