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;
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 /// Data store path where the target entity is persisted.
105 target_store_path: &'static str,
106 key_kind: &'static Self,
107 strength: RelationStrength,
108 },
109
110 // Collections
111 List(&'static Self),
112 Set(&'static Self),
113 /// Deterministic, unordered key/value collection.
114 ///
115 /// Map fields are persistable and patchable, but not queryable or indexable.
116 Map {
117 key: &'static Self,
118 value: &'static Self,
119 },
120
121 /// Structured (non-atomic) value.
122 /// Queryability here controls whether predicates may target this field,
123 /// not whether it may be stored or updated.
124 Structured {
125 queryable: bool,
126 },
127}
128
129impl FieldKind {
130 #[must_use]
131 pub const fn value_kind(&self) -> FieldValueKind {
132 match self {
133 Self::Account
134 | Self::Blob
135 | Self::Bool
136 | Self::Date
137 | Self::Duration
138 | Self::Enum { .. }
139 | Self::Float32
140 | Self::Float64
141 | Self::Int
142 | Self::Int128
143 | Self::IntBig
144 | Self::Principal
145 | Self::Subaccount
146 | Self::Text
147 | Self::Timestamp
148 | Self::Uint
149 | Self::Uint128
150 | Self::UintBig
151 | Self::Ulid
152 | Self::Unit
153 | Self::Decimal { .. }
154 | Self::Relation { .. } => FieldValueKind::Atomic,
155 Self::List(_) | Self::Set(_) => FieldValueKind::Structured { queryable: true },
156 Self::Map { .. } => FieldValueKind::Structured { queryable: false },
157 Self::Structured { queryable } => FieldValueKind::Structured {
158 queryable: *queryable,
159 },
160 }
161 }
162
163 /// Returns `true` if this field shape is permitted in
164 /// persisted or query-visible schemas under the current
165 /// determinism policy.
166 ///
167 /// This shape-level check is structural only; query-time policy
168 /// enforcement (for example, map predicate fencing) is applied at
169 /// query construction and validation boundaries.
170 #[must_use]
171 pub const fn is_deterministic_collection_shape(&self) -> bool {
172 match self {
173 Self::Relation { key_kind, .. } => key_kind.is_deterministic_collection_shape(),
174
175 Self::List(inner) | Self::Set(inner) => inner.is_deterministic_collection_shape(),
176
177 Self::Map { key, value } => {
178 key.is_deterministic_collection_shape() && value.is_deterministic_collection_shape()
179 }
180
181 _ => true,
182 }
183 }
184}