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