1use crate::{
2 traits::FieldValue,
3 types::{Account, Principal, Subaccount, Timestamp, Ulid, Unit},
4 value::Value,
5};
6use candid::{CandidType, Principal as WrappedPrincipal};
7use canic::impl_storable_bounded;
8use derive_more::Display;
9use serde::{Deserialize, Serialize};
10use std::cmp::Ordering;
11
12#[derive(CandidType, Clone, Copy, Debug, Deserialize, Display, Eq, Hash, PartialEq, Serialize)]
21pub enum Key {
22 Account(Account),
23 Int(i64),
24 Principal(Principal),
25 Subaccount(Subaccount),
26 Timestamp(Timestamp),
27 Uint(u64),
28 Ulid(Ulid),
29 Unit,
30}
31
32impl Key {
33 pub const STORABLE_MAX_SIZE: u32 = 128;
34
35 #[must_use]
36 pub fn max_storable() -> Self {
37 Self::Account(Account::max_storable())
38 }
39
40 #[must_use]
41 pub const fn lower_bound() -> Self {
42 Self::Int(i64::MIN)
43 }
44
45 #[must_use]
46 pub const fn upper_bound() -> Self {
47 Self::Unit
48 }
49
50 const fn variant_rank(&self) -> u8 {
51 match self {
52 Self::Account(_) => 0,
53 Self::Int(_) => 1,
54 Self::Principal(_) => 2,
55 Self::Subaccount(_) => 3,
56 Self::Timestamp(_) => 4,
57 Self::Uint(_) => 5,
58 Self::Ulid(_) => 6,
59 Self::Unit => 7,
60 }
61 }
62}
63
64impl FieldValue for Key {
65 fn to_value(&self) -> Value {
66 match self {
67 Self::Account(v) => Value::Account(*v),
68 Self::Int(v) => Value::Int(*v),
69 Self::Principal(v) => Value::Principal(*v),
70 Self::Subaccount(v) => Value::Subaccount(*v),
71 Self::Timestamp(v) => Value::Timestamp(*v),
72 Self::Uint(v) => Value::Uint(*v),
73 Self::Ulid(v) => Value::Ulid(*v),
74 Self::Unit => Value::Unit,
75 }
76 }
77}
78
79impl From<()> for Key {
80 fn from((): ()) -> Self {
81 Self::Unit
82 }
83}
84
85impl From<Unit> for Key {
86 fn from(_: Unit) -> Self {
87 Self::Unit
88 }
89}
90
91impl PartialEq<()> for Key {
92 fn eq(&self, (): &()) -> bool {
93 matches!(self, Self::Unit)
94 }
95}
96
97impl PartialEq<Key> for () {
98 fn eq(&self, other: &Key) -> bool {
99 other == self
100 }
101}
102
103macro_rules! impl_from_key {
105 ( $( $ty:ty => $variant:ident ),* $(,)? ) => {
106 $(
107 impl From<$ty> for Key {
108 fn from(v: $ty) -> Self {
109 Self::$variant(v.into())
110 }
111 }
112 )*
113 }
114}
115
116macro_rules! impl_eq_key {
118 ( $( $ty:ty => $variant:ident ),* $(,)? ) => {
119 $(
120 impl PartialEq<$ty> for Key {
121 fn eq(&self, other: &$ty) -> bool {
122 matches!(self, Self::$variant(val) if val == other)
123 }
124 }
125
126 impl PartialEq<Key> for $ty {
127 fn eq(&self, other: &Key) -> bool {
128 other == self
129 }
130 }
131 )*
132 }
133}
134
135impl_from_key! {
136 Account => Account,
137 i8 => Int,
138 i16 => Int,
139 i32 => Int,
140 i64 => Int,
141 Principal => Principal,
142 WrappedPrincipal => Principal,
143 Subaccount => Subaccount,
144 Timestamp => Timestamp,
145 u8 => Uint,
146 u16 => Uint,
147 u32 => Uint,
148 u64 => Uint,
149 Ulid => Ulid,
150}
151
152impl_eq_key! {
153 Account => Account,
154 i64 => Int,
155 Principal => Principal,
156 Subaccount => Subaccount,
157 Timestamp => Timestamp,
158 u64 => Uint,
159 Ulid => Ulid,
160}
161
162impl Ord for Key {
163 fn cmp(&self, other: &Self) -> Ordering {
164 match (self, other) {
165 (Self::Account(a), Self::Account(b)) => Ord::cmp(a, b),
166 (Self::Int(a), Self::Int(b)) => Ord::cmp(a, b),
167 (Self::Principal(a), Self::Principal(b)) => Ord::cmp(a, b),
168 (Self::Uint(a), Self::Uint(b)) => Ord::cmp(a, b),
169 (Self::Ulid(a), Self::Ulid(b)) => Ord::cmp(a, b),
170 (Self::Subaccount(a), Self::Subaccount(b)) => Ord::cmp(a, b),
171 (Self::Timestamp(a), Self::Timestamp(b)) => Ord::cmp(a, b),
172
173 _ => Ord::cmp(&self.variant_rank(), &other.variant_rank()), }
175 }
176}
177
178impl PartialOrd for Key {
179 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
180 Some(Ord::cmp(self, other))
181 }
182}
183
184impl_storable_bounded!(Key, Self::STORABLE_MAX_SIZE, false);
185
186#[cfg(test)]
191mod tests {
192 use super::*;
193 use crate::traits::Storable;
194
195 #[test]
196 fn key_max_size_is_bounded() {
197 let key = Key::max_storable();
198 let size = Storable::to_bytes(&key).len();
199
200 assert!(
201 size <= Key::STORABLE_MAX_SIZE as usize,
202 "serialized Key too large: got {size} bytes (limit {})",
203 Key::STORABLE_MAX_SIZE
204 );
205 }
206}