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 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        /// Required schema-declared fractional scale for decimal fields.
50        scale: u32,
51    },
52    Duration,
53    Enum {
54        /// Fully-qualified enum type path used for strict filter normalization.
55        path: &'static str,
56    },
57    Float32,
58    Float64,
59    Int,
60    Int128,
61    IntBig,
62    Principal,
63    Subaccount,
64    Text,
65    Timestamp,
66    Uint,
67    Uint128,
68    UintBig,
69    Ulid,
70    Unit,
71
72    /// Typed relation; `key_kind` reflects the referenced key type.
73    /// `strength` encodes strong vs. weak relation intent.
74    Relation {
75        /// Fully-qualified Rust type path for diagnostics.
76        target_path: &'static str,
77        /// Stable external name used in storage keys.
78        target_entity_name: &'static str,
79        /// Data store path where the target entity is persisted.
80        target_store_path: &'static str,
81        key_kind: &'static Self,
82        strength: RelationStrength,
83    },
84
85    // Collections
86    List(&'static Self),
87    Set(&'static Self),
88    /// Deterministic, unordered key/value collection.
89    ///
90    /// Map fields are persistable and patchable, but not queryable or indexable.
91    Map {
92        key: &'static Self,
93        value: &'static Self,
94    },
95
96    /// Structured (non-atomic) value.
97    /// Queryability here controls whether predicates may target this field,
98    /// not whether it may be stored or updated.
99    Structured {
100        queryable: bool,
101    },
102}
103
104impl FieldKind {
105    #[must_use]
106    pub const fn value_kind(&self) -> FieldValueKind {
107        match self {
108            Self::Account
109            | Self::Blob
110            | Self::Bool
111            | Self::Date
112            | Self::Duration
113            | Self::Enum { .. }
114            | Self::Float32
115            | Self::Float64
116            | Self::Int
117            | Self::Int128
118            | Self::IntBig
119            | Self::Principal
120            | Self::Subaccount
121            | Self::Text
122            | Self::Timestamp
123            | Self::Uint
124            | Self::Uint128
125            | Self::UintBig
126            | Self::Ulid
127            | Self::Unit
128            | Self::Decimal { .. }
129            | Self::Relation { .. } => FieldValueKind::Atomic,
130            Self::List(_) | Self::Set(_) => FieldValueKind::Structured { queryable: true },
131            Self::Map { .. } => FieldValueKind::Structured { queryable: false },
132            Self::Structured { queryable } => FieldValueKind::Structured {
133                queryable: *queryable,
134            },
135        }
136    }
137
138    /// Returns `true` if this field shape is permitted in
139    /// persisted or query-visible schemas under the current
140    /// determinism policy.
141    ///
142    /// This shape-level check is structural only; query-time policy
143    /// enforcement (for example, map predicate fencing) is applied at
144    /// query construction and validation boundaries.
145    #[must_use]
146    pub const fn is_deterministic_collection_shape(&self) -> bool {
147        match self {
148            Self::Relation { key_kind, .. } => key_kind.is_deterministic_collection_shape(),
149
150            Self::List(inner) | Self::Set(inner) => inner.is_deterministic_collection_shape(),
151
152            Self::Map { key, value } => {
153                key.is_deterministic_collection_shape() && value.is_deterministic_collection_shape()
154            }
155
156            _ => true,
157        }
158    }
159}