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