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