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