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)]
297#[repr(transparent)]
298pub struct Ulid([u8; 16]);
299
300impl Ulid {
301 pub const STORED_SIZE: u32 = 16;
303
304 pub const MIN: Self = Self::from_bytes([0x00; 16]);
306
307 pub const MAX: Self = Self::from_bytes([0xFF; 16]);
309
310 #[must_use]
312 pub const fn nil() -> Self {
313 Self::MIN
314 }
315
316 #[must_use]
318 pub const fn from_bytes(bytes: [u8; 16]) -> Self {
319 Self(bytes)
320 }
321
322 #[must_use]
324 pub const fn from_u128(value: u128) -> Self {
325 Self::from_bytes(value.to_be_bytes())
326 }
327
328 #[must_use]
330 pub const fn to_bytes(self) -> [u8; 16] {
331 self.0
332 }
333
334 pub const fn try_from_bytes(bytes: &[u8]) -> Result<Self, UlidDecodeError> {
340 if bytes.len() != Self::STORED_SIZE as usize {
341 return Err(UlidDecodeError::InvalidSize { len: bytes.len() });
342 }
343 let mut value = [0; 16];
344 value.copy_from_slice(bytes);
345 Ok(Self::from_bytes(value))
346 }
347}
348
349impl Display for Ulid {
350 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
351 Display::fmt(&ulid::Ulid::from_bytes(self.0), formatter)
352 }
353}
354
355impl fmt::Debug for Ulid {
356 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
357 fmt::Debug::fmt(&self.to_string(), formatter)
358 }
359}
360
361impl FromStr for Ulid {
362 type Err = UlidParseError;
363
364 fn from_str(input: &str) -> Result<Self, Self::Err> {
365 let value = input
366 .parse::<ulid::Ulid>()
367 .map_err(|_| UlidParseError::InvalidString)?;
368 if value.to_string() != input {
369 return Err(UlidParseError::InvalidString);
370 }
371 Ok(Self::from_bytes(value.to_bytes()))
372 }
373}
374
375impl CandidType for Ulid {
376 fn ty() -> candid::types::Type {
377 <Vec<u8> as CandidType>::ty()
378 }
379
380 fn _ty() -> candid::types::Type {
381 <Vec<u8> as CandidType>::_ty()
382 }
383
384 fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
385 where
386 S: candid::types::Serializer,
387 {
388 serializer.serialize_blob(&self.0)
389 }
390}
391
392impl<'de> Deserialize<'de> for Ulid {
393 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
394 where
395 D: Deserializer<'de>,
396 {
397 struct UlidBytesVisitor;
400
401 impl<'de> serde::de::Visitor<'de> for UlidBytesVisitor {
402 type Value = Ulid;
403
404 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
405 formatter.write_str("exactly 16 ULID bytes")
406 }
407
408 fn visit_seq<A: serde::de::SeqAccess<'de>>(
409 self,
410 mut sequence: A,
411 ) -> Result<Ulid, A::Error> {
412 let mut bytes = [0; 16];
413 for (index, byte) in bytes.iter_mut().enumerate() {
414 *byte = sequence
415 .next_element()?
416 .ok_or_else(|| A::Error::invalid_length(index, &self))?;
417 }
418 if sequence.next_element::<u8>()?.is_some() {
419 return Err(A::Error::invalid_length(17, &self));
420 }
421
422 Ok(Ulid::from_bytes(bytes))
423 }
424 }
425
426 if deserializer.is_human_readable() {
427 return String::deserialize(deserializer)?
428 .parse()
429 .map_err(D::Error::custom);
430 }
431
432 deserializer.deserialize_seq(UlidBytesVisitor)
433 }
434}
435
436impl Serialize for Ulid {
437 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
438 where
439 S: serde::Serializer,
440 {
441 if serializer.is_human_readable() {
442 serializer.serialize_str(&self.to_string())
443 } else {
444 serializer.serialize_bytes(&self.0)
445 }
446 }
447}
448
449impl TryFrom<&[u8]> for Ulid {
450 type Error = UlidDecodeError;
451
452 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
453 Self::try_from_bytes(bytes)
454 }
455}
456
457#[derive(Clone, Copy, Debug, Eq, PartialEq)]
459pub enum Float32DecodeError {
460 InvalidSize {
462 len: usize,
464 },
465 NonFinite,
467}
468
469#[derive(CandidType, Clone, Copy, Debug, Default)]
471#[repr(transparent)]
472pub struct Float32(f32);
473
474impl Float32 {
475 #[must_use]
477 pub fn try_new(value: f32) -> Option<Self> {
478 if !value.is_finite() {
479 return None;
480 }
481 Some(Self(if value == 0.0 { 0.0 } else { value }))
482 }
483
484 #[must_use]
486 pub const fn get(self) -> f32 {
487 self.0
488 }
489
490 #[must_use]
492 pub const fn to_be_bytes(&self) -> [u8; 4] {
493 self.0.to_bits().to_be_bytes()
494 }
495
496 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, Float32DecodeError> {
502 let bytes: [u8; 4] = bytes
503 .try_into()
504 .map_err(|_| Float32DecodeError::InvalidSize { len: bytes.len() })?;
505 Self::try_new(f32::from_bits(u32::from_be_bytes(bytes)))
506 .ok_or(Float32DecodeError::NonFinite)
507 }
508
509 #[must_use]
511 #[expect(clippy::cast_possible_truncation)]
512 pub fn try_from_f64(value: f64) -> Option<Self> {
513 if !value.is_finite() || value < f64::from(f32::MIN) || value > f64::from(f32::MAX) {
514 return None;
515 }
516 Self::try_new(value as f32)
517 }
518}
519
520impl Display for Float32 {
521 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
522 Display::fmt(&self.0, formatter)
523 }
524}
525
526impl<'de> Deserialize<'de> for Float32 {
527 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
528 where
529 D: Deserializer<'de>,
530 {
531 Self::try_new(f32::deserialize(deserializer)?)
532 .ok_or_else(|| D::Error::custom("Float32 must be finite"))
533 }
534}
535
536impl Serialize for Float32 {
537 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
538 where
539 S: serde::Serializer,
540 {
541 serializer.serialize_f32(self.0)
542 }
543}
544
545impl Eq for Float32 {}
546
547impl From<Float32> for f32 {
548 fn from(value: Float32) -> Self {
549 value.0
550 }
551}
552
553#[expect(clippy::cast_precision_loss)]
554impl From<i32> for Float32 {
555 fn from(value: i32) -> Self {
556 Self(value as f32)
557 }
558}
559
560impl Hash for Float32 {
561 fn hash<H: Hasher>(&self, state: &mut H) {
562 state.write_u32(self.0.to_bits());
563 }
564}
565
566impl Ord for Float32 {
567 fn cmp(&self, other: &Self) -> Ordering {
568 self.0.total_cmp(&other.0)
569 }
570}
571
572impl PartialEq for Float32 {
573 fn eq(&self, other: &Self) -> bool {
574 self.0 == other.0
575 }
576}
577
578impl PartialOrd for Float32 {
579 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
580 Some(self.cmp(other))
581 }
582}
583
584impl TryFrom<&[u8]> for Float32 {
585 type Error = Float32DecodeError;
586
587 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
588 Self::try_from_bytes(bytes)
589 }
590}
591
592#[derive(Clone, Copy, Debug, Eq, PartialEq)]
594pub enum Float64DecodeError {
595 InvalidSize {
597 len: usize,
599 },
600 NonFinite,
602}
603
604#[derive(CandidType, Clone, Copy, Debug, Default)]
606#[repr(transparent)]
607pub struct Float64(f64);
608
609impl Float64 {
610 #[must_use]
612 pub fn try_new(value: f64) -> Option<Self> {
613 if !value.is_finite() {
614 return None;
615 }
616 Some(Self(if value == 0.0 { 0.0 } else { value }))
617 }
618
619 #[must_use]
621 pub const fn get(self) -> f64 {
622 self.0
623 }
624
625 #[must_use]
627 pub const fn to_be_bytes(&self) -> [u8; 8] {
628 self.0.to_bits().to_be_bytes()
629 }
630
631 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, Float64DecodeError> {
637 let bytes: [u8; 8] = bytes
638 .try_into()
639 .map_err(|_| Float64DecodeError::InvalidSize { len: bytes.len() })?;
640 Self::try_new(f64::from_bits(u64::from_be_bytes(bytes)))
641 .ok_or(Float64DecodeError::NonFinite)
642 }
643}
644
645impl Display for Float64 {
646 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
647 Display::fmt(&self.0, formatter)
648 }
649}
650
651impl<'de> Deserialize<'de> for Float64 {
652 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
653 where
654 D: Deserializer<'de>,
655 {
656 Self::try_new(f64::deserialize(deserializer)?)
657 .ok_or_else(|| D::Error::custom("Float64 must be finite"))
658 }
659}
660
661impl Serialize for Float64 {
662 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
663 where
664 S: serde::Serializer,
665 {
666 serializer.serialize_f64(self.0)
667 }
668}
669
670impl Eq for Float64 {}
671
672impl From<Float64> for f64 {
673 fn from(value: Float64) -> Self {
674 value.0
675 }
676}
677
678impl From<i32> for Float64 {
679 fn from(value: i32) -> Self {
680 Self(f64::from(value))
681 }
682}
683
684impl Hash for Float64 {
685 fn hash<H: Hasher>(&self, state: &mut H) {
686 state.write_u64(self.0.to_bits());
687 }
688}
689
690impl Ord for Float64 {
691 fn cmp(&self, other: &Self) -> Ordering {
692 self.0.total_cmp(&other.0)
693 }
694}
695
696impl PartialEq for Float64 {
697 fn eq(&self, other: &Self) -> bool {
698 self.0 == other.0
699 }
700}
701
702impl PartialOrd for Float64 {
703 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
704 Some(self.cmp(other))
705 }
706}
707
708impl TryFrom<&[u8]> for Float64 {
709 type Error = Float64DecodeError;
710
711 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
712 Self::try_from_bytes(bytes)
713 }
714}