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