canic/types/
ulid.rs

1use candid::CandidType;
2use derive_more::{Deref, DerefMut, Display, FromStr};
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use ulid::Ulid as WrappedUlid;
5
6///
7/// Ulid
8/// A Candid-safe wrapper around `ulid::Ulid`.
9///
10
11#[derive(
12    Clone, Copy, Debug, Deref, DerefMut, Display, Eq, FromStr, Hash, Ord, PartialEq, PartialOrd,
13)]
14#[repr(transparent)]
15pub struct Ulid(WrappedUlid);
16
17impl Ulid {
18    #[must_use]
19    pub const fn nil() -> Self {
20        Self(WrappedUlid::nil())
21    }
22
23    #[must_use]
24    pub const fn from_bytes(bytes: [u8; 16]) -> Self {
25        Self(WrappedUlid::from_bytes(bytes))
26    }
27
28    #[must_use]
29    pub const fn from_parts(timestamp_ms: u64, random: u128) -> Self {
30        Self(WrappedUlid::from_parts(timestamp_ms, random))
31    }
32
33    pub fn from_string(s: &str) -> Result<Self, ulid::DecodeError> {
34        Ok(Self(WrappedUlid::from_string(s)?))
35    }
36
37    #[must_use]
38    pub fn increment(&self) -> Option<Self> {
39        self.0.increment().map(Self::from)
40    }
41}
42
43impl CandidType for Ulid {
44    fn _ty() -> candid::types::Type {
45        candid::types::TypeInner::Text.into()
46    }
47
48    fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
49    where
50        S: candid::types::Serializer,
51    {
52        serializer.serialize_text(&self.0.to_string())
53    }
54}
55
56impl From<WrappedUlid> for Ulid {
57    fn from(u: WrappedUlid) -> Self {
58        Self(u)
59    }
60}
61
62impl From<Ulid> for WrappedUlid {
63    fn from(u: Ulid) -> Self {
64        u.0
65    }
66}
67
68// The ulid crate's serde impls are gated behind its `serde` feature.
69// With default-features disabled (to avoid pulling in `rand`), we implement
70// Serialize/Deserialize here explicitly.
71impl Serialize for Ulid {
72    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
73    where
74        S: Serializer,
75    {
76        let mut buffer = [0; ::ulid::ULID_LEN];
77        let text = self.array_to_str(&mut buffer);
78        text.serialize(serializer)
79    }
80}
81
82impl<'de> Deserialize<'de> for Ulid {
83    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
84    where
85        D: Deserializer<'de>,
86    {
87        let deserialized_str = String::deserialize(deserializer)?;
88        match WrappedUlid::from_string(&deserialized_str) {
89            Ok(u) => Ok(Self(u)),
90            Err(_) => Err(serde::de::Error::custom("invalid ulid string")),
91        }
92    }
93}