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