use crate::prelude::*;
use candid::CandidType;
use darling::FromMeta;
use derive_more::{Display, FromStr};
use icydb_primitives::ScalarKind;
use proc_macro2::TokenStream;
use quote::{ToTokens, format_ident, quote};
#[derive(
CandidType, Clone, Copy, Default, Debug, Deserialize, Display, Eq, FromStr, PartialEq, Serialize,
)]
pub enum Cardinality {
#[default]
One,
Opt,
Many,
}
impl FromMeta for Cardinality {
fn from_string(s: &str) -> Result<Self, darling::Error> {
s.parse::<Self>()
.map_err(|_| darling::Error::unknown_value(s))
}
}
impl ToTokens for Cardinality {
fn to_tokens(&self, tokens: &mut TokenStream) {
let ident = format_ident!("{self}");
tokens.extend(quote!(::icydb::schema::types::Cardinality::#ident));
}
}
#[derive(
CandidType, Clone, Copy, Debug, Deserialize, Display, Eq, PartialEq, FromStr, Serialize,
)]
#[remain::sorted]
pub enum Primitive {
Account,
Blob,
Bool,
Date,
Decimal,
Duration,
Float32,
Float64,
Int,
Int8,
Int16,
Int32,
Int64,
Int128,
Nat,
Nat8,
Nat16,
Nat32,
Nat64,
Nat128,
Principal,
Subaccount,
Text,
Timestamp,
Ulid,
Unit,
}
const fn primitive_scalar_kind(primitive: Primitive) -> ScalarKind {
match primitive {
Primitive::Account => ScalarKind::Account,
Primitive::Blob => ScalarKind::Blob,
Primitive::Bool => ScalarKind::Bool,
Primitive::Date => ScalarKind::Date,
Primitive::Decimal => ScalarKind::Decimal,
Primitive::Duration => ScalarKind::Duration,
Primitive::Float32 => ScalarKind::Float32,
Primitive::Float64 => ScalarKind::Float64,
Primitive::Int => ScalarKind::IntBig,
Primitive::Int8 | Primitive::Int16 | Primitive::Int32 | Primitive::Int64 => ScalarKind::Int,
Primitive::Int128 => ScalarKind::Int128,
Primitive::Nat => ScalarKind::UintBig,
Primitive::Nat8 | Primitive::Nat16 | Primitive::Nat32 | Primitive::Nat64 => {
ScalarKind::Uint
}
Primitive::Nat128 => ScalarKind::Uint128,
Primitive::Principal => ScalarKind::Principal,
Primitive::Subaccount => ScalarKind::Subaccount,
Primitive::Text => ScalarKind::Text,
Primitive::Timestamp => ScalarKind::Timestamp,
Primitive::Ulid => ScalarKind::Ulid,
Primitive::Unit => ScalarKind::Unit,
}
}
impl Primitive {
#[must_use]
pub const fn supports_arithmetic(self) -> bool {
primitive_scalar_kind(self).supports_arithmetic()
}
#[must_use]
pub const fn is_storage_key_encodable(self) -> bool {
primitive_scalar_kind(self).is_storage_key_encodable()
}
#[must_use]
pub const fn supports_remainder(self) -> bool {
matches!(
self,
Self::Decimal
| Self::Int8
| Self::Int16
| Self::Int32
| Self::Int64
| Self::Int128
| Self::Nat8
| Self::Nat16
| Self::Nat32
| Self::Nat64
| Self::Nat128
)
}
#[must_use]
pub const fn supports_copy(self) -> bool {
!matches!(self, Self::Blob | Self::Int | Self::Nat | Self::Text)
}
#[must_use]
pub const fn supports_hash(self) -> bool {
!matches!(self, Self::Blob | Self::Unit)
}
#[must_use]
pub const fn supports_num_cast(self) -> bool {
matches!(
self,
Self::Date
| Self::Decimal
| Self::Duration
| Self::Int8
| Self::Int16
| Self::Int32
| Self::Int64
| Self::Float32
| Self::Float64
| Self::Nat8
| Self::Nat16
| Self::Nat32
| Self::Nat64
| Self::Timestamp
)
}
#[must_use]
pub const fn supports_ord(self) -> bool {
primitive_scalar_kind(self).supports_ordering()
}
#[must_use]
pub const fn is_decimal(self) -> bool {
matches!(self, Self::Decimal)
}
#[must_use]
pub const fn is_numeric(self) -> bool {
self.is_int() || self.is_float() || self.is_decimal()
}
#[must_use]
pub const fn is_float(self) -> bool {
matches!(self, Self::Float32 | Self::Float64)
}
#[must_use]
pub const fn is_signed_int(self) -> bool {
matches!(
self,
Self::Int | Self::Int8 | Self::Int16 | Self::Int32 | Self::Int64 | Self::Int128
)
}
#[must_use]
pub const fn is_unsigned_int(self) -> bool {
matches!(
self,
Self::Nat | Self::Nat8 | Self::Nat16 | Self::Nat32 | Self::Nat64 | Self::Nat128
)
}
#[must_use]
pub const fn is_int(self) -> bool {
self.is_signed_int() || self.is_unsigned_int()
}
#[must_use]
pub fn as_type(self) -> TokenStream {
let ident = format_ident!("{self}");
quote!(::icydb::types::#ident)
}
pub fn num_cast_fn(self) -> Result<&'static str, darling::Error> {
match self {
Self::Float32 => Ok("f32"),
Self::Float64 | Self::Decimal => Ok("f64"),
Self::Int8 => Ok("i8"),
Self::Int16 => Ok("i16"),
Self::Int32 | Self::Date => Ok("i32"),
Self::Int64 => Ok("i64"),
Self::Nat8 => Ok("u8"),
Self::Nat16 => Ok("u16"),
Self::Nat32 => Ok("u32"),
Self::Nat64 | Self::Duration | Self::Timestamp => Ok("u64"),
_ => Err(darling::Error::custom(format!(
"numeric cast is unsupported for primitive {self}"
))),
}
}
}
impl FromMeta for Primitive {
fn from_string(s: &str) -> Result<Self, darling::Error> {
s.parse::<Self>()
.map_err(|_| darling::Error::unknown_value(s))
}
}
impl ToTokens for Primitive {
fn to_tokens(&self, tokens: &mut TokenStream) {
let ident = format_ident!("{self}");
tokens.extend(quote!(::icydb::schema::types::Primitive::#ident));
}
}