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