1use std::{
4 cmp::Ordering,
5 fmt::{self, Display, Formatter},
6 hash::{Hash, Hasher},
7 str::FromStr,
8};
9
10use ic_wire::ByteBuf;
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(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
53#[repr(transparent)]
54#[serde(transparent)]
55pub struct Principal(ic_platform::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(ic_platform::Principal::anonymous())
71 }
72
73 pub fn from_text(text: &str) -> Result<Self, PrincipalError> {
79 ic_platform::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(ic_platform::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<ic_platform::Principal> for Principal {
147 fn from(value: ic_platform::Principal) -> Self {
148 Self(value)
149 }
150}
151
152impl From<Principal> for ic_platform::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)]
185pub struct Blob(Vec<u8>);
186
187impl Blob {
188 pub fn from_hex_literal(text: &str) -> Result<Self, crate::SchemaContractError> {
198 if text.len() > crate::MAX_PROPOSAL_LITERAL_BYTES * 2
199 || !text.len().is_multiple_of(2)
200 || !text.bytes().all(|byte| byte.is_ascii_hexdigit())
201 {
202 return Err(crate::SchemaContractError::InvalidLiteral);
203 }
204 text.as_bytes()
205 .as_chunks::<2>()
206 .0
207 .iter()
208 .map(|pair| {
209 let pair = std::str::from_utf8(pair)
210 .map_err(|_| crate::SchemaContractError::InvalidLiteral)?;
211 u8::from_str_radix(pair, 16).map_err(|_| crate::SchemaContractError::InvalidLiteral)
212 })
213 .collect::<Result<Vec<_>, _>>()
214 .map(Self::from)
215 }
216
217 #[must_use]
219 pub fn as_bytes(&self) -> &[u8] {
220 &self.0
221 }
222
223 #[must_use]
225 pub fn into_bytes(self) -> Vec<u8> {
226 self.0
227 }
228
229 #[must_use]
231 pub fn to_vec(&self) -> Vec<u8> {
232 self.0.clone()
233 }
234
235 #[must_use]
237 pub const fn len(&self) -> usize {
238 self.0.len()
239 }
240
241 #[must_use]
243 pub const fn is_empty(&self) -> bool {
244 self.0.is_empty()
245 }
246}
247
248impl Display for Blob {
249 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
250 write!(formatter, "[blob ({} bytes)]", self.len())
251 }
252}
253
254impl From<Vec<u8>> for Blob {
255 fn from(bytes: Vec<u8>) -> Self {
256 Self(bytes)
257 }
258}
259
260impl From<&[u8]> for Blob {
261 fn from(bytes: &[u8]) -> Self {
262 Self(bytes.to_vec())
263 }
264}
265
266impl<const N: usize> From<&[u8; N]> for Blob {
267 fn from(bytes: &[u8; N]) -> Self {
268 Self(bytes.to_vec())
269 }
270}
271
272impl<'de> Deserialize<'de> for Blob {
273 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
274 where
275 D: Deserializer<'de>,
276 {
277 ByteBuf::deserialize(deserializer).map(|bytes| Self(bytes.into_vec()))
278 }
279}
280
281#[derive(Clone, Copy, Debug, Eq, PartialEq)]
283pub enum UlidParseError {
284 InvalidString,
286}
287
288impl Display for UlidParseError {
289 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
290 formatter.write_str("ULID text is invalid")
291 }
292}
293
294#[derive(Clone, Copy, Debug, Eq, PartialEq)]
296pub enum UlidDecodeError {
297 InvalidSize {
299 len: usize,
301 },
302}
303
304#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
309#[repr(transparent)]
310pub struct Ulid([u8; 16]);
311
312impl Ulid {
313 pub const STORED_SIZE: u32 = 16;
315
316 pub const MIN: Self = Self::from_bytes([0x00; 16]);
318
319 pub const MAX: Self = Self::from_bytes([0xFF; 16]);
321
322 #[must_use]
324 pub const fn nil() -> Self {
325 Self::MIN
326 }
327
328 #[must_use]
330 pub const fn from_bytes(bytes: [u8; 16]) -> Self {
331 Self(bytes)
332 }
333
334 #[must_use]
336 pub const fn from_u128(value: u128) -> Self {
337 Self::from_bytes(value.to_be_bytes())
338 }
339
340 #[must_use]
342 pub const fn to_bytes(self) -> [u8; 16] {
343 self.0
344 }
345
346 pub const fn try_from_bytes(bytes: &[u8]) -> Result<Self, UlidDecodeError> {
352 if bytes.len() != Self::STORED_SIZE as usize {
353 return Err(UlidDecodeError::InvalidSize { len: bytes.len() });
354 }
355 let mut value = [0; 16];
356 value.copy_from_slice(bytes);
357 Ok(Self::from_bytes(value))
358 }
359}
360
361impl Display for Ulid {
362 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
363 Display::fmt(&ulid::Ulid::from_bytes(self.0), formatter)
364 }
365}
366
367impl fmt::Debug for Ulid {
368 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
369 fmt::Debug::fmt(&self.to_string(), formatter)
370 }
371}
372
373impl FromStr for Ulid {
374 type Err = UlidParseError;
375
376 fn from_str(input: &str) -> Result<Self, Self::Err> {
377 let value = input
378 .parse::<ulid::Ulid>()
379 .map_err(|_| UlidParseError::InvalidString)?;
380 if value.to_string() != input {
381 return Err(UlidParseError::InvalidString);
382 }
383 Ok(Self::from_bytes(value.to_bytes()))
384 }
385}
386
387impl<'de> Deserialize<'de> for Ulid {
388 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
389 where
390 D: Deserializer<'de>,
391 {
392 if deserializer.is_human_readable() {
393 return String::deserialize(deserializer)?
394 .parse()
395 .map_err(D::Error::custom);
396 }
397
398 deserialize_fixed_bytes(deserializer).map(Self::from_bytes)
399 }
400}
401
402impl Serialize for Ulid {
403 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
404 where
405 S: serde::Serializer,
406 {
407 if serializer.is_human_readable() {
408 serializer.serialize_str(&self.to_string())
409 } else {
410 serializer.serialize_bytes(&self.0)
411 }
412 }
413}
414
415impl TryFrom<&[u8]> for Ulid {
416 type Error = UlidDecodeError;
417
418 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
419 Self::try_from_bytes(bytes)
420 }
421}
422
423#[derive(Clone, Copy, Debug, Eq, PartialEq)]
425pub enum Float32DecodeError {
426 InvalidSize {
428 len: usize,
430 },
431 NonFinite,
433}
434
435#[derive(Clone, Copy, Debug, Default)]
437#[repr(transparent)]
438pub struct Float32(f32);
439
440impl Float32 {
441 #[must_use]
443 pub fn try_new(value: f32) -> Option<Self> {
444 if !value.is_finite() {
445 return None;
446 }
447 Some(Self(if value == 0.0 { 0.0 } else { value }))
448 }
449
450 #[must_use]
452 pub const fn get(self) -> f32 {
453 self.0
454 }
455
456 #[must_use]
458 pub const fn to_be_bytes(&self) -> [u8; 4] {
459 self.0.to_bits().to_be_bytes()
460 }
461
462 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, Float32DecodeError> {
468 let bytes: [u8; 4] = bytes
469 .try_into()
470 .map_err(|_| Float32DecodeError::InvalidSize { len: bytes.len() })?;
471 Self::try_new(f32::from_bits(u32::from_be_bytes(bytes)))
472 .ok_or(Float32DecodeError::NonFinite)
473 }
474
475 #[must_use]
477 #[expect(clippy::cast_possible_truncation)]
478 pub fn try_from_f64(value: f64) -> Option<Self> {
479 if !value.is_finite() || value < f64::from(f32::MIN) || value > f64::from(f32::MAX) {
480 return None;
481 }
482 Self::try_new(value as f32)
483 }
484}
485
486impl Display for Float32 {
487 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
488 Display::fmt(&self.0, formatter)
489 }
490}
491
492impl<'de> Deserialize<'de> for Float32 {
493 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
494 where
495 D: Deserializer<'de>,
496 {
497 Self::try_new(f32::deserialize(deserializer)?)
498 .ok_or_else(|| D::Error::custom("Float32 must be finite"))
499 }
500}
501
502impl Serialize for Float32 {
503 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
504 where
505 S: serde::Serializer,
506 {
507 serializer.serialize_f32(self.0)
508 }
509}
510
511impl Eq for Float32 {}
512
513impl From<Float32> for f32 {
514 fn from(value: Float32) -> Self {
515 value.0
516 }
517}
518
519#[expect(clippy::cast_precision_loss)]
520impl From<i32> for Float32 {
521 fn from(value: i32) -> Self {
522 Self(value as f32)
523 }
524}
525
526impl Hash for Float32 {
527 fn hash<H: Hasher>(&self, state: &mut H) {
528 state.write_u32(self.0.to_bits());
529 }
530}
531
532impl Ord for Float32 {
533 fn cmp(&self, other: &Self) -> Ordering {
534 self.0.total_cmp(&other.0)
535 }
536}
537
538impl PartialEq for Float32 {
539 fn eq(&self, other: &Self) -> bool {
540 self.0 == other.0
541 }
542}
543
544impl PartialOrd for Float32 {
545 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
546 Some(self.cmp(other))
547 }
548}
549
550impl TryFrom<&[u8]> for Float32 {
551 type Error = Float32DecodeError;
552
553 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
554 Self::try_from_bytes(bytes)
555 }
556}
557
558#[derive(Clone, Copy, Debug, Eq, PartialEq)]
560pub enum Float64DecodeError {
561 InvalidSize {
563 len: usize,
565 },
566 NonFinite,
568}
569
570#[derive(Clone, Copy, Debug, Default)]
572#[repr(transparent)]
573pub struct Float64(f64);
574
575impl Float64 {
576 #[must_use]
578 pub fn try_new(value: f64) -> Option<Self> {
579 if !value.is_finite() {
580 return None;
581 }
582 Some(Self(if value == 0.0 { 0.0 } else { value }))
583 }
584
585 #[must_use]
587 pub const fn get(self) -> f64 {
588 self.0
589 }
590
591 #[must_use]
593 pub const fn to_be_bytes(&self) -> [u8; 8] {
594 self.0.to_bits().to_be_bytes()
595 }
596
597 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, Float64DecodeError> {
603 let bytes: [u8; 8] = bytes
604 .try_into()
605 .map_err(|_| Float64DecodeError::InvalidSize { len: bytes.len() })?;
606 Self::try_new(f64::from_bits(u64::from_be_bytes(bytes)))
607 .ok_or(Float64DecodeError::NonFinite)
608 }
609}
610
611impl Display for Float64 {
612 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
613 Display::fmt(&self.0, formatter)
614 }
615}
616
617impl<'de> Deserialize<'de> for Float64 {
618 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
619 where
620 D: Deserializer<'de>,
621 {
622 Self::try_new(f64::deserialize(deserializer)?)
623 .ok_or_else(|| D::Error::custom("Float64 must be finite"))
624 }
625}
626
627impl Serialize for Float64 {
628 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
629 where
630 S: serde::Serializer,
631 {
632 serializer.serialize_f64(self.0)
633 }
634}
635
636impl Eq for Float64 {}
637
638impl From<Float64> for f64 {
639 fn from(value: Float64) -> Self {
640 value.0
641 }
642}
643
644impl From<i32> for Float64 {
645 fn from(value: i32) -> Self {
646 Self(f64::from(value))
647 }
648}
649
650impl Hash for Float64 {
651 fn hash<H: Hasher>(&self, state: &mut H) {
652 state.write_u64(self.0.to_bits());
653 }
654}
655
656impl Ord for Float64 {
657 fn cmp(&self, other: &Self) -> Ordering {
658 self.0.total_cmp(&other.0)
659 }
660}
661
662impl PartialEq for Float64 {
663 fn eq(&self, other: &Self) -> bool {
664 self.0 == other.0
665 }
666}
667
668impl PartialOrd for Float64 {
669 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
670 Some(self.cmp(other))
671 }
672}
673
674impl TryFrom<&[u8]> for Float64 {
675 type Error = Float64DecodeError;
676
677 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
678 Self::try_from_bytes(bytes)
679 }
680}
681
682impl Serialize for Blob {
683 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
684 serializer.serialize_bytes(self.as_bytes())
685 }
686}
687
688pub(crate) fn deserialize_fixed_bytes<'de, D: Deserializer<'de>, const N: usize>(
691 deserializer: D,
692) -> Result<[u8; N], D::Error> {
693 struct Fixed<const N: usize>;
694 impl<'de, const N: usize> serde::de::Visitor<'de> for Fixed<N> {
695 type Value = [u8; N];
696 fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
697 write!(f, "exactly {N} bytes")
698 }
699 fn visit_bytes<E: serde::de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
700 bytes
701 .try_into()
702 .map_err(|_| E::invalid_length(bytes.len(), &self))
703 }
704 fn visit_seq<A: serde::de::SeqAccess<'de>>(
705 self,
706 mut values: A,
707 ) -> Result<Self::Value, A::Error> {
708 let mut bytes = [0; N];
709 for (index, byte) in bytes.iter_mut().enumerate() {
710 *byte = values
711 .next_element()?
712 .ok_or_else(|| A::Error::invalid_length(index, &self))?;
713 }
714 if values.next_element::<u8>()?.is_some() {
715 return Err(A::Error::invalid_length(N + 1, &self));
716 }
717 Ok(bytes)
718 }
719 }
720 deserializer.deserialize_bytes(Fixed::<N>)
721}