Skip to main content

icydb_schema/
types.rs

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///
9/// Cardinality
10///
11
12#[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///
38/// Primitive
39///
40
41#[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
76impl Primitive {
77    #[must_use]
78    pub const fn supports_arithmetic(self) -> bool {
79        self.is_int() || self.is_fixed_point() || self.is_decimal()
80    }
81
82    #[must_use]
83    pub const fn supports_copy(self) -> bool {
84        !matches!(self, Self::Blob | Self::Int | Self::Nat | Self::Text)
85    }
86
87    #[must_use]
88    pub const fn supports_display(self) -> bool {
89        !matches!(self, Self::Blob | Self::Unit)
90    }
91
92    #[must_use]
93    pub const fn supports_hash(self) -> bool {
94        !matches!(self, Self::Blob | Self::Unit)
95    }
96
97    // Int and Nat are unbounded integers so have no native representation
98    #[must_use]
99    pub const fn supports_num_cast(self) -> bool {
100        matches!(
101            self,
102            Self::Date
103                | Self::Decimal
104                | Self::Duration
105                | Self::E8s
106                | Self::E18s
107                | Self::Int8
108                | Self::Int16
109                | Self::Int32
110                | Self::Int64
111                | Self::Float32
112                | Self::Float64
113                | Self::Nat8
114                | Self::Nat16
115                | Self::Nat32
116                | Self::Nat64
117                | Self::Timestamp
118        )
119    }
120
121    // both Ord and PartialOrd
122    #[must_use]
123    pub const fn supports_ord(self) -> bool {
124        !matches!(self, Self::Blob | Self::Unit)
125    }
126
127    //
128    // grouped helpers
129    //
130
131    #[must_use]
132    pub const fn is_decimal(self) -> bool {
133        matches!(self, Self::Decimal)
134    }
135
136    // is_numeric
137    // Includes ints, floats, fixed‑point (E8s/E18s), and Decimal.
138    #[must_use]
139    pub const fn is_numeric(self) -> bool {
140        self.is_int() || self.is_float() || self.is_fixed_point() || self.is_decimal()
141    }
142
143    #[must_use]
144    pub const fn is_float(self) -> bool {
145        matches!(self, Self::Float32 | Self::Float64)
146    }
147
148    #[must_use]
149    pub const fn is_signed_int(self) -> bool {
150        matches!(
151            self,
152            Self::Int | Self::Int8 | Self::Int16 | Self::Int32 | Self::Int64
153        )
154    }
155
156    #[must_use]
157    pub const fn is_unsigned_int(self) -> bool {
158        matches!(
159            self,
160            Self::Nat | Self::Nat8 | Self::Nat16 | Self::Nat32 | Self::Nat64
161        )
162    }
163
164    #[must_use]
165    pub const fn is_int(self) -> bool {
166        self.is_signed_int() || self.is_unsigned_int()
167    }
168
169    #[must_use]
170    pub const fn is_fixed_point(self) -> bool {
171        matches!(self, Self::E8s | Self::E18s)
172    }
173
174    #[must_use]
175    pub fn as_type(self) -> TokenStream {
176        let ident = format_ident!("{self}");
177
178        quote!(::icydb::types::#ident)
179    }
180
181    ///
182    /// Returns the numeric cast function suffix for supported primitives.
183    /// Emits a structured error for non-numeric primitives.
184    ///
185    pub fn num_cast_fn(self) -> Result<&'static str, darling::Error> {
186        match self {
187            Self::E18s => Ok("u128"),
188            Self::Float32 => Ok("f32"),
189            Self::Float64 | Self::Decimal => Ok("f64"),
190            Self::Int8 => Ok("i8"),
191            Self::Int16 => Ok("i16"),
192            Self::Int32 | Self::Date => Ok("i32"),
193            Self::Int64 => Ok("i64"),
194            Self::Nat8 => Ok("u8"),
195            Self::Nat16 => Ok("u16"),
196            Self::Nat32 => Ok("u32"),
197            Self::Nat64 | Self::Duration | Self::E8s | Self::Timestamp => Ok("u64"),
198            _ => Err(darling::Error::custom(format!(
199                "numeric cast is unsupported for primitive {self}"
200            ))),
201        }
202    }
203}
204
205impl FromMeta for Primitive {
206    fn from_string(s: &str) -> Result<Self, darling::Error> {
207        s.parse::<Self>()
208            .map_err(|_| darling::Error::unknown_value(s))
209    }
210}
211
212impl ToTokens for Primitive {
213    fn to_tokens(&self, tokens: &mut TokenStream) {
214        let ident = format_ident!("{self}");
215
216        tokens.extend(quote!(::icydb::schema::types::Primitive::#ident));
217    }
218}
219
220///
221/// StoreType
222///
223
224#[derive(CandidType, Clone, Copy, Debug, Deserialize, Display, FromStr, Serialize)]
225pub enum StoreType {
226    Data,
227    Index,
228}
229
230impl FromMeta for StoreType {
231    fn from_string(s: &str) -> Result<Self, darling::Error> {
232        s.parse::<Self>()
233            .map_err(|_| darling::Error::unknown_value(s))
234    }
235}
236
237impl ToTokens for StoreType {
238    fn to_tokens(&self, tokens: &mut TokenStream) {
239        let ident = format_ident!("{self}");
240
241        tokens.extend(quote!(::icydb::schema::types::StoreType::#ident));
242    }
243}