Skip to main content

icydb_model/
types.rs

1use crate::prelude::*;
2use icydb_schema::ScalarKind;
3use std::str::FromStr;
4
5//
6// Cardinality
7//
8// Schema-level multiplicity marker used by codegen and validation passes.
9// `One` means a required single value.
10// `Opt` means an optional slot (nullable / absent is valid).
11// `Many` means repeated values (for list/set-like shapes).
12//
13
14#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
15pub enum Cardinality {
16    #[default]
17    One,
18    Opt,
19    Many,
20}
21
22impl FromStr for Cardinality {
23    type Err = &'static str;
24
25    fn from_str(s: &str) -> Result<Self, Self::Err> {
26        match s {
27            "One" => Ok(Self::One),
28            "Opt" => Ok(Self::Opt),
29            "Many" => Ok(Self::Many),
30            _ => Err("unknown Cardinality"),
31        }
32    }
33}
34
35//
36// Primitive
37//
38// Scalar primitive catalog used by the application authoring graph.
39// This enum is the canonical source for primitive capability checks
40// (ordering, arithmetic, casting, key-encoding, and hashing support).
41//
42
43#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
44#[remain::sorted]
45pub enum Primitive {
46    Account,
47    Blob,
48    Bool,
49    Date,
50    Decimal,
51    Duration,
52    Float32,
53    Float64,
54    Int8,
55    Int16,
56    Int32,
57    Int64,
58    Int128,
59    IntBig,
60    Nat8,
61    Nat16,
62    Nat32,
63    Nat64,
64    Nat128,
65    NatBig,
66    Principal,
67    Subaccount,
68    Text,
69    Timestamp,
70    Ulid,
71    Unit,
72}
73
74impl FromStr for Primitive {
75    type Err = &'static str;
76
77    fn from_str(s: &str) -> Result<Self, Self::Err> {
78        match s {
79            "Account" => Ok(Self::Account),
80            "Blob" => Ok(Self::Blob),
81            "Bool" => Ok(Self::Bool),
82            "Date" => Ok(Self::Date),
83            "Decimal" => Ok(Self::Decimal),
84            "Duration" => Ok(Self::Duration),
85            "Float32" => Ok(Self::Float32),
86            "Float64" => Ok(Self::Float64),
87            "Int8" => Ok(Self::Int8),
88            "Int16" => Ok(Self::Int16),
89            "Int32" => Ok(Self::Int32),
90            "Int64" => Ok(Self::Int64),
91            "Int128" => Ok(Self::Int128),
92            "IntBig" => Ok(Self::IntBig),
93            "Nat8" => Ok(Self::Nat8),
94            "Nat16" => Ok(Self::Nat16),
95            "Nat32" => Ok(Self::Nat32),
96            "Nat64" => Ok(Self::Nat64),
97            "Nat128" => Ok(Self::Nat128),
98            "NatBig" => Ok(Self::NatBig),
99            "Principal" => Ok(Self::Principal),
100            "Subaccount" => Ok(Self::Subaccount),
101            "Text" => Ok(Self::Text),
102            "Timestamp" => Ok(Self::Timestamp),
103            "Ulid" => Ok(Self::Ulid),
104            "Unit" => Ok(Self::Unit),
105            _ => Err("unknown Primitive"),
106        }
107    }
108}
109
110const fn primitive_scalar_kind(primitive: Primitive) -> ScalarKind {
111    match primitive {
112        Primitive::Account => ScalarKind::Account,
113        Primitive::Blob => ScalarKind::Blob,
114        Primitive::Bool => ScalarKind::Bool,
115        Primitive::Date => ScalarKind::Date,
116        Primitive::Decimal => ScalarKind::Decimal,
117        Primitive::Duration => ScalarKind::Duration,
118        Primitive::Float32 => ScalarKind::Float32,
119        Primitive::Float64 => ScalarKind::Float64,
120        Primitive::Int8 | Primitive::Int16 | Primitive::Int32 | Primitive::Int64 => ScalarKind::Int,
121        Primitive::Int128 => ScalarKind::Int128,
122        Primitive::IntBig => ScalarKind::IntBig,
123        Primitive::Nat8 | Primitive::Nat16 | Primitive::Nat32 | Primitive::Nat64 => ScalarKind::Nat,
124        Primitive::Nat128 => ScalarKind::Nat128,
125        Primitive::NatBig => ScalarKind::NatBig,
126        Primitive::Principal => ScalarKind::Principal,
127        Primitive::Subaccount => ScalarKind::Subaccount,
128        Primitive::Text => ScalarKind::Text,
129        Primitive::Timestamp => ScalarKind::Timestamp,
130        Primitive::Ulid => ScalarKind::Ulid,
131        Primitive::Unit => ScalarKind::Unit,
132    }
133}
134
135impl Primitive {
136    #[must_use]
137    pub const fn supports_arithmetic(self) -> bool {
138        primitive_scalar_kind(self).supports_arithmetic()
139    }
140
141    #[must_use]
142    pub const fn is_primary_key_component_encodable(self) -> bool {
143        primitive_scalar_kind(self).is_primary_key_component_encodable()
144    }
145
146    #[must_use]
147    pub const fn is_primary_key_encodable(self) -> bool {
148        primitive_scalar_kind(self).is_primary_key_component_encodable()
149    }
150
151    #[must_use]
152    pub const fn supports_remainder(self) -> bool {
153        matches!(
154            self,
155            Self::Decimal
156                | Self::Int8
157                | Self::Int16
158                | Self::Int32
159                | Self::Int64
160                | Self::Int128
161                | Self::Nat8
162                | Self::Nat16
163                | Self::Nat32
164                | Self::Nat64
165                | Self::Nat128
166        )
167    }
168
169    #[must_use]
170    pub const fn supports_copy(self) -> bool {
171        !matches!(self, Self::Blob | Self::IntBig | Self::NatBig | Self::Text)
172    }
173
174    #[must_use]
175    pub const fn supports_hash(self) -> bool {
176        !matches!(self, Self::Blob | Self::Unit)
177    }
178
179    // NumericValue can fallibly route all numeric-like primitives through Decimal.
180    #[must_use]
181    pub const fn supports_numeric_value(self) -> bool {
182        matches!(
183            self,
184            Self::Date
185                | Self::Decimal
186                | Self::Duration
187                | Self::Int8
188                | Self::Int16
189                | Self::Int32
190                | Self::Int64
191                | Self::Int128
192                | Self::IntBig
193                | Self::Float32
194                | Self::Float64
195                | Self::Nat8
196                | Self::Nat16
197                | Self::Nat32
198                | Self::Nat64
199                | Self::Nat128
200                | Self::NatBig
201                | Self::Timestamp
202        )
203    }
204
205    // both Ord and PartialOrd
206    #[must_use]
207    pub const fn supports_ord(self) -> bool {
208        primitive_scalar_kind(self).supports_ordering()
209    }
210
211    //
212    // grouped helpers
213    //
214
215    #[must_use]
216    pub const fn is_decimal(self) -> bool {
217        matches!(self, Self::Decimal)
218    }
219
220    // is_numeric
221    // Includes ints, floats, and Decimal.
222    #[must_use]
223    pub const fn is_numeric(self) -> bool {
224        self.is_int() || self.is_float() || self.is_decimal()
225    }
226
227    #[must_use]
228    pub const fn is_float(self) -> bool {
229        matches!(self, Self::Float32 | Self::Float64)
230    }
231
232    #[must_use]
233    pub const fn is_signed_int(self) -> bool {
234        matches!(
235            self,
236            Self::Int8 | Self::Int16 | Self::Int32 | Self::Int64 | Self::Int128 | Self::IntBig
237        )
238    }
239
240    #[must_use]
241    pub const fn is_unsigned_int(self) -> bool {
242        matches!(
243            self,
244            Self::Nat8 | Self::Nat16 | Self::Nat32 | Self::Nat64 | Self::Nat128 | Self::NatBig
245        )
246    }
247
248    #[must_use]
249    pub const fn is_int(self) -> bool {
250        self.is_signed_int() || self.is_unsigned_int()
251    }
252}