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 idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
258 where
259 S: candid::types::Serializer,
260 {
261 serializer.serialize_blob(self.as_bytes())
262 }
263}
264
265#[derive(Clone, Copy, Debug, Eq, PartialEq)]
267pub enum UlidParseError {
268 InvalidString,
270}
271
272impl Display for UlidParseError {
273 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
274 formatter.write_str("ULID text is invalid")
275 }
276}
277
278#[derive(Clone, Copy, Debug, Eq, PartialEq)]
280pub enum UlidDecodeError {
281 InvalidSize {
283 len: usize,
285 },
286}
287
288#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
290#[repr(transparent)]
291pub struct Ulid([u8; 16]);
292
293impl Ulid {
294 pub const STORED_SIZE: u32 = 16;
296
297 pub const MIN: Self = Self::from_bytes([0x00; 16]);
299
300 pub const MAX: Self = Self::from_bytes([0xFF; 16]);
302
303 #[must_use]
305 pub const fn nil() -> Self {
306 Self::MIN
307 }
308
309 #[must_use]
311 pub const fn from_bytes(bytes: [u8; 16]) -> Self {
312 Self(bytes)
313 }
314
315 #[must_use]
317 pub const fn from_u128(value: u128) -> Self {
318 Self::from_bytes(value.to_be_bytes())
319 }
320
321 #[must_use]
323 pub const fn to_bytes(self) -> [u8; 16] {
324 self.0
325 }
326
327 pub const fn try_from_bytes(bytes: &[u8]) -> Result<Self, UlidDecodeError> {
333 if bytes.len() != Self::STORED_SIZE as usize {
334 return Err(UlidDecodeError::InvalidSize { len: bytes.len() });
335 }
336 let mut value = [0; 16];
337 value.copy_from_slice(bytes);
338 Ok(Self::from_bytes(value))
339 }
340}
341
342impl Display for Ulid {
343 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
344 Display::fmt(&ulid::Ulid::from_bytes(self.0), formatter)
345 }
346}
347
348impl fmt::Debug for Ulid {
349 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
350 fmt::Debug::fmt(&self.to_string(), formatter)
351 }
352}
353
354impl FromStr for Ulid {
355 type Err = UlidParseError;
356
357 fn from_str(input: &str) -> Result<Self, Self::Err> {
358 let value = input
359 .parse::<ulid::Ulid>()
360 .map_err(|_| UlidParseError::InvalidString)?;
361 if value.to_string() != input {
362 return Err(UlidParseError::InvalidString);
363 }
364 Ok(Self::from_bytes(value.to_bytes()))
365 }
366}
367
368impl CandidType for Ulid {
369 fn _ty() -> candid::types::Type {
370 <String as CandidType>::_ty()
371 }
372
373 fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
374 where
375 S: candid::types::Serializer,
376 {
377 serializer.serialize_text(&self.to_string())
378 }
379}
380
381impl<'de> Deserialize<'de> for Ulid {
382 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
383 where
384 D: Deserializer<'de>,
385 {
386 String::deserialize(deserializer)?
387 .parse()
388 .map_err(D::Error::custom)
389 }
390}
391
392impl Serialize for Ulid {
393 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
394 where
395 S: serde::Serializer,
396 {
397 serializer.serialize_str(&self.to_string())
398 }
399}
400
401impl TryFrom<&[u8]> for Ulid {
402 type Error = UlidDecodeError;
403
404 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
405 Self::try_from_bytes(bytes)
406 }
407}
408
409#[derive(Clone, Copy, Debug, Eq, PartialEq)]
411pub enum Float32DecodeError {
412 InvalidSize {
414 len: usize,
416 },
417 NonFinite,
419}
420
421#[derive(CandidType, Clone, Copy, Debug, Default)]
423#[repr(transparent)]
424pub struct Float32(f32);
425
426impl Float32 {
427 #[must_use]
429 pub fn try_new(value: f32) -> Option<Self> {
430 if !value.is_finite() {
431 return None;
432 }
433 Some(Self(if value == 0.0 { 0.0 } else { value }))
434 }
435
436 #[must_use]
438 pub const fn get(self) -> f32 {
439 self.0
440 }
441
442 #[must_use]
444 pub const fn to_be_bytes(&self) -> [u8; 4] {
445 self.0.to_bits().to_be_bytes()
446 }
447
448 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, Float32DecodeError> {
454 let bytes: [u8; 4] = bytes
455 .try_into()
456 .map_err(|_| Float32DecodeError::InvalidSize { len: bytes.len() })?;
457 Self::try_new(f32::from_bits(u32::from_be_bytes(bytes)))
458 .ok_or(Float32DecodeError::NonFinite)
459 }
460
461 #[must_use]
463 #[expect(clippy::cast_possible_truncation)]
464 pub fn try_from_f64(value: f64) -> Option<Self> {
465 if !value.is_finite() || value < f64::from(f32::MIN) || value > f64::from(f32::MAX) {
466 return None;
467 }
468 Self::try_new(value as f32)
469 }
470}
471
472impl Display for Float32 {
473 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
474 Display::fmt(&self.0, formatter)
475 }
476}
477
478impl<'de> Deserialize<'de> for Float32 {
479 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
480 where
481 D: Deserializer<'de>,
482 {
483 Self::try_new(f32::deserialize(deserializer)?)
484 .ok_or_else(|| D::Error::custom("Float32 must be finite"))
485 }
486}
487
488impl Serialize for Float32 {
489 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
490 where
491 S: serde::Serializer,
492 {
493 serializer.serialize_f32(self.0)
494 }
495}
496
497impl Eq for Float32 {}
498
499impl From<Float32> for f32 {
500 fn from(value: Float32) -> Self {
501 value.0
502 }
503}
504
505#[expect(clippy::cast_precision_loss)]
506impl From<i32> for Float32 {
507 fn from(value: i32) -> Self {
508 Self(value as f32)
509 }
510}
511
512impl Hash for Float32 {
513 fn hash<H: Hasher>(&self, state: &mut H) {
514 state.write_u32(self.0.to_bits());
515 }
516}
517
518impl Ord for Float32 {
519 fn cmp(&self, other: &Self) -> Ordering {
520 self.0.total_cmp(&other.0)
521 }
522}
523
524impl PartialEq for Float32 {
525 fn eq(&self, other: &Self) -> bool {
526 self.0 == other.0
527 }
528}
529
530impl PartialOrd for Float32 {
531 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
532 Some(self.cmp(other))
533 }
534}
535
536impl TryFrom<&[u8]> for Float32 {
537 type Error = Float32DecodeError;
538
539 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
540 Self::try_from_bytes(bytes)
541 }
542}
543
544#[derive(Clone, Copy, Debug, Eq, PartialEq)]
546pub enum Float64DecodeError {
547 InvalidSize {
549 len: usize,
551 },
552 NonFinite,
554}
555
556#[derive(CandidType, Clone, Copy, Debug, Default)]
558#[repr(transparent)]
559pub struct Float64(f64);
560
561impl Float64 {
562 #[must_use]
564 pub fn try_new(value: f64) -> Option<Self> {
565 if !value.is_finite() {
566 return None;
567 }
568 Some(Self(if value == 0.0 { 0.0 } else { value }))
569 }
570
571 #[must_use]
573 pub const fn get(self) -> f64 {
574 self.0
575 }
576
577 #[must_use]
579 pub const fn to_be_bytes(&self) -> [u8; 8] {
580 self.0.to_bits().to_be_bytes()
581 }
582
583 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, Float64DecodeError> {
589 let bytes: [u8; 8] = bytes
590 .try_into()
591 .map_err(|_| Float64DecodeError::InvalidSize { len: bytes.len() })?;
592 Self::try_new(f64::from_bits(u64::from_be_bytes(bytes)))
593 .ok_or(Float64DecodeError::NonFinite)
594 }
595}
596
597impl Display for Float64 {
598 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
599 Display::fmt(&self.0, formatter)
600 }
601}
602
603impl<'de> Deserialize<'de> for Float64 {
604 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
605 where
606 D: Deserializer<'de>,
607 {
608 Self::try_new(f64::deserialize(deserializer)?)
609 .ok_or_else(|| D::Error::custom("Float64 must be finite"))
610 }
611}
612
613impl Serialize for Float64 {
614 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
615 where
616 S: serde::Serializer,
617 {
618 serializer.serialize_f64(self.0)
619 }
620}
621
622impl Eq for Float64 {}
623
624impl From<Float64> for f64 {
625 fn from(value: Float64) -> Self {
626 value.0
627 }
628}
629
630impl From<i32> for Float64 {
631 fn from(value: i32) -> Self {
632 Self(f64::from(value))
633 }
634}
635
636impl Hash for Float64 {
637 fn hash<H: Hasher>(&self, state: &mut H) {
638 state.write_u64(self.0.to_bits());
639 }
640}
641
642impl Ord for Float64 {
643 fn cmp(&self, other: &Self) -> Ordering {
644 self.0.total_cmp(&other.0)
645 }
646}
647
648impl PartialEq for Float64 {
649 fn eq(&self, other: &Self) -> bool {
650 self.0 == other.0
651 }
652}
653
654impl PartialOrd for Float64 {
655 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
656 Some(self.cmp(other))
657 }
658}
659
660impl TryFrom<&[u8]> for Float64 {
661 type Error = Float64DecodeError;
662
663 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
664 Self::try_from_bytes(bytes)
665 }
666}