1use std::{
4 cmp::Ordering,
5 fmt::{self, Display, Formatter},
6 hash::{Hash, Hasher},
7 str::FromStr,
8};
9
10use candid::CandidType;
11use serde::{Deserialize, Deserializer, Serialize, de::Error as DeError};
12
13#[cfg(test)]
14mod tests;
15
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18pub enum PrincipalError {
19 InvalidText,
21}
22
23impl Display for PrincipalError {
24 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
25 formatter.write_str("principal text is invalid")
26 }
27}
28
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
31pub enum PrincipalDecodeError {
32 TooLarge {
34 len: usize,
36 },
37}
38
39#[derive(Clone, Copy, Debug, Eq, PartialEq)]
41pub enum PrincipalEncodeError {
42 TooLarge {
44 len: usize,
46 max: usize,
48 },
49}
50
51#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
53#[repr(transparent)]
54#[serde(transparent)]
55pub struct Principal(candid::Principal);
56
57impl Principal {
58 pub const MAX_LENGTH_IN_BYTES: u32 = 29;
60
61 pub const MIN: Self = Self::from_slice(&[0x00; 29]);
63
64 pub const MAX: Self = Self::from_slice(&[0xFF; 29]);
66
67 #[must_use]
69 pub const fn anonymous() -> Self {
70 Self(candid::Principal::anonymous())
71 }
72
73 pub fn from_text(text: &str) -> Result<Self, PrincipalError> {
79 candid::Principal::from_text(text)
80 .map(Self)
81 .map_err(|_| PrincipalError::InvalidText)
82 }
83
84 #[must_use]
91 pub const fn from_slice(bytes: &[u8]) -> Self {
92 Self(candid::Principal::from_slice(bytes))
93 }
94
95 #[must_use]
97 pub const fn as_slice(&self) -> &[u8] {
98 self.0.as_slice()
99 }
100
101 pub const fn stored_bytes(&self) -> Result<&[u8], PrincipalEncodeError> {
108 let bytes = self.as_slice();
109 if bytes.len() > Self::MAX_LENGTH_IN_BYTES as usize {
110 return Err(PrincipalEncodeError::TooLarge {
111 len: bytes.len(),
112 max: Self::MAX_LENGTH_IN_BYTES as usize,
113 });
114 }
115 Ok(bytes)
116 }
117
118 pub fn to_bytes(self) -> Result<Vec<u8>, PrincipalEncodeError> {
124 Ok(self.stored_bytes()?.to_vec())
125 }
126
127 pub const fn try_from_bytes(bytes: &[u8]) -> Result<Self, PrincipalDecodeError> {
133 if bytes.len() > Self::MAX_LENGTH_IN_BYTES as usize {
134 return Err(PrincipalDecodeError::TooLarge { len: bytes.len() });
135 }
136 Ok(Self::from_slice(bytes))
137 }
138}
139
140impl Display for Principal {
141 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
142 Display::fmt(&self.0, formatter)
143 }
144}
145
146impl From<candid::Principal> for Principal {
147 fn from(value: candid::Principal) -> Self {
148 Self(value)
149 }
150}
151
152impl From<Principal> for candid::Principal {
153 fn from(value: Principal) -> Self {
154 value.0
155 }
156}
157
158impl FromStr for Principal {
159 type Err = PrincipalError;
160
161 fn from_str(input: &str) -> Result<Self, Self::Err> {
162 Self::from_text(input)
163 }
164}
165
166impl Serialize for Principal {
167 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
168 where
169 S: serde::Serializer,
170 {
171 self.0.serialize(serializer)
172 }
173}
174
175impl TryFrom<&[u8]> for Principal {
176 type Error = PrincipalDecodeError;
177
178 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
179 Self::try_from_bytes(bytes)
180 }
181}
182
183#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
185pub struct Blob(Vec<u8>);
186
187impl Blob {
188 #[must_use]
190 pub fn as_bytes(&self) -> &[u8] {
191 &self.0
192 }
193
194 #[must_use]
196 pub fn into_bytes(self) -> Vec<u8> {
197 self.0
198 }
199
200 #[must_use]
202 pub fn to_vec(&self) -> Vec<u8> {
203 self.0.clone()
204 }
205
206 #[must_use]
208 pub const fn len(&self) -> usize {
209 self.0.len()
210 }
211
212 #[must_use]
214 pub const fn is_empty(&self) -> bool {
215 self.0.is_empty()
216 }
217}
218
219impl Display for Blob {
220 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
221 write!(formatter, "[blob ({} bytes)]", self.len())
222 }
223}
224
225impl From<Vec<u8>> for Blob {
226 fn from(bytes: Vec<u8>) -> Self {
227 Self(bytes)
228 }
229}
230
231impl From<&[u8]> for Blob {
232 fn from(bytes: &[u8]) -> Self {
233 Self(bytes.to_vec())
234 }
235}
236
237impl<const N: usize> From<&[u8; N]> for Blob {
238 fn from(bytes: &[u8; N]) -> Self {
239 Self(bytes.to_vec())
240 }
241}
242
243impl<'de> Deserialize<'de> for Blob {
244 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
245 where
246 D: Deserializer<'de>,
247 {
248 Vec::<u8>::deserialize(deserializer).map(Self)
249 }
250}
251
252impl CandidType for Blob {
253 fn ty() -> candid::types::Type {
254 <Vec<u8> as CandidType>::ty()
255 }
256
257 fn _ty() -> candid::types::Type {
258 <Vec<u8> as CandidType>::_ty()
259 }
260
261 fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
262 where
263 S: candid::types::Serializer,
264 {
265 serializer.serialize_blob(self.as_bytes())
266 }
267}
268
269#[derive(Clone, Copy, Debug, Eq, PartialEq)]
271pub enum UlidParseError {
272 InvalidString,
274}
275
276impl Display for UlidParseError {
277 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
278 formatter.write_str("ULID text is invalid")
279 }
280}
281
282#[derive(Clone, Copy, Debug, Eq, PartialEq)]
284pub enum UlidDecodeError {
285 InvalidSize {
287 len: usize,
289 },
290}
291
292#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
294#[repr(transparent)]
295pub struct Ulid([u8; 16]);
296
297impl Ulid {
298 pub const STORED_SIZE: u32 = 16;
300
301 pub const MIN: Self = Self::from_bytes([0x00; 16]);
303
304 pub const MAX: Self = Self::from_bytes([0xFF; 16]);
306
307 #[must_use]
309 pub const fn nil() -> Self {
310 Self::MIN
311 }
312
313 #[must_use]
315 pub const fn from_bytes(bytes: [u8; 16]) -> Self {
316 Self(bytes)
317 }
318
319 #[must_use]
321 pub const fn from_u128(value: u128) -> Self {
322 Self::from_bytes(value.to_be_bytes())
323 }
324
325 #[must_use]
327 pub const fn to_bytes(self) -> [u8; 16] {
328 self.0
329 }
330
331 pub const fn try_from_bytes(bytes: &[u8]) -> Result<Self, UlidDecodeError> {
337 if bytes.len() != Self::STORED_SIZE as usize {
338 return Err(UlidDecodeError::InvalidSize { len: bytes.len() });
339 }
340 let mut value = [0; 16];
341 value.copy_from_slice(bytes);
342 Ok(Self::from_bytes(value))
343 }
344}
345
346impl Display for Ulid {
347 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
348 Display::fmt(&ulid::Ulid::from_bytes(self.0), formatter)
349 }
350}
351
352impl fmt::Debug for Ulid {
353 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
354 fmt::Debug::fmt(&self.to_string(), formatter)
355 }
356}
357
358impl FromStr for Ulid {
359 type Err = UlidParseError;
360
361 fn from_str(input: &str) -> Result<Self, Self::Err> {
362 let value = input
363 .parse::<ulid::Ulid>()
364 .map_err(|_| UlidParseError::InvalidString)?;
365 if value.to_string() != input {
366 return Err(UlidParseError::InvalidString);
367 }
368 Ok(Self::from_bytes(value.to_bytes()))
369 }
370}
371
372impl CandidType for Ulid {
373 fn ty() -> candid::types::Type {
374 <String as CandidType>::ty()
375 }
376
377 fn _ty() -> candid::types::Type {
378 <String as CandidType>::_ty()
379 }
380
381 fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
382 where
383 S: candid::types::Serializer,
384 {
385 serializer.serialize_text(&self.to_string())
386 }
387}
388
389impl<'de> Deserialize<'de> for Ulid {
390 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
391 where
392 D: Deserializer<'de>,
393 {
394 String::deserialize(deserializer)?
395 .parse()
396 .map_err(D::Error::custom)
397 }
398}
399
400impl Serialize for Ulid {
401 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
402 where
403 S: serde::Serializer,
404 {
405 serializer.serialize_str(&self.to_string())
406 }
407}
408
409impl TryFrom<&[u8]> for Ulid {
410 type Error = UlidDecodeError;
411
412 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
413 Self::try_from_bytes(bytes)
414 }
415}
416
417#[derive(Clone, Copy, Debug, Eq, PartialEq)]
419pub enum Float32DecodeError {
420 InvalidSize {
422 len: usize,
424 },
425 NonFinite,
427}
428
429#[derive(CandidType, Clone, Copy, Debug, Default)]
431#[repr(transparent)]
432pub struct Float32(f32);
433
434impl Float32 {
435 #[must_use]
437 pub fn try_new(value: f32) -> Option<Self> {
438 if !value.is_finite() {
439 return None;
440 }
441 Some(Self(if value == 0.0 { 0.0 } else { value }))
442 }
443
444 #[must_use]
446 pub const fn get(self) -> f32 {
447 self.0
448 }
449
450 #[must_use]
452 pub const fn to_be_bytes(&self) -> [u8; 4] {
453 self.0.to_bits().to_be_bytes()
454 }
455
456 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, Float32DecodeError> {
462 let bytes: [u8; 4] = bytes
463 .try_into()
464 .map_err(|_| Float32DecodeError::InvalidSize { len: bytes.len() })?;
465 Self::try_new(f32::from_bits(u32::from_be_bytes(bytes)))
466 .ok_or(Float32DecodeError::NonFinite)
467 }
468
469 #[must_use]
471 #[expect(clippy::cast_possible_truncation)]
472 pub fn try_from_f64(value: f64) -> Option<Self> {
473 if !value.is_finite() || value < f64::from(f32::MIN) || value > f64::from(f32::MAX) {
474 return None;
475 }
476 Self::try_new(value as f32)
477 }
478}
479
480impl Display for Float32 {
481 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
482 Display::fmt(&self.0, formatter)
483 }
484}
485
486impl<'de> Deserialize<'de> for Float32 {
487 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
488 where
489 D: Deserializer<'de>,
490 {
491 Self::try_new(f32::deserialize(deserializer)?)
492 .ok_or_else(|| D::Error::custom("Float32 must be finite"))
493 }
494}
495
496impl Serialize for Float32 {
497 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
498 where
499 S: serde::Serializer,
500 {
501 serializer.serialize_f32(self.0)
502 }
503}
504
505impl Eq for Float32 {}
506
507impl From<Float32> for f32 {
508 fn from(value: Float32) -> Self {
509 value.0
510 }
511}
512
513#[expect(clippy::cast_precision_loss)]
514impl From<i32> for Float32 {
515 fn from(value: i32) -> Self {
516 Self(value as f32)
517 }
518}
519
520impl Hash for Float32 {
521 fn hash<H: Hasher>(&self, state: &mut H) {
522 state.write_u32(self.0.to_bits());
523 }
524}
525
526impl Ord for Float32 {
527 fn cmp(&self, other: &Self) -> Ordering {
528 self.0.total_cmp(&other.0)
529 }
530}
531
532impl PartialEq for Float32 {
533 fn eq(&self, other: &Self) -> bool {
534 self.0 == other.0
535 }
536}
537
538impl PartialOrd for Float32 {
539 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
540 Some(self.cmp(other))
541 }
542}
543
544impl TryFrom<&[u8]> for Float32 {
545 type Error = Float32DecodeError;
546
547 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
548 Self::try_from_bytes(bytes)
549 }
550}
551
552#[derive(Clone, Copy, Debug, Eq, PartialEq)]
554pub enum Float64DecodeError {
555 InvalidSize {
557 len: usize,
559 },
560 NonFinite,
562}
563
564#[derive(CandidType, Clone, Copy, Debug, Default)]
566#[repr(transparent)]
567pub struct Float64(f64);
568
569impl Float64 {
570 #[must_use]
572 pub fn try_new(value: f64) -> Option<Self> {
573 if !value.is_finite() {
574 return None;
575 }
576 Some(Self(if value == 0.0 { 0.0 } else { value }))
577 }
578
579 #[must_use]
581 pub const fn get(self) -> f64 {
582 self.0
583 }
584
585 #[must_use]
587 pub const fn to_be_bytes(&self) -> [u8; 8] {
588 self.0.to_bits().to_be_bytes()
589 }
590
591 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, Float64DecodeError> {
597 let bytes: [u8; 8] = bytes
598 .try_into()
599 .map_err(|_| Float64DecodeError::InvalidSize { len: bytes.len() })?;
600 Self::try_new(f64::from_bits(u64::from_be_bytes(bytes)))
601 .ok_or(Float64DecodeError::NonFinite)
602 }
603}
604
605impl Display for Float64 {
606 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
607 Display::fmt(&self.0, formatter)
608 }
609}
610
611impl<'de> Deserialize<'de> for Float64 {
612 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
613 where
614 D: Deserializer<'de>,
615 {
616 Self::try_new(f64::deserialize(deserializer)?)
617 .ok_or_else(|| D::Error::custom("Float64 must be finite"))
618 }
619}
620
621impl Serialize for Float64 {
622 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
623 where
624 S: serde::Serializer,
625 {
626 serializer.serialize_f64(self.0)
627 }
628}
629
630impl Eq for Float64 {}
631
632impl From<Float64> for f64 {
633 fn from(value: Float64) -> Self {
634 value.0
635 }
636}
637
638impl From<i32> for Float64 {
639 fn from(value: i32) -> Self {
640 Self(f64::from(value))
641 }
642}
643
644impl Hash for Float64 {
645 fn hash<H: Hasher>(&self, state: &mut H) {
646 state.write_u64(self.0.to_bits());
647 }
648}
649
650impl Ord for Float64 {
651 fn cmp(&self, other: &Self) -> Ordering {
652 self.0.total_cmp(&other.0)
653 }
654}
655
656impl PartialEq for Float64 {
657 fn eq(&self, other: &Self) -> bool {
658 self.0 == other.0
659 }
660}
661
662impl PartialOrd for Float64 {
663 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
664 Some(self.cmp(other))
665 }
666}
667
668impl TryFrom<&[u8]> for Float64 {
669 type Error = Float64DecodeError;
670
671 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
672 Self::try_from_bytes(bytes)
673 }
674}