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    #[must_use]
182    pub fn num_cast_fn(self) -> String {
183        match self {
184            Self::E18s => "u128",
185            Self::Float32 => "f32",
186            Self::Float64 | Self::Decimal => "f64",
187            Self::Int8 => "i8",
188            Self::Int16 => "i16",
189            Self::Int32 | Self::Date => "i32",
190            Self::Int64 => "i64",
191            Self::Nat8 => "u8",
192            Self::Nat16 => "u16",
193            Self::Nat32 => "u32",
194            Self::Nat64 | Self::Duration | Self::E8s | Self::Timestamp => "u64",
195            _ => panic!("unexpected primitive type"),
196        }
197        .into()
198    }
199}
200
201impl FromMeta for Primitive {
202    fn from_string(s: &str) -> Result<Self, darling::Error> {
203        s.parse::<Self>()
204            .map_err(|_| darling::Error::unknown_value(s))
205    }
206}
207
208impl ToTokens for Primitive {
209    fn to_tokens(&self, tokens: &mut TokenStream) {
210        let ident = format_ident!("{self}");
211
212        tokens.extend(quote!(::icydb::schema::types::Primitive::#ident));
213    }
214}
215
216///
217/// StoreType
218///
219
220#[derive(CandidType, Clone, Copy, Debug, Deserialize, Display, FromStr, Serialize)]
221pub enum StoreType {
222    Data,
223    Index,
224}
225
226impl FromMeta for StoreType {
227    fn from_string(s: &str) -> Result<Self, darling::Error> {
228        s.parse::<Self>()
229            .map_err(|_| darling::Error::unknown_value(s))
230    }
231}
232
233impl ToTokens for StoreType {
234    fn to_tokens(&self, tokens: &mut TokenStream) {
235        let ident = format_ident!("{self}");
236
237        tokens.extend(quote!(::icydb::schema::types::StoreType::#ident));
238    }
239}