1use candid::CandidType;
10use serde::{Deserialize, Serialize};
11
12#[derive(
19 CandidType, Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize,
20)]
21pub enum ScalarKind {
22 Account,
23 Blob,
24 Bool,
25 Date,
26 Decimal,
27 Duration,
28 Enum,
29 Float32,
30 Float64,
31 Int,
32 Int128,
33 IntBig,
34 Principal,
35 Subaccount,
36 Text,
37 Timestamp,
38 Nat,
39 Nat128,
40 NatBig,
41 Ulid,
42 Unit,
43}
44
45impl ScalarKind {
46 #[must_use]
48 pub const fn metadata(self) -> ScalarMetadata {
49 scalar_kind_registry!(metadata_from_registry, self)
50 }
51
52 #[must_use]
54 pub const fn coercion_family(self) -> ScalarCoercionFamily {
55 self.metadata().family
56 }
57
58 #[must_use]
60 pub const fn is_numeric_value(self) -> bool {
61 self.metadata().is_numeric_value
62 }
63
64 #[must_use]
66 pub const fn supports_numeric_coercion(self) -> bool {
67 self.metadata().supports_numeric_coercion
68 }
69
70 #[must_use]
72 pub const fn supports_arithmetic(self) -> bool {
73 self.metadata().supports_arithmetic
74 }
75
76 #[must_use]
78 pub const fn supports_equality(self) -> bool {
79 self.metadata().supports_equality
80 }
81
82 #[must_use]
84 pub const fn supports_ordering(self) -> bool {
85 self.metadata().supports_ordering
86 }
87
88 #[must_use]
90 pub const fn is_keyable(self) -> bool {
91 self.metadata().is_keyable
92 }
93
94 #[must_use]
96 pub const fn is_primary_key_component_encodable(self) -> bool {
97 self.metadata().is_primary_key_component_encodable
98 }
99}
100
101#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
108#[expect(clippy::struct_excessive_bools)]
109pub struct ScalarMetadata {
110 family: ScalarCoercionFamily,
111 is_numeric_value: bool,
112 supports_numeric_coercion: bool,
113 supports_arithmetic: bool,
114 supports_equality: bool,
115 supports_ordering: bool,
116 is_keyable: bool,
117 is_primary_key_component_encodable: bool,
118}
119
120impl ScalarMetadata {
121 #[must_use]
123 pub const fn family(self) -> ScalarCoercionFamily {
124 self.family
125 }
126
127 #[must_use]
129 pub const fn is_numeric_value(self) -> bool {
130 self.is_numeric_value
131 }
132
133 #[must_use]
135 pub const fn supports_numeric_coercion(self) -> bool {
136 self.supports_numeric_coercion
137 }
138
139 #[must_use]
141 pub const fn supports_arithmetic(self) -> bool {
142 self.supports_arithmetic
143 }
144
145 #[must_use]
147 pub const fn supports_equality(self) -> bool {
148 self.supports_equality
149 }
150
151 #[must_use]
153 pub const fn supports_ordering(self) -> bool {
154 self.supports_ordering
155 }
156
157 #[must_use]
159 pub const fn is_keyable(self) -> bool {
160 self.is_keyable
161 }
162
163 #[must_use]
165 pub const fn is_primary_key_component_encodable(self) -> bool {
166 self.is_primary_key_component_encodable
167 }
168}
169
170#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
177pub enum ScalarCoercionFamily {
178 Numeric,
179 Textual,
180 Identifier,
181 Enum,
182 Blob,
183 Bool,
184 Unit,
185}
186
187pub const ALL_SCALAR_KINDS: [ScalarKind; 21] = scalar_kind_registry!(all_kinds_from_registry);
189
190#[cfg(test)]
191mod tests {
192 use super::{ALL_SCALAR_KINDS, ScalarKind};
193 use std::collections::HashSet;
194
195 const EXPECTED_SCALAR_KINDS: [ScalarKind; 21] = [
196 ScalarKind::Account,
197 ScalarKind::Blob,
198 ScalarKind::Bool,
199 ScalarKind::Date,
200 ScalarKind::Decimal,
201 ScalarKind::Duration,
202 ScalarKind::Enum,
203 ScalarKind::Float32,
204 ScalarKind::Float64,
205 ScalarKind::Int,
206 ScalarKind::Int128,
207 ScalarKind::IntBig,
208 ScalarKind::Principal,
209 ScalarKind::Subaccount,
210 ScalarKind::Text,
211 ScalarKind::Timestamp,
212 ScalarKind::Nat,
213 ScalarKind::Nat128,
214 ScalarKind::NatBig,
215 ScalarKind::Ulid,
216 ScalarKind::Unit,
217 ];
218
219 #[test]
220 fn all_scalar_kinds_has_expected_length_and_order() {
221 assert_eq!(ALL_SCALAR_KINDS, EXPECTED_SCALAR_KINDS);
222 }
223
224 #[test]
225 fn all_scalar_kinds_is_unique() {
226 let unique = ALL_SCALAR_KINDS.into_iter().collect::<HashSet<_>>();
227
228 assert_eq!(unique.len(), ALL_SCALAR_KINDS.len());
229 }
230
231 #[test]
232 fn all_scalar_kind_variants_are_audited() {
233 for kind in EXPECTED_SCALAR_KINDS {
234 assert_variant_is_known(kind);
235 }
236 }
237
238 #[test]
239 fn all_scalar_kinds_metadata_is_available() {
240 for kind in ALL_SCALAR_KINDS {
241 let metadata = kind.metadata();
242
243 assert_eq!(kind.coercion_family(), metadata.family());
244 assert_eq!(kind.is_numeric_value(), metadata.is_numeric_value());
245 assert_eq!(
246 kind.supports_numeric_coercion(),
247 metadata.supports_numeric_coercion(),
248 );
249 assert_eq!(kind.supports_arithmetic(), metadata.supports_arithmetic());
250 assert_eq!(kind.supports_equality(), metadata.supports_equality());
251 assert_eq!(kind.supports_ordering(), metadata.supports_ordering());
252 assert_eq!(kind.is_keyable(), metadata.is_keyable());
253 assert_eq!(
254 kind.is_primary_key_component_encodable(),
255 metadata.is_primary_key_component_encodable(),
256 );
257 }
258 }
259
260 #[test]
261 fn enum_capabilities_are_equality_only() {
262 assert!(ScalarKind::Enum.supports_equality());
263 assert!(!ScalarKind::Enum.supports_ordering());
264 assert!(!ScalarKind::Enum.is_keyable());
265 }
266
267 fn assert_variant_is_known(kind: ScalarKind) {
268 match kind {
269 ScalarKind::Account
270 | ScalarKind::Blob
271 | ScalarKind::Bool
272 | ScalarKind::Date
273 | ScalarKind::Decimal
274 | ScalarKind::Duration
275 | ScalarKind::Enum
276 | ScalarKind::Float32
277 | ScalarKind::Float64
278 | ScalarKind::Int
279 | ScalarKind::Int128
280 | ScalarKind::IntBig
281 | ScalarKind::Principal
282 | ScalarKind::Subaccount
283 | ScalarKind::Text
284 | ScalarKind::Timestamp
285 | ScalarKind::Nat
286 | ScalarKind::Nat128
287 | ScalarKind::NatBig
288 | ScalarKind::Ulid
289 | ScalarKind::Unit => {}
290 }
291 }
292}