1use crate::prelude::*;
2use candid::CandidType;
3use darling::FromMeta;
4use derive_more::{Display, FromStr};
5use proc_macro2::TokenStream;
6use quote::{ToTokens, format_ident, quote};
7
8#[derive(
13 CandidType, Clone, Copy, Default, Debug, Deserialize, Display, Eq, FromStr, PartialEq, Serialize,
14)]
15pub enum Cardinality {
16 #[default]
17 One,
18 Opt,
19 Many,
20}
21
22impl FromMeta for Cardinality {
23 fn from_string(s: &str) -> Result<Self, darling::Error> {
24 s.parse::<Self>()
25 .map_err(|_| darling::Error::unknown_value(s))
26 }
27}
28
29impl ToTokens for Cardinality {
30 fn to_tokens(&self, tokens: &mut TokenStream) {
31 let ident = format_ident!("{self}");
32
33 tokens.extend(quote!(::icydb::schema::types::Cardinality::#ident));
34 }
35}
36
37#[derive(
42 CandidType, Clone, Copy, Debug, Deserialize, Display, Eq, PartialEq, FromStr, Serialize,
43)]
44#[remain::sorted]
45pub enum Primitive {
46 Account,
47 Blob,
48 Bool,
49 Date,
50 Decimal,
51 Duration,
52 E8s,
53 E18s,
54 Float32,
55 Float64,
56 Int,
57 Int8,
58 Int16,
59 Int32,
60 Int64,
61 Int128,
62 Nat,
63 Nat8,
64 Nat16,
65 Nat32,
66 Nat64,
67 Nat128,
68 Principal,
69 Subaccount,
70 Text,
71 Timestamp,
72 Ulid,
73 Unit,
74}
75
76macro_rules! primitive_matches_scalar {
78 ( $primitive:expr, Int ) => {
79 matches!(
80 $primitive,
81 Primitive::Int8 | Primitive::Int16 | Primitive::Int32 | Primitive::Int64
82 )
83 };
84 ( $primitive:expr, Uint ) => {
85 matches!(
86 $primitive,
87 Primitive::Nat8 | Primitive::Nat16 | Primitive::Nat32 | Primitive::Nat64
88 )
89 };
90 ( $primitive:expr, Enum ) => {
91 false
92 };
93 ( $primitive:expr, IntBig ) => {
94 matches!($primitive, Primitive::Int)
95 };
96 ( $primitive:expr, UintBig ) => {
97 matches!($primitive, Primitive::Nat)
98 };
99 ( $primitive:expr, Uint128 ) => {
100 matches!($primitive, Primitive::Nat128)
101 };
102 ( $primitive:expr, $scalar:ident ) => {
103 matches!($primitive, Primitive::$scalar)
104 };
105}
106
107macro_rules! primitive_supports_arithmetic_from_registry {
108 ( @args $primitive:expr; @entries $( ($scalar:ident, $family:expr, $value_pat:pat, is_numeric_value = $is_numeric:expr, supports_numeric_coercion = $supports_numeric_coercion:expr, supports_arithmetic = $supports_arithmetic:expr, supports_equality = $supports_equality:expr, supports_ordering = $supports_ordering:expr, is_keyable = $is_keyable:expr, is_storage_key_encodable = $is_storage_key_encodable:expr) ),* $(,)? ) => {
109 false $( || (primitive_matches_scalar!($primitive, $scalar) && $supports_arithmetic) )*
110 };
111}
112
113impl Primitive {
114 #[must_use]
115 pub const fn supports_arithmetic(self) -> bool {
116 icydb_core::scalar_registry!(primitive_supports_arithmetic_from_registry, self)
117 }
118
119 #[must_use]
120 pub const fn supports_remainder(self) -> bool {
121 matches!(
122 self,
123 Self::Decimal
124 | Self::Int8
125 | Self::Int16
126 | Self::Int32
127 | Self::Int64
128 | Self::Int128
129 | Self::Nat8
130 | Self::Nat16
131 | Self::Nat32
132 | Self::Nat64
133 | Self::Nat128
134 )
135 }
136
137 #[must_use]
138 pub const fn supports_copy(self) -> bool {
139 !matches!(self, Self::Blob | Self::Int | Self::Nat | Self::Text)
140 }
141
142 #[must_use]
143 pub const fn supports_hash(self) -> bool {
144 !matches!(self, Self::Blob | Self::Unit)
145 }
146
147 #[must_use]
149 pub const fn supports_num_cast(self) -> bool {
150 matches!(
151 self,
152 Self::Date
153 | Self::Decimal
154 | Self::Duration
155 | Self::E8s
156 | Self::E18s
157 | Self::Int8
158 | Self::Int16
159 | Self::Int32
160 | Self::Int64
161 | Self::Float32
162 | Self::Float64
163 | Self::Nat8
164 | Self::Nat16
165 | Self::Nat32
166 | Self::Nat64
167 | Self::Timestamp
168 )
169 }
170
171 #[must_use]
173 pub const fn supports_ord(self) -> bool {
174 !matches!(self, Self::Blob | Self::Unit)
175 }
176
177 #[must_use]
182 pub const fn is_decimal(self) -> bool {
183 matches!(self, Self::Decimal)
184 }
185
186 #[must_use]
189 pub const fn is_numeric(self) -> bool {
190 self.is_int() || self.is_float() || self.is_fixed_point() || self.is_decimal()
191 }
192
193 #[must_use]
194 pub const fn is_float(self) -> bool {
195 matches!(self, Self::Float32 | Self::Float64)
196 }
197
198 #[must_use]
199 pub const fn is_signed_int(self) -> bool {
200 matches!(
201 self,
202 Self::Int | Self::Int8 | Self::Int16 | Self::Int32 | Self::Int64 | Self::Int128
203 )
204 }
205
206 #[must_use]
207 pub const fn is_unsigned_int(self) -> bool {
208 matches!(
209 self,
210 Self::Nat | Self::Nat8 | Self::Nat16 | Self::Nat32 | Self::Nat64 | Self::Nat128
211 )
212 }
213
214 #[must_use]
215 pub const fn is_int(self) -> bool {
216 self.is_signed_int() || self.is_unsigned_int()
217 }
218
219 #[must_use]
220 pub const fn is_fixed_point(self) -> bool {
221 matches!(self, Self::E8s | Self::E18s)
222 }
223
224 #[must_use]
225 pub fn as_type(self) -> TokenStream {
226 let ident = format_ident!("{self}");
227
228 quote!(::icydb::types::#ident)
229 }
230
231 pub fn num_cast_fn(self) -> Result<&'static str, darling::Error> {
236 match self {
237 Self::E18s => Ok("u128"),
238 Self::Float32 => Ok("f32"),
239 Self::Float64 | Self::Decimal => Ok("f64"),
240 Self::Int8 => Ok("i8"),
241 Self::Int16 => Ok("i16"),
242 Self::Int32 | Self::Date => Ok("i32"),
243 Self::Int64 => Ok("i64"),
244 Self::Nat8 => Ok("u8"),
245 Self::Nat16 => Ok("u16"),
246 Self::Nat32 => Ok("u32"),
247 Self::Nat64 | Self::Duration | Self::E8s | Self::Timestamp => Ok("u64"),
248 _ => Err(darling::Error::custom(format!(
249 "numeric cast is unsupported for primitive {self}"
250 ))),
251 }
252 }
253}
254
255impl FromMeta for Primitive {
256 fn from_string(s: &str) -> Result<Self, darling::Error> {
257 s.parse::<Self>()
258 .map_err(|_| darling::Error::unknown_value(s))
259 }
260}
261
262impl ToTokens for Primitive {
263 fn to_tokens(&self, tokens: &mut TokenStream) {
264 let ident = format_ident!("{self}");
265
266 tokens.extend(quote!(::icydb::schema::types::Primitive::#ident));
267 }
268}