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 U256,
42 Ulid,
43 Unit,
44}
45
46impl ScalarKind {
47 #[must_use]
49 pub const fn metadata(self) -> ScalarMetadata {
50 scalar_kind_registry!(metadata_from_registry, self)
51 }
52
53 #[must_use]
55 pub const fn coercion_family(self) -> ScalarCoercionFamily {
56 self.metadata().family
57 }
58
59 #[must_use]
61 pub const fn is_numeric_value(self) -> bool {
62 self.metadata().is_numeric_value
63 }
64
65 #[must_use]
67 pub const fn supports_numeric_coercion(self) -> bool {
68 self.metadata().supports_numeric_coercion
69 }
70
71 #[must_use]
73 pub const fn supports_arithmetic(self) -> bool {
74 self.metadata().supports_arithmetic
75 }
76
77 #[must_use]
79 pub const fn supports_equality(self) -> bool {
80 self.metadata().supports_equality
81 }
82
83 #[must_use]
85 pub const fn supports_ordering(self) -> bool {
86 self.metadata().supports_ordering
87 }
88
89 #[must_use]
91 pub const fn is_keyable(self) -> bool {
92 self.metadata().is_keyable
93 }
94
95 #[must_use]
97 pub const fn is_primary_key_component_encodable(self) -> bool {
98 self.metadata().is_primary_key_component_encodable
99 }
100}
101
102#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
109#[expect(clippy::struct_excessive_bools)]
110pub struct ScalarMetadata {
111 family: ScalarCoercionFamily,
112 is_numeric_value: bool,
113 supports_numeric_coercion: bool,
114 supports_arithmetic: bool,
115 supports_equality: bool,
116 supports_ordering: bool,
117 is_keyable: bool,
118 is_primary_key_component_encodable: bool,
119}
120
121impl ScalarMetadata {
122 #[must_use]
124 pub const fn family(self) -> ScalarCoercionFamily {
125 self.family
126 }
127
128 #[must_use]
130 pub const fn is_numeric_value(self) -> bool {
131 self.is_numeric_value
132 }
133
134 #[must_use]
136 pub const fn supports_numeric_coercion(self) -> bool {
137 self.supports_numeric_coercion
138 }
139
140 #[must_use]
142 pub const fn supports_arithmetic(self) -> bool {
143 self.supports_arithmetic
144 }
145
146 #[must_use]
148 pub const fn supports_equality(self) -> bool {
149 self.supports_equality
150 }
151
152 #[must_use]
154 pub const fn supports_ordering(self) -> bool {
155 self.supports_ordering
156 }
157
158 #[must_use]
160 pub const fn is_keyable(self) -> bool {
161 self.is_keyable
162 }
163
164 #[must_use]
166 pub const fn is_primary_key_component_encodable(self) -> bool {
167 self.is_primary_key_component_encodable
168 }
169}
170
171#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
178pub enum ScalarCoercionFamily {
179 Numeric,
180 Textual,
181 Identifier,
182 Enum,
183 Blob,
184 Bool,
185 Unit,
186}
187
188pub const ALL_SCALAR_KINDS: [ScalarKind; 22] = scalar_kind_registry!(all_kinds_from_registry);
190
191#[cfg(test)]
192mod tests {
193 use super::{ALL_SCALAR_KINDS, ScalarKind};
194 use std::collections::HashSet;
195
196 const EXPECTED_SCALAR_KINDS: [ScalarKind; 22] = [
197 ScalarKind::Account,
198 ScalarKind::Blob,
199 ScalarKind::Bool,
200 ScalarKind::Date,
201 ScalarKind::Decimal,
202 ScalarKind::Duration,
203 ScalarKind::Enum,
204 ScalarKind::Float32,
205 ScalarKind::Float64,
206 ScalarKind::Int,
207 ScalarKind::Int128,
208 ScalarKind::IntBig,
209 ScalarKind::Principal,
210 ScalarKind::Subaccount,
211 ScalarKind::Text,
212 ScalarKind::Timestamp,
213 ScalarKind::Nat,
214 ScalarKind::Nat128,
215 ScalarKind::NatBig,
216 ScalarKind::U256,
217 ScalarKind::Ulid,
218 ScalarKind::Unit,
219 ];
220
221 #[test]
222 fn all_scalar_kinds_has_expected_length_and_order() {
223 assert_eq!(ALL_SCALAR_KINDS, EXPECTED_SCALAR_KINDS);
224 }
225
226 #[test]
227 fn all_scalar_kinds_is_unique() {
228 let unique = ALL_SCALAR_KINDS.into_iter().collect::<HashSet<_>>();
229
230 assert_eq!(unique.len(), ALL_SCALAR_KINDS.len());
231 }
232
233 #[test]
234 fn all_scalar_kind_variants_are_audited() {
235 for kind in EXPECTED_SCALAR_KINDS {
236 assert_variant_is_known(kind);
237 }
238 }
239
240 #[test]
241 fn all_scalar_kinds_metadata_is_available() {
242 for kind in ALL_SCALAR_KINDS {
243 let metadata = kind.metadata();
244
245 assert_eq!(kind.coercion_family(), metadata.family());
246 assert_eq!(kind.is_numeric_value(), metadata.is_numeric_value());
247 assert_eq!(
248 kind.supports_numeric_coercion(),
249 metadata.supports_numeric_coercion(),
250 );
251 assert_eq!(kind.supports_arithmetic(), metadata.supports_arithmetic());
252 assert_eq!(kind.supports_equality(), metadata.supports_equality());
253 assert_eq!(kind.supports_ordering(), metadata.supports_ordering());
254 assert_eq!(kind.is_keyable(), metadata.is_keyable());
255 assert_eq!(
256 kind.is_primary_key_component_encodable(),
257 metadata.is_primary_key_component_encodable(),
258 );
259 }
260 }
261
262 #[test]
263 fn enum_capabilities_are_equality_only() {
264 assert!(ScalarKind::Enum.supports_equality());
265 assert!(!ScalarKind::Enum.supports_ordering());
266 assert!(!ScalarKind::Enum.is_keyable());
267 }
268
269 #[test]
270 fn unit_capabilities_include_trivial_total_order() {
271 let metadata = ScalarKind::Unit.metadata();
272
273 assert!(metadata.supports_equality());
274 assert!(metadata.supports_ordering());
275 assert!(metadata.is_keyable());
276 assert!(metadata.is_primary_key_component_encodable());
277 assert!(!metadata.supports_numeric_coercion());
278 assert!(!metadata.supports_arithmetic());
279 }
280
281 fn assert_variant_is_known(kind: ScalarKind) {
282 match kind {
283 ScalarKind::Account
284 | ScalarKind::Blob
285 | ScalarKind::Bool
286 | ScalarKind::Date
287 | ScalarKind::Decimal
288 | ScalarKind::Duration
289 | ScalarKind::Enum
290 | ScalarKind::Float32
291 | ScalarKind::Float64
292 | ScalarKind::Int
293 | ScalarKind::Int128
294 | ScalarKind::IntBig
295 | ScalarKind::Principal
296 | ScalarKind::Subaccount
297 | ScalarKind::Text
298 | ScalarKind::Timestamp
299 | ScalarKind::Nat
300 | ScalarKind::Nat128
301 | ScalarKind::NatBig
302 | ScalarKind::U256
303 | ScalarKind::Ulid
304 | ScalarKind::Unit => {}
305 }
306 }
307}