Skip to main content

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